diff --git a/Oqtane.Client/Modules/Admin/Profiles/Edit.razor b/Oqtane.Client/Modules/Admin/Profiles/Edit.razor index 1c149c5e..9620a396 100644 --- a/Oqtane.Client/Modules/Admin/Profiles/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Profiles/Edit.razor @@ -64,6 +64,12 @@ +
+ +
+ +
+
@@ -97,6 +103,7 @@ private string _maxlength = "0"; private string _defaultvalue = string.Empty; private string _options = string.Empty; + private string _validation = string.Empty; private string _isrequired = "False"; private string _isprivate = "False"; private string createdby; @@ -126,6 +133,7 @@ _maxlength = profile.MaxLength.ToString(); _defaultvalue = profile.DefaultValue; _options = profile.Options; + _validation = profile.Validation; _isrequired = profile.IsRequired.ToString(); _isprivate = profile.IsPrivate.ToString(); createdby = profile.CreatedBy; @@ -169,6 +177,7 @@ profile.MaxLength = int.Parse(_maxlength); profile.DefaultValue = _defaultvalue; profile.Options = _options; + profile.Validation = _validation; profile.IsRequired = (_isrequired == null ? false : Boolean.Parse(_isrequired)); profile.IsPrivate = (_isprivate == null ? false : Boolean.Parse(_isprivate)); if (_profileid != -1) diff --git a/Oqtane.Client/Modules/Admin/UserProfile/Index.razor b/Oqtane.Client/Modules/Admin/UserProfile/Index.razor index 70aa3487..4c1c22b9 100644 --- a/Oqtane.Client/Modules/Admin/UserProfile/Index.razor +++ b/Oqtane.Client/Modules/Admin/UserProfile/Index.razor @@ -1,4 +1,5 @@ @namespace Oqtane.Modules.Admin.UserProfile +@using System.Text.RegularExpressions; @inherits ModuleBase @inject NavigationManager NavigationManager @inject IUserService UserService @@ -227,140 +228,143 @@ else

