using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Windows; using System.Windows.Controls; using System.Windows.Media.Imaging; using Comal.Classes; using InABox.Clients; using InABox.Core; using InABox.DynamicGrid; using InABox.Mail; using InABox.Wpf; using InABox.WPF; using NPOI.SS.Formula.Functions; using PRS.Shared; using PRSDesktop.Panels.Users; using Syncfusion.Windows.Shared; namespace PRSDesktop { internal class UserGrid : DynamicDataGrid { private bool ShowAll; public UserGrid() { AddButton("Show All", PRSDesktop.Resources.anonymous.AsBitmapImage(Color.White), ToggleDisabledUsers); ActionColumns.Add(new DynamicTickColumn(x => x.Logins, null, PRSDesktop.Resources.tick.AsBitmapImage(), null, null)); HiddenColumns.Add(x => x.AuthenticatorToken); HiddenColumns.Add(x => x.Logins); HiddenColumns.Add(x => x.Password); OnAfterSave += AfterSave; OnCustomiseEditor += UserGrid_OnCustomiseEditor; OnEditorValueChanged += UserGrid_OnEditorValueChanged; if (Security.IsAllowed()) ActionColumns.Add(new DynamicImageColumn(EmailImage, SendEmail)); } protected override void DoReconfigure(FluentList options) { base.DoReconfigure(options); options.AddRange(DynamicGridOption.RecordCount, DynamicGridOption.FilterRows, DynamicGridOption.MultiSelect, DynamicGridOption.SelectColumns); } private class MobileLinkData : BaseObject { [TextBoxEditor] [EditorSequence(1)] public string ServerURLS { get; set; } = ""; [TextBoxEditor] [EditorSequence(2)] public string From { get; set; } = ""; [TextBoxEditor] [EditorSequence(3)] public string To { get; set; } = ""; [IntegerEditor(ToolTip = "Enter link expiry time (mins)")] [EditorSequence(4)] public int ExpiryTime { get; set; } = 10; } private static readonly string IOSLink = @"prsmobile://open/"; private static readonly string AndroidLink = @"http://www.prsmobile.com/open/"; private bool SendEmail(CoreRow? row) { if (row is null) return false; User user = row.ToObject(); var data = new MobileLinkData { ServerURLS = string.Join(';', App.DatabaseSettings.URLs), From = EmailUtils.GetAddressFromUserOrNull() ?? "", To = user.EmailAddress, ExpiryTime = 10 }; if (DynamicGridUtils.EditObject(data, customiseGrid: (grid) => { grid.OnLoadEditorButtons += (item, buttons) => { buttons.Add("Scan for URL", null, null, (s, o) => { Progress.Show("Looking for available Servers..."); var settings = DataBaseConfiguration.AutoDiscoverServer(); Progress.Close(); if(settings is not null && settings.Protocol == SerializerProtocol.RPC) { data.ServerURLS = string.Join(';', settings.URLs); MessageWindow.ShowMessage("Server found.", "Success"); } else { MessageWindow.ShowMessage("No RPC server found.", "Not found"); } }); }; })) { CreateLink(user, data); } return false; } private static void CreateLink(User user, MobileLinkData data) { var URLs = CreateURLs(data.ServerURLS.Split(';')); var toEncrypt = URLs + "," + user.UserID + "," + user.Password + "," + DateTime.Now.AddMinutes(data.ExpiryTime); var encrypted = Encryption.Encrypt(toEncrypt, "logindetailslink", true); var emailcontent = $"Please ensure PRS Mobile is closed, then choose a link below:\n\n" + $"For Apple devices, click this link: {IOSLink}{encrypted}\n\n" + $"For Android devices (Samsung, Google, Xiaomi, Oppo, Vivo, Huawei, Motorola etc), click this link: {AndroidLink}{encrypted}\n\n" + $"Please restart the app after loading from the link.\n\nThese links will expire after {data.ExpiryTime} minutes."; var message = EmailUtils.CreateMessage(from: data.From, subject: "PRS Mobile Configuration Links", body: emailcontent, to: data.To); EmailUtils.OpenEmail(message); } private static string CreateURLs(string[] urls) { return string.Join("", urls.Select(x => $"{x},")) + "ENDURLS"; } private BitmapImage? EmailImage(CoreRow? arg) { return PRSDesktop.Resources.email.AsBitmapImage(); } private Dictionary UserGrid_OnEditorValueChanged(object sender, string name, object value) { var editorForm = (IDynamicEditorForm)sender; if (name == nameof(User.TwoFactorAuthenticationType)) { var addressEditor = editorForm.FindEditor(nameof(User.Recipient2FA)); var editor = editorForm.FindEditor(name) as LookupEditorControl; var choice = (TwoFactorAuthenticationType)value; var isGoogle = choice == TwoFactorAuthenticationType.GoogleAuthenticator; addressEditor.SetEnabled(!isGoogle); (editor.EditorDefinition as EnumLookupEditor)!.Buttons[0].SetEnabled(isGoogle); } return new(); } private void UserGrid_OnCustomiseEditor(IDynamicEditorForm sender, User[]? items, DynamicGridColumn column, BaseEditor editor) { var user = items?.FirstOrDefault(); if (user is null) return; if (column.ColumnName == nameof(User.TwoFactorAuthenticationType) && editor is EnumLookupEditor enumEditor) { var qrCodeButton = new EditorButton(user, "View QR Code", 100, ViewQRCode_Click, false); qrCodeButton.SetEnabled(user.TwoFactorAuthenticationType == TwoFactorAuthenticationType.GoogleAuthenticator); enumEditor.Buttons = new[] { qrCodeButton }; } else if (column.ColumnName == nameof(User.Recipient2FA)) { editor.Editable = user.TwoFactorAuthenticationType == TwoFactorAuthenticationType.GoogleAuthenticator ? Editable.Disabled : Editable.Enabled; } } private void ViewQRCode_Click(object editor, object? item) { if (item is User user && user.TwoFactorAuthenticationType == TwoFactorAuthenticationType.GoogleAuthenticator) { var qrWindow = new QR2FAWindow(user); qrWindow.ShowDialog(); } } private bool ToggleDisabledUsers(Button btn, CoreRow[] rows) { ShowAll = !ShowAll; UpdateButton(btn, PRSDesktop.Resources.anonymous.AsBitmapImage(Color.White), ShowAll ? "Hide Finished" : "Show All"); return true; } protected override void Reload(Filters criteria, Columns columns, ref SortOrder? sort, Action action) { if (!ShowAll) criteria.Add(new Filter(x => x.Disabled).IsEqualTo(false)); sort = new SortOrder(x => x.UserID); base.Reload(criteria, columns, ref sort, action); } public override void SaveItem(User item) { base.SaveItem(item); if (item.ID == ClientFactory.UserGuid) Security.Reset(); } private void AfterSave(IDynamicEditorForm editor, BaseObject[] items) { var users = items.Cast().ToArray(); var ids = users.Select(x => x.ID).ToArray(); var linkedEmployees = new Client().Query( new Filter(x => x.UserLink.ID).InList(ids), new Columns(x => x.UserLink.ID) ).Rows.Select(r => r.Get(c=>c.UserLink.ID)).ToArray(); var newEmployees = new List(); foreach (var user in users) { if (!linkedEmployees.Contains(user.ID)) { var result = MessageBox.Show($"{user.UserID} is not associated with an employee. Do you wish to create one?", "Create new Employee?", MessageBoxButton.YesNo); if (result == MessageBoxResult.Yes) { var newEmployee = new Employee() { Name = user.Description }; if(!string.IsNullOrWhiteSpace(user.EmailAddress)) newEmployee.Email = user.EmailAddress; newEmployee.UserLink.ID = user.ID; newEmployee.UserLink.Synchronise(user); var grid = DynamicGridUtils.CreateDynamicGrid(typeof(DynamicDataGrid<>), typeof(Employee)); grid.EditItems(new object[] { newEmployee }); } } } } } }