123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553 |
- using Comal.Classes;
- using FastReport;
- using InABox.Clients;
- using InABox.Configuration;
- using InABox.Core;
- using InABox.Core.Reports;
- using InABox.DynamicGrid;
- using InABox.Scripting;
- using InABox.Wpf;
- using InABox.Wpf.Reports;
- using InABox.WPF;
- using PRSDesktop.Configuration;
- using System;
- using System.Collections.Generic;
- using System.ComponentModel;
- using System.Drawing;
- using System.Linq;
- using System.Reflection;
- using System.Text;
- using System.Threading.Tasks;
- using System.Windows;
- using System.Windows.Controls;
- namespace PRSDesktop;
- public interface IPanelHostControl
- {
- void ClearActions();
- void CreatePanelAction(PanelAction action);
- void ClearReports();
- void CreateReport(PanelAction action);
- }
- public class PanelHost : IPanelHost
- {
- public IBasePanel? CurrentPanel { get; private set; }
- public string CurrentModuleName { get; private set; } = "";
- private readonly IPanelHostControl HostControl;
- private readonly List<IPanelActionItem> SetupActions = new();
- public PanelHost(IPanelHostControl hostControl)
- {
- HostControl = hostControl;
- }
- #region Module Tracking
- private int TrackedClicks;
- private int TrackedKeys;
- private DateTime TrackedTicks = DateTime.MinValue;
- public void IncrementTrackingModuleClick()
- {
- if (CurrentPanel is not null)
- TrackedClicks++;
- }
- public void IncrementTrackingModuleKey()
- {
- if (CurrentPanel is not null)
- TrackedKeys++;
- }
- #endregion
- #region IPanelHost
- void IPanelHost.CreatePanelAction(PanelAction action)
- {
- HostControl.CreatePanelAction(action);
- }
- void IPanelHost.CreateReport(PanelAction action)
- {
- HostControl.CreateReport(action);
- }
- void IPanelHost.CreateSetupAction(PanelAction action)
- {
- SetupActions.Add(action);
- }
- void IPanelHost.CreateSetupSeparator()
- {
- SetupActions.Add(new PanelActionSeparator());
- }
- #endregion
- #region Panel Properties
- private void InitializePanelProperties(IBasePanel panel)
- {
- var propertiesInterface = panel.GetType().GetInterfaceDefinition(typeof(IPropertiesPanel<>));
- if (propertiesInterface is not null)
- {
- var propertiesType = propertiesInterface.GenericTypeArguments[0];
- var method = typeof(PanelHost)
- .GetMethod(nameof(InitializePanelPropertiesGeneric), BindingFlags.NonPublic | BindingFlags.Instance)
- ?.MakeGenericMethod(panel.GetType(), propertiesType)
- .Invoke(this, new object?[] { panel });
- }
- }
- private void InitializePanelPropertiesGeneric<TPanel, TProperties>(TPanel panel)
- where TPanel : IPropertiesPanel<TProperties>
- where TProperties : BaseObject, IGlobalConfigurationSettings, new()
- {
- panel.Properties = LoadPanelProperties<TPanel, TProperties>();
- }
- private TProperties LoadPanelProperties<TPanel, TProperties>()
- where TPanel : IPropertiesPanel<TProperties>
- where TProperties : BaseObject, IGlobalConfigurationSettings, new()
- {
- var config = new GlobalConfiguration<TProperties>();
- return config.Load();
- }
- private void SavePanelProperties<TPanel, TProperties>(TProperties properties)
- where TPanel : IPropertiesPanel<TProperties>
- where TProperties : BaseObject, IGlobalConfigurationSettings, new()
- {
- var config = new GlobalConfiguration<TProperties>();
- config.Save(properties);
- }
- private void EditPanelProperties<TPanel, TProperties>()
- where TPanel : IPropertiesPanel<TProperties>
- where TProperties : BaseObject, IGlobalConfigurationSettings, new()
- {
- var properties = LoadPanelProperties<TPanel, TProperties>();
- bool result;
- if (DynamicGridUtils.TryFindDynamicGrid(typeof(DynamicGrid<>), typeof(TProperties), out var gridType))
- {
- var grid = (Activator.CreateInstance(gridType) as DynamicGrid<TProperties>)!;
- result = grid.EditItems(new TProperties[] { properties });
- }
- else
- {
- var grid = new DynamicItemsListGrid<TProperties>();
- result = grid.EditItems(new TProperties[] { properties });
- }
- if (result)
- {
- SavePanelProperties<TPanel, TProperties>(properties);
- }
- }
- private void ConfigurePanel()
- {
- if (CurrentPanel is null) return;
- var propertiesInterface = CurrentPanel.GetType().GetInterfaceDefinition(typeof(IPropertiesPanel<>))!;
- var propertiesType = propertiesInterface.GenericTypeArguments[0];
- var basemethod = typeof(PanelHost)
- .GetMethod(nameof(EditPanelProperties), BindingFlags.NonPublic | BindingFlags.Instance);
- if (basemethod == null)
- return;
- var method = basemethod?.MakeGenericMethod(CurrentPanel.GetType(), propertiesType);
- if (method != null)
- method.Invoke(this, Array.Empty<object?>());
- }
- #endregion
- #region Actions
- private void ReloadActions(string sectionName, DataModel model)
- {
- SetupActions.Clear();
- HostControl.ClearActions();
- HostControl.ClearReports();
-
- if (CurrentPanel != null)
- CurrentPanel.CreateToolbarButtons(this);
- CreateModules(sectionName, model);
-
- CreateReports(sectionName, model);
- }
- #endregion
- #region Custom Modules
- private void CreateModules(string section, DataModel model)
- {
- if (ClientFactory.IsSupported<CustomModule>())
- {
- foreach (var (module, image) in CustomModuleUtils.LoadCustomModuleThumbnails(section, model))
- {
- HostControl.CreatePanelAction(new PanelAction
- {
- Caption = module.Name ?? "",
- Image = image,
- OnExecute = (action) =>
- {
- ClickModule(action, module);
- }
- });
- }
- }
- }
- private void ClickModule(PanelAction action, CustomModule code)
- {
- if (CurrentPanel != null)
- {
- if (!string.IsNullOrWhiteSpace(code.Script))
- try
- {
- Selection selection;
- if (code.SelectedRecords && code.AllRecords)
- selection = RecordSelectionDialog.Execute();
- else if (code.SelectedRecords)
- selection = Selection.Selected;
- else if (code.AllRecords)
- selection = Selection.All;
- else
- selection = Selection.None;
- var result = ScriptDocument.RunCustomModule(CurrentPanel.DataModel(selection), CurrentPanel.Selected(), code.Script);
- if (result)
- CurrentPanel.Refresh();
- }
- catch (CompileException c)
- {
- MessageWindow.ShowError(c.Message, c, shouldLog: false);
- }
- catch (Exception err)
- {
- MessageWindow.ShowError($"Unable to load {action.Caption}", err);
- }
- else
- MessageWindow.ShowMessage("Unable to load " + action.Caption, "Error", image: MessageWindow.WarningImage);
- }
- }
- private void ManageModules(PanelAction action)
- {
- if (CurrentPanel != null)
- {
- var section = CurrentPanel.SectionName;
- var dataModel = CurrentPanel.DataModel(Selection.Selected);
- var manager = new CustomModuleManager()
- {
- Section = section,
- DataModel = dataModel
- };
- manager.ShowDialog();
- ReloadActions(section, dataModel);
- }
- }
- #endregion
- #region Reports
- private IEnumerable<ReportExportDefinition> AddTemplateDefinitions()
- {
- if (CurrentPanel is null)
- return new List<ReportExportDefinition>() { new ReportExportDefinition("Email Report", PRSDesktop.Resources.email, ReportExportType.PDF,
- PRSEmailUtils.DoEmailReport)};
- else
- return PRSEmailUtils.CreateTemplateDefinitions(CurrentPanel.DataModel(Selection.None));
- }
- public static PanelAction CreateReportAction(ReportTemplate template, Func<Selection, DataModel> getDataModel)
- {
- var action = new PanelAction
- {
- Caption = template.Name,
- Image = PRSDesktop.Resources.printer,
- OnExecute = (action) =>
- {
- PrintReport(template.ID, getDataModel);
- }
- };
- if (Security.IsAllowed<CanDesignReports>())
- {
- var menu = new ContextMenu();
- menu.AddItem("Design Report", PRSDesktop.Resources.pencil, () => DesignReport(template.ID, getDataModel));
- action.Menu = menu;
- }
- return action;
- }
- private void CreateReports(string section, DataModel model)
- {
- if (CurrentPanel is null) return;
- var client = new Client<ReportTemplate>();
- var templates = ReportUtils.LoadReports(section, model, Columns.None<ReportTemplate>().Add(x => x.ID, x => x.Name));
- foreach (var template in templates)
- {
- HostControl.CreateReport(CreateReportAction(template, CurrentPanel.DataModel));
- }
- }
- private static void DesignReport(Guid templateID, Func<Selection, DataModel> getDataModel)
- {
- var template = new Client<ReportTemplate>().Load(new Filter<ReportTemplate>(x => x.ID).IsEqualTo(templateID)).FirstOrDefault();
- if (template is null)
- {
- Logger.Send(LogType.Error, "", $"No Report Template with ID '{templateID}'");
- MessageWindow.ShowMessage("Report does not exist!", "Error", image: MessageWindow.WarningImage);
- return;
- }
- ReportUtils.DesignReport(template, getDataModel(Selection.None));
- }
- private static void PrintReport(Guid id, Func<Selection, DataModel> getDataModel)
- {
- var template = new Client<ReportTemplate>().Load(new Filter<ReportTemplate>(x => x.ID).IsEqualTo(id)).FirstOrDefault();
- if (template == null)
- {
- Logger.Send(LogType.Error, "", $"No Report Template with ID '{id}'");
- MessageWindow.ShowMessage("Report does not exist!", "Error", image: MessageWindow.WarningImage);
- return;
- }
- var selection = Selection.None;
- if (template.SelectedRecords && template.AllRecords)
- selection = RecordSelectionDialog.Execute();
- else if (template.SelectedRecords)
- selection = Selection.Selected;
- else if (template.AllRecords)
- selection = Selection.All;
- else
- MessageWindow.ShowMessage(
- "Report must have either [Selected Records] or [All Records] checked to display!",
- "Error",
- image: MessageWindow.WarningImage);
- if (selection != Selection.None)
- ReportUtils.PreviewReport(template, getDataModel(selection), false, Security.IsAllowed<CanDesignReports>());
- }
- private void ManageReports(PanelAction action)
- {
- if (CurrentPanel is null)
- return;
- var section = CurrentPanel.SectionName;
- var model = CurrentPanel.DataModel(Selection.None);
- if (model == null)
- {
- MessageWindow.ShowMessage("No DataModel for " + CurrentPanel.SectionName, "No DataModel");
- return;
- }
- var form = new ReportManager { DataModel = model, Section = section, Populate = true };
- form.ShowDialog();
- ReloadActions(section, model);
- }
- private void ManageEmailTemplates(PanelAction action)
- {
- if (CurrentPanel is null)
- return;
- var section = CurrentPanel.SectionName;
- var model = CurrentPanel.DataModel(Selection.None);
- if (model == null)
- {
- MessageWindow.ShowMessage("No DataModel for " + section, "No DataModel");
- return;
- }
- var window = new EmailTemplateManagerWindow(model);
- window.ShowDialog();
- }
- #endregion
- #region Public Interface
- public void InitialiseSetupMenu(ContextMenu menu)
- {
- var items = new List<IPanelActionItem>();
- items.AddRange(SetupActions);
- items.Add(new PanelActionSeparator());
- if (Security.IsAllowed<CanCustomiseModules>())
- {
- items.Add(new PanelAction("Custom Modules", PRSDesktop.Resources.script, ManageModules));
- }
- if (Security.IsAllowed<CanDesignReports>())
- {
- items.Add(new PanelAction("Reports", PRSDesktop.Resources.printer, ManageReports));
- }
- if (Security.IsAllowed<CanDesignReports>())
- {
- items.Add(new PanelAction("Email Templates", PRSDesktop.Resources.email, ManageEmailTemplates));
- }
- for (var i = 0; i < items.Count; ++i)
- {
- var item = items[i];
- if (item is PanelAction setupAction)
- {
- menu.AddItem(setupAction.Caption, setupAction.Image, setupAction, setupAction.OnExecute);
- }
- else if (item is PanelActionSeparator && i > 0 && i < items.Count - 1)
- {
- var last = items[i - 1];
- if (last is not PanelActionSeparator)
- menu.AddSeparator();
- }
- }
- if (CurrentPanel?.GetType().HasInterface(typeof(IPropertiesPanel<>)) == true && Security.IsAllowed<CanConfigurePanels>())
- {
- var securityInterface = CurrentPanel?.GetType().GetInterfaceDefinition(typeof(IPropertiesPanel<,>));
- var canConfigure = false;
- if (securityInterface is not null)
- {
- var token = securityInterface.GenericTypeArguments[1];
- canConfigure = Security.IsAllowed(token);
- }
- else
- {
- canConfigure = Security.IsAllowed<CanConfigurePanels>();
- }
- if (canConfigure)
- {
- menu.AddItem("Configure Panel", PRSDesktop.Resources.edit, ConfigurePanel);
- }
- }
- if (menu.Items.Count == 0)
- {
- menu.AddItem("No Items", null, null, false);
- }
- }
- public IBasePanel LoadPanel(Type T, string moduleName)
- {
- return (LoadPanelMethod.MakeGenericMethod(T).Invoke(this, new object[] { moduleName })
- as IBasePanel)!;
- }
- private static readonly MethodInfo LoadPanelMethod = typeof(PanelHost)
- .GetMethods().First(x => x.Name == nameof(LoadPanel) && x.IsGenericMethod);
- public T LoadPanel<T>(string moduleName) where T : class, IBasePanel, new()
- {
- var panel = new T();
- CurrentPanel = panel;
- ReportUtils.ExportDefinitions.Clear();
- ReportUtils.ExportDefinitions.AddRange(AddTemplateDefinitions());
- InitializePanelProperties(panel);
- CurrentModuleName = moduleName;
- TrackedTicks = DateTime.Now;
- CurrentPanel.IsReady = false;
- CurrentPanel.Setup();
- CurrentPanel.IsReady = true;
- CurrentPanel.OnUpdateDataModel += ReloadActions;
- var model = CurrentPanel.DataModel(Selection.None);
- var section = CurrentPanel.SectionName;
- ReloadActions(section, model);
- return panel;
- }
- public void Refresh()
- {
- CurrentPanel?.Refresh();
- }
- private void Heartbeat(TimeSpan time, bool closing)
- {
- if (!closing && time.TotalMinutes < 5)
- return;
- TrackedTicks = DateTime.Now;
- if (CurrentPanel is not null)
- {
- //Logger.Send(LogType.Information, "", string.Format("Heartbeat: {0}", CurrentPanel_Label));
- if (ClientFactory.IsSupported<ModuleTracking>())
- {
- var keys = TrackedKeys;
- TrackedKeys = 0;
- var clicks = TrackedClicks;
- TrackedClicks = 0;
- var tracking = new ModuleTracking
- {
- Date = DateTime.Today,
- Module = CurrentModuleName,
- Clicks = clicks,
- Keys = keys,
- ActiveTime = clicks + keys > 0 ? time : new TimeSpan(),
- IdleTime = clicks + keys == 0 ? time : new TimeSpan()
- };
- tracking.User.ID = ClientFactory.UserGuid;
- new Client<ModuleTracking>().Save(tracking, "", (mt, ex) => { });
- }
- CurrentPanel.Heartbeat(time);
- }
- }
- public void Heartbeat()
- {
- Heartbeat(DateTime.Now - TrackedTicks, false);
- }
- public void UnloadPanel(CancelEventArgs? cancel)
- {
- if (CurrentPanel != null)
- {
- Heartbeat(DateTime.Now - TrackedTicks, true);
- try
- {
- CurrentPanel.Shutdown(cancel);
- if (cancel?.Cancel == true)
- {
- return;
- }
- }
- catch (Exception e)
- {
- Logger.Send(LogType.Error, ClientFactory.UserID, string.Format("Error in UnloadPanel(): {0}\n{1}", e.Message, e.StackTrace));
- }
- TrackedTicks = DateTime.MinValue;
- CurrentModuleName = "";
- TrackedClicks = 0;
- TrackedKeys = 0;
- }
- }
- #endregion
- }
|