@code { - private string username = string.Empty; - private string _password = string.Empty; - private string _passwordtype = "password"; - private string _togglepassword = string.Empty; - private string confirm = string.Empty; - private bool allowtwofactor = false; - private string twofactor = "False"; - private string email = string.Empty; - private string displayname = string.Empty; - private FileManager filemanager; - private int folderid = -1; - private int photofileid = -1; - private File photo = null; - private List profiles; - private Dictionary settings; - private string category = string.Empty; - private string filter = "to"; - private List notifications; + private string username = string.Empty; + private string _password = string.Empty; + private string _passwordtype = "password"; + private string _togglepassword = string.Empty; + private string confirm = string.Empty; + private bool allowtwofactor = false; + private string twofactor = "False"; + private string email = string.Empty; + private string displayname = string.Empty; + private FileManager filemanager; + private int folderid = -1; + private int photofileid = -1; + private File photo = null; + private List profiles; + private Dictionary settings; + private string category = string.Empty; + private string filter = "to"; + private List notifications; - public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.View; + public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.View; - protected override async Task OnParametersSetAsync() - { - try - { - _togglepassword = SharedLocalizer["ShowPassword"]; + protected override async Task OnParametersSetAsync() + { + try + { + _togglepassword = SharedLocalizer["ShowPassword"]; - if (PageState.Site.Settings.ContainsKey("LoginOptions:TwoFactor") && !string.IsNullOrEmpty(PageState.Site.Settings["LoginOptions:TwoFactor"])) - { - allowtwofactor = (PageState.Site.Settings["LoginOptions:TwoFactor"] == "true"); - } + if (PageState.Site.Settings.ContainsKey("LoginOptions:TwoFactor") && !string.IsNullOrEmpty(PageState.Site.Settings["LoginOptions:TwoFactor"])) + { + allowtwofactor = (PageState.Site.Settings["LoginOptions:TwoFactor"] == "true"); + } - if (PageState.User != null) - { - username = PageState.User.Username; - twofactor = PageState.User.TwoFactorRequired.ToString(); - email = PageState.User.Email; - displayname = PageState.User.DisplayName; + if (PageState.User != null) + { + username = PageState.User.Username; + twofactor = PageState.User.TwoFactorRequired.ToString(); + email = PageState.User.Email; + displayname = PageState.User.DisplayName; - // get user folder - var folder = await FolderService.GetFolderAsync(ModuleState.SiteId, PageState.User.FolderPath); - if (folder != null) - { - folderid = folder.FolderId; - } + // get user folder + var folder = await FolderService.GetFolderAsync(ModuleState.SiteId, PageState.User.FolderPath); + if (folder != null) + { + folderid = folder.FolderId; + } - if (PageState.User.PhotoFileId != null) - { - photofileid = PageState.User.PhotoFileId.Value; - photo = await FileService.GetFileAsync(photofileid); - } - else - { - photofileid = -1; - photo = null; - } + if (PageState.User.PhotoFileId != null) + { + photofileid = PageState.User.PhotoFileId.Value; + photo = await FileService.GetFileAsync(photofileid); + } + else + { + photofileid = -1; + photo = null; + } - profiles = await ProfileService.GetProfilesAsync(ModuleState.SiteId); - settings = await SettingService.GetUserSettingsAsync(PageState.User.UserId); + profiles = await ProfileService.GetProfilesAsync(ModuleState.SiteId); + settings = await SettingService.GetUserSettingsAsync(PageState.User.UserId); - await LoadNotificationsAsync(); - } - else - { - AddModuleMessage(Localizer["Message.User.NoLogIn"], MessageType.Warning); - } - } - catch (Exception ex) - { - await logger.LogError(ex, "Error Loading User Profile {Error}", ex.Message); - AddModuleMessage(Localizer["Error.Profile.Load"], MessageType.Error); - } - } + await LoadNotificationsAsync(); + } + else + { + AddModuleMessage(Localizer["Message.User.NoLogIn"], MessageType.Warning); + } + } + catch (Exception ex) + { + await logger.LogError(ex, "Error Loading User Profile {Error}", ex.Message); + AddModuleMessage(Localizer["Error.Profile.Load"], MessageType.Error); + } + } - private async Task LoadNotificationsAsync() - { - notifications = await NotificationService.GetNotificationsAsync(PageState.Site.SiteId, filter, PageState.User.UserId); - notifications = notifications.Where(item => item.DeletedBy != PageState.User.Username).ToList(); - } + private async Task LoadNotificationsAsync() + { + notifications = await NotificationService.GetNotificationsAsync(PageState.Site.SiteId, filter, PageState.User.UserId); + notifications = notifications.Where(item => item.DeletedBy != PageState.User.Username).ToList(); + } - private string GetProfileValue(string SettingName, string DefaultValue) - { - string value = SettingService.GetSetting(settings, SettingName, DefaultValue); - if (value.Contains("]")) - { - value = value.Substring(value.IndexOf("]") + 1); - } - return value; - } + private string GetProfileValue(string SettingName, string DefaultValue) + { + string value = SettingService.GetSetting(settings, SettingName, DefaultValue); + if (value.Contains("]")) + { + value = value.Substring(value.IndexOf("]") + 1); + } + return value; + } - private async Task Save() - { - try - { - if (username != string.Empty && email != string.Empty && ValidateProfiles()) - { - if (_password == confirm) - { - var user = PageState.User; - user.Username = username; - user.Password = _password; - user.TwoFactorRequired = bool.Parse(twofactor); - user.Email = email; - user.DisplayName = (displayname == string.Empty ? username : displayname); - user.PhotoFileId = filemanager.GetFileId(); - if (user.PhotoFileId == -1) - { - user.PhotoFileId = null; - } - if (user.PhotoFileId != null) - { - photofileid = user.PhotoFileId.Value; - photo = await FileService.GetFileAsync(photofileid); - } - else - { - photofileid = -1; - photo = null; - } + private async Task Save() + { + try + { + if (username != string.Empty && email != string.Empty && ValidateProfiles()) + { + if (_password == confirm) + { + var user = PageState.User; + user.Username = username; + user.Password = _password; + user.TwoFactorRequired = bool.Parse(twofactor); + user.Email = email; + user.DisplayName = (displayname == string.Empty ? username : displayname); + user.PhotoFileId = filemanager.GetFileId(); + if (user.PhotoFileId == -1) + { + user.PhotoFileId = null; + } + if (user.PhotoFileId != null) + { + photofileid = user.PhotoFileId.Value; + photo = await FileService.GetFileAsync(photofileid); + } + else + { + photofileid = -1; + photo = null; + } - user = await UserService.UpdateUserAsync(user); - if (user != null) - { - await SettingService.UpdateUserSettingsAsync(settings, PageState.User.UserId); - await logger.LogInformation("User Profile Saved"); + user = await UserService.UpdateUserAsync(user); + if (user != null) + { + await SettingService.UpdateUserSettingsAsync(settings, PageState.User.UserId); + await logger.LogInformation("User Profile Saved"); - AddModuleMessage(Localizer["Success.Profile.Update"], MessageType.Success); - StateHasChanged(); - } - else - { - AddModuleMessage(Localizer["Message.Password.Complexity"], MessageType.Error); - } - } + AddModuleMessage(Localizer["Success.Profile.Update"], MessageType.Success); + StateHasChanged(); + + var interop = new Interop(JSRuntime); + await interop.ScrollTo(0, 0, "smooth"); + } + else + { + AddModuleMessage(Localizer["Message.Password.Complexity"], MessageType.Error); + } + } else { AddModuleMessage(Localizer["Message.Password.Invalid"], MessageType.Warning); @@ -389,10 +393,15 @@ else } if (!profile.IsPrivate || UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin)) { - if (profile.IsRequired && string.IsNullOrEmpty(SettingService.GetSetting(settings, profile.Name, string.Empty))) + if (valid == true && profile.IsRequired && string.IsNullOrEmpty(SettingService.GetSetting(settings, profile.Name, string.Empty))) { valid = false; } + if (valid == true && !string.IsNullOrEmpty(profile.Validation)) + { + Regex regex = new Regex(profile.Validation); + valid = regex.Match(SettingService.GetSetting(settings, profile.Name, string.Empty)).Success; + } } } return valid; diff --git a/Oqtane.Client/Modules/Admin/Users/Add.razor b/Oqtane.Client/Modules/Admin/Users/Add.razor index 713dab6e..54c396ee 100644 --- a/Oqtane.Client/Modules/Admin/Users/Add.razor +++ b/Oqtane.Client/Modules/Admin/Users/Add.razor @@ -1,4 +1,5 @@ @namespace Oqtane.Modules.Admin.Users +@using System.Text.RegularExpressions; @inherits ModuleBase @inject NavigationManager NavigationManager @inject IUserService UserService @@ -95,8 +96,8 @@ @code { private string username = string.Empty; private string _password = string.Empty; - private string _passwordtype = "password"; - private string _togglepassword = string.Empty; + private string _passwordtype = "password"; + private string _togglepassword = string.Empty; private string confirm = string.Empty; private string email = string.Empty; private string displayname = string.Empty; @@ -121,15 +122,15 @@ } } - private string GetProfileValue(string SettingName, string DefaultValue) - { - string value = SettingService.GetSetting(settings, SettingName, DefaultValue); - if (value.Contains("]")) - { - value = value.Substring(value.IndexOf("]") + 1); - } - return value; - } + private string GetProfileValue(string SettingName, string DefaultValue) + { + string value = SettingService.GetSetting(settings, SettingName, DefaultValue); + if (value.Contains("]")) + { + value = value.Substring(value.IndexOf("]") + 1); + } + return value; + } private async Task SaveUser() { @@ -195,9 +196,17 @@ { settings = SettingService.SetSetting(settings, profile.Name, profile.DefaultValue); } - if (profile.IsRequired && string.IsNullOrEmpty(SettingService.GetSetting(settings, profile.Name, string.Empty))) + if (!profile.IsPrivate || UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin)) { - valid = false; + if (valid == true && profile.IsRequired && string.IsNullOrEmpty(SettingService.GetSetting(settings, profile.Name, string.Empty))) + { + valid = false; + } + if (valid == true && !string.IsNullOrEmpty(profile.Validation)) + { + Regex regex = new Regex(profile.Validation); + valid = regex.Match(SettingService.GetSetting(settings, profile.Name, string.Empty)).Success; + } } } return valid; diff --git a/Oqtane.Client/Modules/Admin/Users/Edit.razor b/Oqtane.Client/Modules/Admin/Users/Edit.razor index af541617..fea1a6f0 100644 --- a/Oqtane.Client/Modules/Admin/Users/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Users/Edit.razor @@ -1,4 +1,5 @@ @namespace Oqtane.Modules.Admin.Users +@using System.Text.RegularExpressions; @inherits ModuleBase @inject NavigationManager NavigationManager @inject IUserService UserService @@ -148,124 +149,124 @@ else @code { - private int userid; - private string username = string.Empty; - private string _password = string.Empty; - private string _passwordtype = "password"; - private string _togglepassword = string.Empty; - private string confirm = string.Empty; - private string email = string.Empty; - private string displayname = string.Empty; - private FileManager filemanager; - private int photofileid = -1; - private File photo = null; - private string isdeleted; - private string lastlogin; - private string lastipaddress; + private int userid; + private string username = string.Empty; + private string _password = string.Empty; + private string _passwordtype = "password"; + private string _togglepassword = string.Empty; + private string confirm = string.Empty; + private string email = string.Empty; + private string displayname = string.Empty; + private FileManager filemanager; + private int photofileid = -1; + private File photo = null; + private string isdeleted; + private string lastlogin; + private string lastipaddress; - private List profiles; - private Dictionary settings; - private string category = string.Empty; + private List profiles; + private Dictionary settings; + private string category = string.Empty; - private string createdby; - private DateTime createdon; - private string modifiedby; - private DateTime modifiedon; - private string deletedby; - private DateTime? deletedon; + private string createdby; + private DateTime createdon; + private string modifiedby; + private DateTime modifiedon; + private string deletedby; + private DateTime? deletedon; - public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit; + public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit; - protected override async Task OnParametersSetAsync() - { - try - { - if (PageState.QueryString.ContainsKey("id")) - { - _togglepassword = SharedLocalizer["ShowPassword"]; - profiles = await ProfileService.GetProfilesAsync(PageState.Site.SiteId); - userid = Int32.Parse(PageState.QueryString["id"]); - var user = await UserService.GetUserAsync(userid, PageState.Site.SiteId); - if (user != null) - { - username = user.Username; - email = user.Email; - displayname = user.DisplayName; - if (user.PhotoFileId != null) - { - photofileid = user.PhotoFileId.Value; - photo = await FileService.GetFileAsync(photofileid); - } - else - { - photofileid = -1; - photo = null; - } - isdeleted = user.IsDeleted.ToString(); - lastlogin = string.Format("{0:MMM dd yyyy HH:mm:ss}", user.LastLoginOn); - lastipaddress = user.LastIPAddress; + protected override async Task OnParametersSetAsync() + { + try + { + if (PageState.QueryString.ContainsKey("id")) + { + _togglepassword = SharedLocalizer["ShowPassword"]; + profiles = await ProfileService.GetProfilesAsync(PageState.Site.SiteId); + userid = Int32.Parse(PageState.QueryString["id"]); + var user = await UserService.GetUserAsync(userid, PageState.Site.SiteId); + if (user != null) + { + username = user.Username; + email = user.Email; + displayname = user.DisplayName; + if (user.PhotoFileId != null) + { + photofileid = user.PhotoFileId.Value; + photo = await FileService.GetFileAsync(photofileid); + } + else + { + photofileid = -1; + photo = null; + } + isdeleted = user.IsDeleted.ToString(); + lastlogin = string.Format("{0:MMM dd yyyy HH:mm:ss}", user.LastLoginOn); + lastipaddress = user.LastIPAddress; - settings = await SettingService.GetUserSettingsAsync(user.UserId); - createdby = user.CreatedBy; - createdon = user.CreatedOn; - modifiedby = user.ModifiedBy; - modifiedon = user.ModifiedOn; - deletedby = user.DeletedBy; - deletedon = user.DeletedOn; - } - } - } - catch (Exception ex) - { - await logger.LogError(ex, "Error Loading User {UserId} {Error}", userid, ex.Message); - AddModuleMessage(Localizer["Error.User.Load"], MessageType.Error); - } - } + settings = await SettingService.GetUserSettingsAsync(user.UserId); + createdby = user.CreatedBy; + createdon = user.CreatedOn; + modifiedby = user.ModifiedBy; + modifiedon = user.ModifiedOn; + deletedby = user.DeletedBy; + deletedon = user.DeletedOn; + } + } + } + catch (Exception ex) + { + await logger.LogError(ex, "Error Loading User {UserId} {Error}", userid, ex.Message); + AddModuleMessage(Localizer["Error.User.Load"], MessageType.Error); + } + } - private string GetProfileValue(string SettingName, string DefaultValue) - { - string value = SettingService.GetSetting(settings, SettingName, DefaultValue); - if (value.Contains("]")) - { - value = value.Substring(value.IndexOf("]") + 1); - } - return value; - } + private string GetProfileValue(string SettingName, string DefaultValue) + { + string value = SettingService.GetSetting(settings, SettingName, DefaultValue); + if (value.Contains("]")) + { + value = value.Substring(value.IndexOf("]") + 1); + } + return value; + } - private async Task SaveUser() - { - try - { - if (username != string.Empty && email != string.Empty && ValidateProfiles()) - { - if (_password == confirm) - { - var user = await UserService.GetUserAsync(userid, PageState.Site.SiteId); - user.SiteId = PageState.Site.SiteId; - user.Username = username; - user.Password = _password; - user.Email = email; - user.DisplayName = string.IsNullOrWhiteSpace(displayname) ? username : displayname; - user.PhotoFileId = null; - user.PhotoFileId = filemanager.GetFileId(); - if (user.PhotoFileId == -1) - { - user.PhotoFileId = null; - } + private async Task SaveUser() + { + try + { + if (username != string.Empty && email != string.Empty && ValidateProfiles()) + { + if (_password == confirm) + { + var user = await UserService.GetUserAsync(userid, PageState.Site.SiteId); + user.SiteId = PageState.Site.SiteId; + user.Username = username; + user.Password = _password; + user.Email = email; + user.DisplayName = string.IsNullOrWhiteSpace(displayname) ? username : displayname; + user.PhotoFileId = null; + user.PhotoFileId = filemanager.GetFileId(); + if (user.PhotoFileId == -1) + { + user.PhotoFileId = null; + } - user.IsDeleted = (isdeleted == null ? true : Boolean.Parse(isdeleted)); + user.IsDeleted = (isdeleted == null ? true : Boolean.Parse(isdeleted)); - user = await UserService.UpdateUserAsync(user); - if (user != null) - { - await SettingService.UpdateUserSettingsAsync(settings, user.UserId); - await logger.LogInformation("User Saved {User}", user); - NavigationManager.NavigateTo(NavigateUrl()); - } - else - { - AddModuleMessage(Localizer["Message.Password.Complexity"], MessageType.Error); - } + user = await UserService.UpdateUserAsync(user); + if (user != null) + { + await SettingService.UpdateUserSettingsAsync(settings, user.UserId); + await logger.LogInformation("User Saved {User}", user); + NavigationManager.NavigateTo(NavigateUrl()); + } + else + { + AddModuleMessage(Localizer["Message.Password.Complexity"], MessageType.Error); + } } else { @@ -293,9 +294,17 @@ else { settings = SettingService.SetSetting(settings, profile.Name, profile.DefaultValue); } - if (profile.IsRequired && string.IsNullOrEmpty(SettingService.GetSetting(settings, profile.Name, string.Empty))) + if (!profile.IsPrivate || UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin)) { - valid = false; + if (valid == true && profile.IsRequired && string.IsNullOrEmpty(SettingService.GetSetting(settings, profile.Name, string.Empty))) + { + valid = false; + } + if (valid == true && !string.IsNullOrEmpty(profile.Validation)) + { + Regex regex = new Regex(profile.Validation); + valid = regex.Match(SettingService.GetSetting(settings, profile.Name, string.Empty)).Success; + } } } return valid; diff --git a/Oqtane.Client/Resources/Modules/Admin/Profiles/Edit.resx b/Oqtane.Client/Resources/Modules/Admin/Profiles/Edit.resx index 720e34f9..8a3a23ca 100644 --- a/Oqtane.Client/Resources/Modules/Admin/Profiles/Edit.resx +++ b/Oqtane.Client/Resources/Modules/Admin/Profiles/Edit.resx @@ -183,4 +183,10 @@ Private? + + Optionally provide a regular expression (RegExp) for validating the value entered + + + Validation: + \ No newline at end of file diff --git a/Oqtane.Server/Migrations/Tenant/04000003_AddProfileValidation.cs b/Oqtane.Server/Migrations/Tenant/04000003_AddProfileValidation.cs new file mode 100644 index 00000000..c2f46cb5 --- /dev/null +++ b/Oqtane.Server/Migrations/Tenant/04000003_AddProfileValidation.cs @@ -0,0 +1,29 @@ +using Microsoft.AspNetCore.Components.Web; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Oqtane.Databases.Interfaces; +using Oqtane.Migrations.EntityBuilders; +using Oqtane.Repository; + +namespace Oqtane.Migrations.Tenant +{ + [DbContext(typeof(TenantDBContext))] + [Migration("Tenant.04.00.00.03")] + public class AddProfileValidation : MultiDatabaseMigration + { + public AddProfileValidation(IDatabase database) : base(database) + { + } + + protected override void Up(MigrationBuilder migrationBuilder) + { + var profileEntityBuilder = new ProfileEntityBuilder(migrationBuilder, ActiveDatabase); + profileEntityBuilder.AddStringColumn("Validation", 200, true); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + // not implemented + } + } +} diff --git a/Oqtane.Shared/Models/Profile.cs b/Oqtane.Shared/Models/Profile.cs index 00c4ea88..e5586a13 100644 --- a/Oqtane.Shared/Models/Profile.cs +++ b/Oqtane.Shared/Models/Profile.cs @@ -68,5 +68,10 @@ namespace Oqtane.Models /// This gives possible values for dropdown input fields. /// public string Options { get; set; } + + /// + /// Optional RegExp validation expression + /// + public string Validation { get; set; } } }