@namespace Oqtane.Modules.Admin.UserProfile @using System.Net @using System.Text.RegularExpressions; @inherits ModuleBase @inject NavigationManager NavigationManager @inject IUserService UserService @inject IProfileService ProfileService @inject ISettingService SettingService @inject INotificationService NotificationService @inject IFileService FileService @inject IFolderService FolderService @inject ITimeZoneService TimeZoneService @inject IJSRuntime jsRuntime @inject IServiceProvider ServiceProvider @inject IStringLocalizer Localizer @inject IStringLocalizer SharedLocalizer @if (_initialized) { @if (PageState.User != null && _photo != null) { @_displayname } else {
}




@if (_allowtwofactor) {

} @if (_allowpasskeys) {
@if (PageState.Route.Scheme == "https") { } else { } @if (_passkeys != null && _passkeys.Count > 0) {
    @Localizer["Passkey"]
@if (context.CredentialId != _passkeyId) { @context.Name } else { }
} else {
@Localizer["Message.Passkeys.None"]
}

} @if (_allowexternallogin) {
@if (_logins != null && _logins.Count > 0) {
  @Localizer["Login"]
@context.Name
} else {
@Localizer["Message.Logins.None"]
}

}

@foreach (Profile profile in _profiles) { var p = profile; if (!p.IsPrivate || UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin)) { if (p.Category != _category) {
@p.Category
_category = p.Category; }
@if (!string.IsNullOrEmpty(p.Options)) { @if (!string.IsNullOrEmpty(p.Autocomplete)) { } else { } } else { @if (p.Rows == 1) { if (!string.IsNullOrEmpty(p.Autocomplete)) { @if (p.IsRequired) { } else { } } else { @if (p.IsRequired) { } else { } } } else { if (!string.IsNullOrEmpty(p.Autocomplete)) { @if (p.IsRequired) { } else { } } else { @if (p.IsRequired) { } else { } } } }
} }



