add ability to import users
This commit is contained in:
		@ -20,8 +20,9 @@ else
 | 
			
		||||
			<div class="container">
 | 
			
		||||
				<div class="row mb-1 align-items-center">
 | 
			
		||||
					<div class="col-sm-4">
 | 
			
		||||
						<ActionLink Action="Add" Text="Add User" Security="SecurityAccessLevel.Edit" ResourceKey="AddUser" />
 | 
			
		||||
					</div>
 | 
			
		||||
						<ActionLink Action="Add" Text="Add User" Security="SecurityAccessLevel.Edit" ResourceKey="AddUser" /> 
 | 
			
		||||
                        <ActionLink Text="Import Users" Class="btn btn-secondary" Action="Users" Security="SecurityAccessLevel.Admin" />                    
 | 
			
		||||
                    </div>
 | 
			
		||||
					<div class="col-sm-4">
 | 
			
		||||
						<input class="form-control" @bind="@_search" />
 | 
			
		||||
					</div>
 | 
			
		||||
@ -54,7 +55,7 @@ else
 | 
			
		||||
					<td>@((context.User.LastLoginOn != DateTime.MinValue) ? string.Format("{0:dd-MMM-yyyy HH:mm:ss}", context.User.LastLoginOn) : "")</td>
 | 
			
		||||
				</Row>
 | 
			
		||||
			</Pager>
 | 
			
		||||
		</TabPanel>
 | 
			
		||||
        </TabPanel>
 | 
			
		||||
        <TabPanel Name="Settings" Heading="Settings" ResourceKey="Settings" Security="SecurityAccessLevel.Admin">
 | 
			
		||||
			<div class="container">
 | 
			
		||||
				<Section Name="User" Heading="User Settings" ResourceKey="UserSettings">
 | 
			
		||||
@ -360,7 +361,7 @@ else
 | 
			
		||||
			</div>
 | 
			
		||||
			<br />
 | 
			
		||||
			<button type="button" class="btn btn-success" @onclick="SaveSiteSettings">@SharedLocalizer["Save"]</button>
 | 
			
		||||
		</TabPanel>
 | 
			
		||||
        </TabPanel>
 | 
			
		||||
	</TabStrip>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										55
									
								
								Oqtane.Client/Modules/Admin/Users/Users.razor
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								Oqtane.Client/Modules/Admin/Users/Users.razor
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,55 @@
 | 
			
		||||
@namespace Oqtane.Modules.Admin.Users
 | 
			
		||||
@inherits ModuleBase
 | 
			
		||||
@inject NavigationManager NavigationManager
 | 
			
		||||
@inject IUserService UserService
 | 
			
		||||
@inject IStringLocalizer<Users> Localizer
 | 
			
		||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
 | 
			
		||||
 | 
			
		||||
<div class="container">
 | 
			
		||||
    <div class="row mb-1 align-items-center">
 | 
			
		||||
        <Label Class="col-sm-3" For="userfile" HelpText="Select or upload a CSV file containing user information. The CSV file must be in the Template format specified." ResourceKey="UserFile">User File:</Label>
 | 
			
		||||
        <div class="col-sm-9">
 | 
			
		||||
            <FileManager Id="userfile" @ref="_filemanager" Filter="csv" />
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
<br />
 | 
			
		||||
<button type="button" class="btn btn-success" @onclick="ImportUsers">@Localizer["Import"]</button> 
 | 
			
		||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink> 
 | 
			
		||||
<a class="btn btn-info" href="/users.csv" target="_new">@Localizer["Template"]</a>
 | 
			
		||||
 | 
			
		||||
@code {
 | 
			
		||||
    private FileManager _filemanager;
 | 
			
		||||
 | 
			
		||||
    public override string Title => "Import Users";
 | 
			
		||||
 | 
			
		||||
    public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
 | 
			
		||||
 | 
			
		||||
    private async Task ImportUsers()
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            var fileid = _filemanager.GetFileId();
 | 
			
		||||
            if (fileid != -1)
 | 
			
		||||
            {
 | 
			
		||||
                if (await UserService.ImportUsersAsync(PageState.Site.SiteId, fileid))
 | 
			
		||||
                {
 | 
			
		||||
                    AddModuleMessage(Localizer["Message.Import.Success"], MessageType.Success);
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    AddModuleMessage(Localizer["Message.Import.Failure"], MessageType.Error);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                AddModuleMessage(Localizer["Message.Import.Validation"], MessageType.Warning);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            await logger.LogError(ex, "Error Importing Users {Error}", ex.Message);
 | 
			
		||||
            AddModuleMessage(Localizer["Error.Import"], MessageType.Error);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										144
									
								
								Oqtane.Client/Resources/Modules/Admin/Users/Users.resx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										144
									
								
								Oqtane.Client/Resources/Modules/Admin/Users/Users.resx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,144 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<root>
 | 
			
		||||
  <!-- 
 | 
			
		||||
    Microsoft ResX Schema 
 | 
			
		||||
    
 | 
			
		||||
    Version 2.0
 | 
			
		||||
    
 | 
			
		||||
    The primary goals of this format is to allow a simple XML format 
 | 
			
		||||
    that is mostly human readable. The generation and parsing of the 
 | 
			
		||||
    various data types are done through the TypeConverter classes 
 | 
			
		||||
    associated with the data types.
 | 
			
		||||
    
 | 
			
		||||
    Example:
 | 
			
		||||
    
 | 
			
		||||
    ... ado.net/XML headers & schema ...
 | 
			
		||||
    <resheader name="resmimetype">text/microsoft-resx</resheader>
 | 
			
		||||
    <resheader name="version">2.0</resheader>
 | 
			
		||||
    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
 | 
			
		||||
    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
 | 
			
		||||
    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
 | 
			
		||||
    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
 | 
			
		||||
    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
 | 
			
		||||
        <value>[base64 mime encoded serialized .NET Framework object]</value>
 | 
			
		||||
    </data>
 | 
			
		||||
    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
 | 
			
		||||
        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
 | 
			
		||||
        <comment>This is a comment</comment>
 | 
			
		||||
    </data>
 | 
			
		||||
                
 | 
			
		||||
    There are any number of "resheader" rows that contain simple 
 | 
			
		||||
    name/value pairs.
 | 
			
		||||
    
 | 
			
		||||
    Each data row contains a name, and value. The row also contains a 
 | 
			
		||||
    type or mimetype. Type corresponds to a .NET class that support 
 | 
			
		||||
    text/value conversion through the TypeConverter architecture. 
 | 
			
		||||
    Classes that don't support this are serialized and stored with the 
 | 
			
		||||
    mimetype set.
 | 
			
		||||
    
 | 
			
		||||
    The mimetype is used for serialized objects, and tells the 
 | 
			
		||||
    ResXResourceReader how to depersist the object. This is currently not 
 | 
			
		||||
    extensible. For a given mimetype the value must be set accordingly:
 | 
			
		||||
    
 | 
			
		||||
    Note - application/x-microsoft.net.object.binary.base64 is the format 
 | 
			
		||||
    that the ResXResourceWriter will generate, however the reader can 
 | 
			
		||||
    read any of the formats listed below.
 | 
			
		||||
    
 | 
			
		||||
    mimetype: application/x-microsoft.net.object.binary.base64
 | 
			
		||||
    value   : The object must be serialized with 
 | 
			
		||||
            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
 | 
			
		||||
            : and then encoded with base64 encoding.
 | 
			
		||||
    
 | 
			
		||||
    mimetype: application/x-microsoft.net.object.soap.base64
 | 
			
		||||
    value   : The object must be serialized with 
 | 
			
		||||
            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
 | 
			
		||||
            : and then encoded with base64 encoding.
 | 
			
		||||
 | 
			
		||||
    mimetype: application/x-microsoft.net.object.bytearray.base64
 | 
			
		||||
    value   : The object must be serialized into a byte array 
 | 
			
		||||
            : using a System.ComponentModel.TypeConverter
 | 
			
		||||
            : and then encoded with base64 encoding.
 | 
			
		||||
    -->
 | 
			
		||||
  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
 | 
			
		||||
    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
 | 
			
		||||
    <xsd:element name="root" msdata:IsDataSet="true">
 | 
			
		||||
      <xsd:complexType>
 | 
			
		||||
        <xsd:choice maxOccurs="unbounded">
 | 
			
		||||
          <xsd:element name="metadata">
 | 
			
		||||
            <xsd:complexType>
 | 
			
		||||
              <xsd:sequence>
 | 
			
		||||
                <xsd:element name="value" type="xsd:string" minOccurs="0" />
 | 
			
		||||
              </xsd:sequence>
 | 
			
		||||
              <xsd:attribute name="name" use="required" type="xsd:string" />
 | 
			
		||||
              <xsd:attribute name="type" type="xsd:string" />
 | 
			
		||||
              <xsd:attribute name="mimetype" type="xsd:string" />
 | 
			
		||||
              <xsd:attribute ref="xml:space" />
 | 
			
		||||
            </xsd:complexType>
 | 
			
		||||
          </xsd:element>
 | 
			
		||||
          <xsd:element name="assembly">
 | 
			
		||||
            <xsd:complexType>
 | 
			
		||||
              <xsd:attribute name="alias" type="xsd:string" />
 | 
			
		||||
              <xsd:attribute name="name" type="xsd:string" />
 | 
			
		||||
            </xsd:complexType>
 | 
			
		||||
          </xsd:element>
 | 
			
		||||
          <xsd:element name="data">
 | 
			
		||||
            <xsd:complexType>
 | 
			
		||||
              <xsd:sequence>
 | 
			
		||||
                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
 | 
			
		||||
                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
 | 
			
		||||
              </xsd:sequence>
 | 
			
		||||
              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
 | 
			
		||||
              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
 | 
			
		||||
              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
 | 
			
		||||
              <xsd:attribute ref="xml:space" />
 | 
			
		||||
            </xsd:complexType>
 | 
			
		||||
          </xsd:element>
 | 
			
		||||
          <xsd:element name="resheader">
 | 
			
		||||
            <xsd:complexType>
 | 
			
		||||
              <xsd:sequence>
 | 
			
		||||
                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
 | 
			
		||||
              </xsd:sequence>
 | 
			
		||||
              <xsd:attribute name="name" type="xsd:string" use="required" />
 | 
			
		||||
            </xsd:complexType>
 | 
			
		||||
          </xsd:element>
 | 
			
		||||
        </xsd:choice>
 | 
			
		||||
      </xsd:complexType>
 | 
			
		||||
    </xsd:element>
 | 
			
		||||
  </xsd:schema>
 | 
			
		||||
  <resheader name="resmimetype">
 | 
			
		||||
    <value>text/microsoft-resx</value>
 | 
			
		||||
  </resheader>
 | 
			
		||||
  <resheader name="version">
 | 
			
		||||
    <value>2.0</value>
 | 
			
		||||
  </resheader>
 | 
			
		||||
  <resheader name="reader">
 | 
			
		||||
    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
 | 
			
		||||
  </resheader>
 | 
			
		||||
  <resheader name="writer">
 | 
			
		||||
    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
 | 
			
		||||
  </resheader>
 | 
			
		||||
  <data name="UserFile.HelpText" xml:space="preserve">
 | 
			
		||||
    <value>Select or upload a CSV file containing user information. The CSV file must be in the Template format specified.</value>
 | 
			
		||||
  </data>
 | 
			
		||||
  <data name="UserFile.Text" xml:space="preserve">
 | 
			
		||||
    <value>User File:</value>
 | 
			
		||||
  </data>
 | 
			
		||||
  <data name="Error.Import" xml:space="preserve">
 | 
			
		||||
    <value>Error Importing Users</value>
 | 
			
		||||
  </data>
 | 
			
		||||
  <data name="Import" xml:space="preserve">
 | 
			
		||||
    <value>Import</value>
 | 
			
		||||
  </data>
 | 
			
		||||
  <data name="Message.Import.Failure" xml:space="preserve">
 | 
			
		||||
    <value>User Import Failed</value>
 | 
			
		||||
  </data>
 | 
			
		||||
  <data name="Message.Import.Success" xml:space="preserve">
 | 
			
		||||
    <value>Users Imported Successfully</value>
 | 
			
		||||
  </data>
 | 
			
		||||
  <data name="Message.Import.Validation" xml:space="preserve">
 | 
			
		||||
    <value>You Must Specify A User File For Import</value>
 | 
			
		||||
  </data>
 | 
			
		||||
  <data name="Template" xml:space="preserve">
 | 
			
		||||
    <value>Template</value>
 | 
			
		||||
  </data>
 | 
			
		||||
</root>
 | 
			
		||||
@ -142,5 +142,12 @@ namespace Oqtane.Services
 | 
			
		||||
        /// <param name="siteId">ID of a <see cref="Site"/></param>
 | 
			
		||||
        /// <returns></returns>
 | 
			
		||||
        Task<string> GetPasswordRequirementsAsync(int siteId);
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Bulk import of users
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="fileId">ID of a <see cref="File"/></param>
 | 
			
		||||
        /// <returns></returns>
 | 
			
		||||
        Task<bool> ImportUsersAsync(int siteId, int fileId);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -6,6 +6,9 @@ using Oqtane.Documentation;
 | 
			
		||||
using System.Net;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using Microsoft.Extensions.Localization;
 | 
			
		||||
using Microsoft.EntityFrameworkCore.Metadata.Internal;
 | 
			
		||||
using Oqtane.Modules.Admin.Roles;
 | 
			
		||||
using System.Xml.Linq;
 | 
			
		||||
 | 
			
		||||
namespace Oqtane.Services
 | 
			
		||||
{
 | 
			
		||||
@ -123,5 +126,10 @@ namespace Oqtane.Services
 | 
			
		||||
            // format requirements
 | 
			
		||||
            return string.Format(passwordValidationCriteriaTemplate, minimumlength, uniquecharacters, digitRequirement, uppercaseRequirement, lowercaseRequirement, punctuationRequirement);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public async Task<bool> ImportUsersAsync(int siteId, int fileId)
 | 
			
		||||
        {
 | 
			
		||||
            return await PostJsonAsync<bool>($"{Apiurl}/import?siteid={siteId}&fileid={fileId}", true);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -28,9 +28,10 @@ namespace Oqtane.Controllers
 | 
			
		||||
        private readonly IUserPermissions _userPermissions;
 | 
			
		||||
        private readonly ISettingRepository _settings;
 | 
			
		||||
        private readonly IJwtManager _jwtManager;
 | 
			
		||||
        private readonly IFileRepository _files;
 | 
			
		||||
        private readonly ILogManager _logger;
 | 
			
		||||
 | 
			
		||||
        public UserController(IUserRepository users, ITenantManager tenantManager, IUserManager userManager, ISiteRepository sites, IUserPermissions userPermissions, ISettingRepository settings, IJwtManager jwtManager, ILogManager logger)
 | 
			
		||||
        public UserController(IUserRepository users, ITenantManager tenantManager, IUserManager userManager, ISiteRepository sites, IUserPermissions userPermissions, ISettingRepository settings, IJwtManager jwtManager, IFileRepository files, ILogManager logger)
 | 
			
		||||
        {
 | 
			
		||||
            _users = users;
 | 
			
		||||
            _tenantManager = tenantManager;
 | 
			
		||||
@ -39,6 +40,7 @@ namespace Oqtane.Controllers
 | 
			
		||||
            _userPermissions = userPermissions;
 | 
			
		||||
            _settings = settings;
 | 
			
		||||
            _jwtManager = jwtManager;
 | 
			
		||||
            _files = files;
 | 
			
		||||
            _logger = logger;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -369,5 +371,22 @@ namespace Oqtane.Controllers
 | 
			
		||||
 | 
			
		||||
            return requirements;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // POST api/<controller>/import?siteid=x&fileid=y
 | 
			
		||||
        [HttpPost("import")]
 | 
			
		||||
        [Authorize(Roles = RoleNames.Admin)]
 | 
			
		||||
        public async Task<bool> Import(string siteid, string fileid)
 | 
			
		||||
        {
 | 
			
		||||
            if (int.TryParse(siteid, out int SiteId) && SiteId == _tenantManager.GetAlias().SiteId && int.TryParse(fileid, out int FileId))
 | 
			
		||||
            {
 | 
			
		||||
                return await _userManager.ImportUsers(SiteId, FileId);
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized User Import Attempt {SiteId} {FileId}", siteid, fileid);
 | 
			
		||||
                HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -18,5 +18,6 @@ namespace Oqtane.Managers
 | 
			
		||||
        User VerifyTwoFactor(User user, string token);
 | 
			
		||||
        Task<User> LinkExternalAccount(User user, string token, string type, string key, string name);
 | 
			
		||||
        Task<bool> ValidatePassword(string password);
 | 
			
		||||
        Task<bool> ImportUsers(int siteId, int fileId);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,6 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Globalization;
 | 
			
		||||
using System.IO;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Net;
 | 
			
		||||
@ -16,24 +17,32 @@ namespace Oqtane.Managers
 | 
			
		||||
    public class UserManager : IUserManager
 | 
			
		||||
    {
 | 
			
		||||
        private readonly IUserRepository _users;
 | 
			
		||||
        private readonly IRoleRepository _roles;
 | 
			
		||||
        private readonly IUserRoleRepository _userRoles;
 | 
			
		||||
        private readonly UserManager<IdentityUser> _identityUserManager;
 | 
			
		||||
        private readonly SignInManager<IdentityUser> _identitySignInManager;
 | 
			
		||||
        private readonly ITenantManager _tenantManager;
 | 
			
		||||
        private readonly INotificationRepository _notifications;
 | 
			
		||||
        private readonly IFolderRepository _folders;
 | 
			
		||||
        private readonly IFileRepository _files;
 | 
			
		||||
        private readonly IProfileRepository _profiles;
 | 
			
		||||
        private readonly ISettingRepository _settings;
 | 
			
		||||
        private readonly ISyncManager _syncManager;
 | 
			
		||||
        private readonly ILogManager _logger;
 | 
			
		||||
 | 
			
		||||
        public UserManager(IUserRepository users, IUserRoleRepository userRoles, UserManager<IdentityUser> identityUserManager, SignInManager<IdentityUser> identitySignInManager, ITenantManager tenantManager, INotificationRepository notifications, IFolderRepository folders, ISyncManager syncManager, ILogManager logger)
 | 
			
		||||
        public UserManager(IUserRepository users, IRoleRepository roles, IUserRoleRepository userRoles, UserManager<IdentityUser> identityUserManager, SignInManager<IdentityUser> identitySignInManager, ITenantManager tenantManager, INotificationRepository notifications, IFolderRepository folders, IFileRepository files, IProfileRepository profiles, ISettingRepository settings, ISyncManager syncManager, ILogManager logger)
 | 
			
		||||
        {
 | 
			
		||||
            _users = users;
 | 
			
		||||
            _roles = roles;
 | 
			
		||||
            _userRoles = userRoles;
 | 
			
		||||
            _identityUserManager = identityUserManager;
 | 
			
		||||
            _identitySignInManager = identitySignInManager;
 | 
			
		||||
            _tenantManager = tenantManager;
 | 
			
		||||
            _notifications = notifications;
 | 
			
		||||
            _folders = folders;
 | 
			
		||||
            _files = files;
 | 
			
		||||
            _profiles = profiles;
 | 
			
		||||
            _settings = settings;
 | 
			
		||||
            _syncManager = syncManager;
 | 
			
		||||
            _logger = logger;
 | 
			
		||||
        }
 | 
			
		||||
@ -95,6 +104,12 @@ namespace Oqtane.Managers
 | 
			
		||||
            IdentityUser identityuser = await _identityUserManager.FindByNameAsync(user.Username);
 | 
			
		||||
            if (identityuser == null)
 | 
			
		||||
            {
 | 
			
		||||
                if (string.IsNullOrEmpty(user.Password))
 | 
			
		||||
                {
 | 
			
		||||
                    // create random password ie. Jan-01-2023+12:00:00!
 | 
			
		||||
                    Random rnd = new Random();
 | 
			
		||||
                    user.Password = DateTime.UtcNow.ToString("MMM-dd-yyyy+HH:mm:ss", CultureInfo.InvariantCulture) + (char)rnd.Next(33, 47);
 | 
			
		||||
                }
 | 
			
		||||
                identityuser = new IdentityUser();
 | 
			
		||||
                identityuser.UserName = user.Username;
 | 
			
		||||
                identityuser.Email = user.Email;
 | 
			
		||||
@ -443,5 +458,144 @@ namespace Oqtane.Managers
 | 
			
		||||
            var result = await validator.ValidateAsync(_identityUserManager, null, password);
 | 
			
		||||
            return result.Succeeded;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public async Task<bool> ImportUsers(int siteId, int fileId)
 | 
			
		||||
        {
 | 
			
		||||
            var success = true;
 | 
			
		||||
            int users = 0;
 | 
			
		||||
 | 
			
		||||
            var file = _files.GetFile(fileId);
 | 
			
		||||
            if (file != null)
 | 
			
		||||
            {
 | 
			
		||||
                var path = _files.GetFilePath(file);
 | 
			
		||||
                if (System.IO.File.Exists(path))
 | 
			
		||||
                {
 | 
			
		||||
                    var roles = _roles.GetRoles(siteId).ToList();
 | 
			
		||||
                    var profiles = _profiles.GetProfiles(siteId).ToList();
 | 
			
		||||
 | 
			
		||||
                    try
 | 
			
		||||
                    {
 | 
			
		||||
                        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)
 | 
			
		||||
                            {
 | 
			
		||||
                                var values = row.Replace("\"", "").Split(',');
 | 
			
		||||
 | 
			
		||||
                                if (values.Length > 3)
 | 
			
		||||
                                {
 | 
			
		||||
                                    // user
 | 
			
		||||
                                    var user = _users.GetUser(values[1], values[0]);
 | 
			
		||||
                                    if (user == null)
 | 
			
		||||
                                    {
 | 
			
		||||
                                        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);
 | 
			
		||||
                                        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)
 | 
			
		||||
                                            {
 | 
			
		||||
                                                role = new Role();
 | 
			
		||||
                                                role.SiteId = siteId;
 | 
			
		||||
                                                role.Name = rolename;
 | 
			
		||||
                                                role.Description = rolename;
 | 
			
		||||
                                                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 (header.Length > index && !string.IsNullOrEmpty(values[index]))
 | 
			
		||||
                                            {
 | 
			
		||||
                                                var profile = profiles.FirstOrDefault(item => item.Name == header[index]);
 | 
			
		||||
                                                if (profile != null)
 | 
			
		||||
                                                {
 | 
			
		||||
                                                    var setting = settings.FirstOrDefault(item => item.SettingName == profile.Name);
 | 
			
		||||
                                                    if (setting == 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])
 | 
			
		||||
                                                        {
 | 
			
		||||
                                                            setting.SettingValue = values[index];
 | 
			
		||||
                                                            _settings.UpdateSetting(setting);
 | 
			
		||||
                                                        }
 | 
			
		||||
                                                    }
 | 
			
		||||
                                                }
 | 
			
		||||
                                            }
 | 
			
		||||
                                        }
 | 
			
		||||
                                    }
 | 
			
		||||
 | 
			
		||||
                                    users++;
 | 
			
		||||
                                }
 | 
			
		||||
 | 
			
		||||
                                row = reader.ReadLine();
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        _logger.Log(LogLevel.Information, this, LogFunction.Create, "{Users} Users Imported", users);
 | 
			
		||||
                    }
 | 
			
		||||
                    catch (Exception ex)
 | 
			
		||||
                    {
 | 
			
		||||
                        _logger.Log(LogLevel.Error, this, LogFunction.Create, ex, "Error Importing User Import File {SiteId} {FileId}", siteId, fileId);
 | 
			
		||||
                        success = false;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    _logger.Log(LogLevel.Error, this, LogFunction.Create,"User Import File Does Not Exist {Path}", path);
 | 
			
		||||
                    success = false;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                _logger.Log(LogLevel.Error, this, LogFunction.Create, "User Import File Does Not Exist {SiteId} {FileId}", siteId, fileId);
 | 
			
		||||
                success = false;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return success;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -11,6 +11,8 @@ namespace Oqtane.Repository
 | 
			
		||||
        UserRole UpdateUserRole(UserRole userRole);
 | 
			
		||||
        UserRole GetUserRole(int userRoleId);
 | 
			
		||||
        UserRole GetUserRole(int userRoleId, bool tracking);
 | 
			
		||||
        UserRole GetUserRole(int userId, int roleId);
 | 
			
		||||
        UserRole GetUserRole(int userId, int roleId, bool tracking);
 | 
			
		||||
        void DeleteUserRole(int userRoleId);
 | 
			
		||||
        void DeleteUserRoles(int userId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -78,6 +78,29 @@ namespace Oqtane.Repository
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public UserRole GetUserRole(int userId, int roleId)
 | 
			
		||||
        {
 | 
			
		||||
            return GetUserRole(userId, roleId, true);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public UserRole GetUserRole(int userId, int roleId, bool tracking)
 | 
			
		||||
        {
 | 
			
		||||
            if (tracking)
 | 
			
		||||
            {
 | 
			
		||||
                return _db.UserRole
 | 
			
		||||
                    .Include(item => item.Role) // eager load roles
 | 
			
		||||
                    .Include(item => item.User) // eager load users
 | 
			
		||||
                    .FirstOrDefault(item => item.UserId == userId && item.RoleId == roleId);
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                return _db.UserRole.AsNoTracking()
 | 
			
		||||
                    .Include(item => item.Role) // eager load roles
 | 
			
		||||
                    .Include(item => item.User) // eager load users
 | 
			
		||||
                    .FirstOrDefault(item => item.UserId == userId && item.RoleId == roleId);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void DeleteUserRole(int userRoleId)
 | 
			
		||||
        {
 | 
			
		||||
            UserRole userRole = _db.UserRole.Find(userRoleId);
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										1
									
								
								Oqtane.Server/wwwroot/users.csv
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								Oqtane.Server/wwwroot/users.csv
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
Email,Username,DisplayName,Roles,FirstName,LastName,Street,City,Region,Country,PostalCode,Phone
 | 
			
		||||
		
		
			
  | 
		Reference in New Issue
	
	Block a user