@if (_filter == "to") { @if (_notifications.Any()) {
    @Localizer["From"] @Localizer["Subject"] @Localizer["Received"]
@if (context.IsRead) { @(string.IsNullOrEmpty(context.FromDisplayName) ? SharedLocalizer["System"] : context.FromDisplayName) @context.Subject @string.Format("{0:dd-MMM-yyyy HH:mm:ss}", @context.CreatedOn) } else { @(string.IsNullOrEmpty(context.FromDisplayName) ? SharedLocalizer["System"] : context.FromDisplayName) @context.Subject @string.Format("{0:dd-MMM-yyyy HH:mm:ss}", @context.CreatedOn) } @{ string input = "___"; if (context.Body.Contains(input)) { context.Body = context.Body.Split(input)[0]; context.Body = context.Body.Replace("\n", ""); context.Body = context.Body.Replace("\r", ""); } _notificationSummary = context.Body.Length > 100 ? (context.Body.Substring(0, 97) + "...") : context.Body; } @if (context.IsRead) { @_notificationSummary } else { @_notificationSummary }

} else {
@Localizer["NoNotificationsReceived"]
} } else { @if (_notifications.Any()) {
@Localizer["To"] @Localizer["Subject"] @Localizer["Sent"]
@if (context.IsRead) { @context.ToDisplayName @context.Subject @string.Format("{0:dd-MMM-yyyy HH:mm:ss}", @context.CreatedOn) } else { @context.ToDisplayName @context.Subject @string.Format("{0:dd-MMM-yyyy HH:mm:ss}", @context.CreatedOn) } @{ string input = "___"; if (context.Body.Contains(input)) { context.Body = context.Body.Split(input)[0]; context.Body = context.Body.Replace("\n", ""); context.Body = context.Body.Replace("\r", ""); } _notificationSummary = context.Body.Length > 100 ? (context.Body.Substring(0, 97) + "...") : context.Body; } @if (context.IsRead) { @_notificationSummary } else { @_notificationSummary }

} else {
@Localizer["NoNotificationsSent"]
} }


} @code { public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.View; private bool _initialized = false; private bool _allowtwofactor = false; private bool _allowpasskeys = false; private bool _allowexternallogin = false; private string _username = string.Empty; private string _email = string.Empty; private string _displayname = string.Empty; private FileManager _filemanager; private int _folderid = -1; private List _timezones; private string _timezoneid = string.Empty; private int _photofileid = -1; private File _photo = null; private string _imagefiles = string.Empty; private string _passwordrequirements; private string _password = string.Empty; private string _passwordtype = "password"; private string _togglepassword = string.Empty; private string _confirm = string.Empty; private string _twofactor = "False"; private List _passkeys; private byte[] _passkeyId; private string _passkeyName = string.Empty; private List _logins; private List _profiles; private Dictionary _userSettings; private string _category = string.Empty; private string _filter = "to"; private List _notifications; private string _notificationSummary = string.Empty; protected override async Task OnInitializedAsync() { try { _allowtwofactor = (SettingService.GetSetting(PageState.Site.Settings, "LoginOptions:TwoFactor", "false") == "true"); _allowpasskeys = (SettingService.GetSetting(PageState.Site.Settings, "LoginOptions:Passkeys", "false") == "true"); _allowexternallogin = (SettingService.GetSetting(PageState.Site.Settings, "ExternalLogin:ProviderType", "") != "") ? true : false; if (PageState.User != null) { // identity section _username = PageState.User.Username; _email = PageState.User.Email; _displayname = PageState.User.DisplayName; _timezones = TimeZoneService.GetTimeZones(); _timezoneid = PageState.User.TimeZoneId; var folder = await FolderService.GetFolderAsync(ModuleState.SiteId, PageState.User.FolderPath); if (folder != null) { _folderid = folder.FolderId; } _imagefiles = SettingService.GetSetting(PageState.Site.Settings, "ImageFiles", Constants.ImageFiles); _imagefiles = (string.IsNullOrEmpty(_imagefiles)) ? Constants.ImageFiles : _imagefiles; if (PageState.User.PhotoFileId != null) { _photofileid = PageState.User.PhotoFileId.Value; _photo = await FileService.GetFileAsync(_photofileid); } else { _photofileid = -1; _photo = null; } // security section _passwordrequirements = await UserService.GetPasswordRequirementsAsync(PageState.Site.SiteId); _togglepassword = SharedLocalizer["ShowPassword"]; _twofactor = PageState.User.TwoFactorRequired.ToString(); await GetPasskeys(); await GetLogins(); // profile section _profiles = await ProfileService.GetProfilesAsync(ModuleState.SiteId); foreach (var profile in _profiles) { if (profile.Options.ToLower().StartsWith("entityname:")) { var options = await SettingService.GetSettingsAsync(profile.Options.Substring(11), -1); options.Add("", $"<{SharedLocalizer["Not Specified"]}>"); profile.Options = string.Join(",", options.OrderBy(item => item.Value).Select(kvp => $"{kvp.Key}:{kvp.Value}")); } } _userSettings = PageState.User.Settings; // notification section await LoadNotificationsAsync(); _initialized = true; } 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); } } // identity methods private async Task Save() { try { if (_username != string.Empty && _email != string.Empty) { if (_password == _confirm) { if (ValidateProfiles()) { 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.TimeZoneId = _timezoneid; 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(_userSettings, PageState.User.UserId); await logger.LogInformation("User Profile Saved"); if (!string.IsNullOrEmpty(PageState.ReturnUrl)) { NavigationManager.NavigateTo(PageState.ReturnUrl); } else // legacy behavior { AddModuleMessage(Localizer["Success.Profile.Update"], MessageType.Success); StateHasChanged(); } } else { AddModuleMessage(Localizer["Message.Password.Complexity"], MessageType.Error); } } } else { AddModuleMessage(Localizer["Message.Password.Invalid"], MessageType.Warning); } } else { AddModuleMessage(Localizer["Message.Required.ProfileInfo"], MessageType.Warning); } await ScrollToPageTop(); } catch (Exception ex) { await logger.LogError(ex, "Error Saving User Profile {Error}", ex.Message); AddModuleMessage(Localizer["Error.Profile.Save"], MessageType.Error); } } private void Cancel() { NavigationManager.NavigateTo(PageState.ReturnUrl); } // security methods private void TogglePassword() { if (_passwordtype == "password") { _passwordtype = "text"; _togglepassword = SharedLocalizer["HidePassword"]; } else { _passwordtype = "password"; _togglepassword = SharedLocalizer["ShowPassword"]; } } private async Task GetPasskeys() { if (_allowpasskeys) { _passkeys = await UserService.GetPasskeysAsync(); } } private async Task AddPasskey() { // post back to the Passkey page so that the cookies are set correctly var interop = new Interop(JSRuntime); var fields = new { __RequestVerificationToken = SiteState.AntiForgeryToken, operation = "create", returnurl = NavigateUrl() }; string url = Utilities.TenantUrl(PageState.Alias, "/pages/passkey/"); await interop.SubmitForm(url, fields); } protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender) { // user has initiated a passkey addition if (PageState.QueryString.ContainsKey("options")) { try { var interop = new Interop(JSRuntime); var credential = await interop.CreateCredential(WebUtility.UrlDecode(PageState.QueryString["options"])); if (!string.IsNullOrEmpty(credential)) { // post back to the Passkey page so that the cookies are set correctly var fields = new { __RequestVerificationToken = SiteState.AntiForgeryToken, operation = "validate", credential = credential, returnurl = NavigateUrl(PageState.Page.Path, "tab=Security") }; string url = Utilities.TenantUrl(PageState.Alias, "/pages/passkey/"); await interop.SubmitForm(url, fields); } else { await logger.LogError("Error Adding Passkey"); AddModuleMessage(Localizer["Error.Passkey"], MessageType.Error); } } catch (Exception ex) { await logger.LogError(ex, "Error Adding Passkey"); AddModuleMessage(Localizer["Error.Passkey"], MessageType.Error); } } } } private void EditPasskey(UserPasskey passkey) { _passkeyId = passkey.CredentialId; _passkeyName = passkey.Name; StateHasChanged(); } private async Task DeletePasskey(UserPasskey passkey) { await UserService.DeletePasskeyAsync(passkey.CredentialId); await GetPasskeys(); StateHasChanged(); } private async Task SavePasskey() { if (!string.IsNullOrEmpty(_passkeyName)) { await UserService.UpdatePasskeyAsync(new UserPasskey { CredentialId = _passkeyId, Name = _passkeyName }); await GetPasskeys(); _passkeyName = ""; StateHasChanged(); } } private async Task CancelPasskey() { await GetPasskeys(); _passkeyName = ""; StateHasChanged(); } private async Task GetLogins() { if (_allowexternallogin) { _logins = await UserService.GetLoginsAsync(); } } private async Task DeleteLogin(UserLogin login) { await UserService.DeleteLoginAsync(login.Provider, login.Key); await GetLogins(); StateHasChanged(); } private async Task Logout() { await logger.LogInformation("User Logout Everywhere For Username {Username}", PageState.User?.Username); var url = NavigateUrl(""); // home page if (PageState.Runtime == Shared.Runtime.Hybrid) { if (PageState.User != null) { // hybrid apps utilize an interactive logout await UserService.LogoutUserEverywhereAsync(PageState.User); var authstateprovider = (IdentityAuthenticationStateProvider)ServiceProvider.GetService(typeof(IdentityAuthenticationStateProvider)); authstateprovider.NotifyAuthenticationChanged(); NavigationManager.NavigateTo(url, true); } } else { // post to the Logout page to complete the logout process var fields = new { __RequestVerificationToken = SiteState.AntiForgeryToken, returnurl = url, everywhere = true }; var interop = new Interop(jsRuntime); await interop.SubmitForm(Utilities.TenantUrl(PageState.Alias, "/pages/logout/"), fields); } } // profile methods private string GetProfileValue(string SettingName, string DefaultValue) { string value = SettingService.GetSetting(_userSettings, SettingName, DefaultValue); if (value.Contains("]")) { value = value.Substring(value.IndexOf("]") + 1); } return value; } private void ProfileChanged(ChangeEventArgs e, string SettingName) { var value = (string)e.Value; _userSettings = SettingService.SetSetting(_userSettings, SettingName, value); } private bool ValidateProfiles() { foreach (Profile profile in _profiles) { var value = GetProfileValue(profile.Name, string.Empty); if (string.IsNullOrEmpty(value) && !string.IsNullOrEmpty(profile.DefaultValue)) { _userSettings = SettingService.SetSetting(_userSettings, profile.Name, profile.DefaultValue); } if (!profile.IsPrivate || UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin)) { if (profile.IsRequired && string.IsNullOrEmpty(value)) { AddModuleMessage(string.Format(SharedLocalizer["ProfileRequired"], profile.Title), MessageType.Warning); return false; } if (!string.IsNullOrEmpty(profile.Validation)) { Regex regex = new Regex(profile.Validation); bool valid = regex.Match(value).Success; if (!valid) { AddModuleMessage(string.Format(SharedLocalizer["ProfileInvalid"], profile.Title), MessageType.Warning); return false; } } } } return true; } // notification methods 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 void FilterNotifications(ChangeEventArgs e) { _filter = (string)e.Value; await LoadNotificationsAsync(); StateHasChanged(); } private async Task DeleteNotification(Notification Notification) { try { if (!Notification.IsDeleted) { Notification.IsDeleted = true; await NotificationService.UpdateNotificationAsync(Notification); } else { await NotificationService.DeleteNotificationAsync(Notification.NotificationId); } await logger.LogInformation("Notification Deleted {Notification}", Notification); await LoadNotificationsAsync(); StateHasChanged(); } catch (Exception ex) { await logger.LogError(ex, "Error Deleting Notification {Notification} {Error}", Notification, ex.Message); AddModuleMessage(ex.Message, MessageType.Error); } } private async Task DeleteAllNotifications() { try { ShowProgressIndicator(); foreach(var Notification in _notifications) { if (!Notification.IsDeleted) { Notification.IsDeleted = true; await NotificationService.UpdateNotificationAsync(Notification); } else { await NotificationService.DeleteNotificationAsync(Notification.NotificationId); } await logger.LogInformation("Notification Deleted {Notification}", Notification); } await logger.LogInformation("Notifications Permanently Deleted"); await LoadNotificationsAsync(); HideProgressIndicator(); StateHasChanged(); } catch (Exception ex) { await logger.LogError(ex, "Error Deleting Notifications {Error}", ex.Message); AddModuleMessage(ex.Message, MessageType.Error); HideProgressIndicator(); } } }