|  | @@ -41,6 +41,10 @@ using System.Globalization;
 | 
	
		
			
				|  |  |  using System.Windows.Media.Imaging;
 | 
	
		
			
				|  |  |  using System.Drawing;
 | 
	
		
			
				|  |  |  using Image = System.Windows.Controls.Image;
 | 
	
		
			
				|  |  | +using Microsoft.Win32;
 | 
	
		
			
				|  |  | +using Syncfusion.CompoundFile.DocIO.Net;
 | 
	
		
			
				|  |  | +using System.IO;
 | 
	
		
			
				|  |  | +using sun.security.krb5.@internal;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  namespace PRSDesktop
 | 
	
		
			
				|  |  |  {
 | 
	
	
		
			
				|  | @@ -622,6 +626,7 @@ namespace PRSDesktop
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          public void CreateToolbarButtons(IPanelHost host)
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  | +            host.CreatePanelAction(new PanelAction("Save to Folder", PRSDesktop.Resources.disk, action => SaveToFolder_Click()));
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          public void Heartbeat(TimeSpan time)
 | 
	
	
		
			
				|  | @@ -697,6 +702,67 @@ namespace PRSDesktop
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +        private void SaveToFolder_Click()
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +            if(Form is null || FormType is null)
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                MessageWindow.ShowMessage("Please select a form first.", "Select form");
 | 
	
		
			
				|  |  | +                return;
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            var model = DataModel(Selection.None);
 | 
	
		
			
				|  |  | +            var reports = ReportUtils.LoadReports(Form.ID.ToString(), model).Where(x => x.Visible).ToList();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            var method = typeof(DigitalFormsDashboard).GetMethod("SaveToFolder", BindingFlags.Instance | BindingFlags.NonPublic)!.MakeGenericMethod(FormType);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            var menu = new ContextMenu();
 | 
	
		
			
				|  |  | +            if(reports.Count == 1)
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                method.Invoke(this, new object[] { reports[0] });
 | 
	
		
			
				|  |  | +                return;
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +            else if(reports.Count > 1)
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                foreach (var report in reports)
 | 
	
		
			
				|  |  | +                {
 | 
	
		
			
				|  |  | +                    menu.AddItem(report.Name, null, report, r => method.Invoke(this, new[] { r }));
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +            if(menu.Items.Count == 0)
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                menu.AddItem("No reports", null, null, enabled: false);
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +            menu.IsOpen = true;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        private void SaveToFolder<TForm>(ReportTemplate report)
 | 
	
		
			
				|  |  | +            where TForm : Entity, IDigitalFormInstance, IRemotable, IPersistent, new()
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +            using (var dialog = new System.Windows.Forms.FolderBrowserDialog())
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                var result = dialog.ShowDialog();
 | 
	
		
			
				|  |  | +                if(result == System.Windows.Forms.DialogResult.OK && !string.IsNullOrWhiteSpace(dialog.SelectedPath))
 | 
	
		
			
				|  |  | +                {
 | 
	
		
			
				|  |  | +                    Progress.ShowModal("Saving forms", progress =>
 | 
	
		
			
				|  |  | +                    {
 | 
	
		
			
				|  |  | +                        foreach (var row in DataGrid.SelectedItems.OfType<DataRowView>().Select(x => x.Row))
 | 
	
		
			
				|  |  | +                        {
 | 
	
		
			
				|  |  | +                            var id = (Guid)row["ID"];
 | 
	
		
			
				|  |  | +                            var number = (string)row["Number"];
 | 
	
		
			
				|  |  | +                            progress.Report($"Saving form {number}");
 | 
	
		
			
				|  |  | +                            var dataModel = new DigitalFormReportDataModel<TForm>(
 | 
	
		
			
				|  |  | +                                new Filter<TForm>(x => x.ID).IsEqualTo(id),
 | 
	
		
			
				|  |  | +                                Form!.ID);
 | 
	
		
			
				|  |  | +                            var pdfData = ReportUtils.ReportToPDF(report, dataModel, true);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                            File.WriteAllBytes(Path.Combine(dialog.SelectedPath, Path.ChangeExtension(number, ".pdf")), pdfData);
 | 
	
		
			
				|  |  | +                        }
 | 
	
		
			
				|  |  | +                    });
 | 
	
		
			
				|  |  | +                    MessageWindow.ShowMessage("All done!");
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |          private void Export_Click()
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  |              var formName = Regex.Replace(Form?.Description ?? "", "[^ a-zA-Z0-9]", "");
 | 
	
	
		
			
				|  | @@ -1091,6 +1157,11 @@ namespace PRSDesktop
 | 
	
		
			
				|  |  |              data.Columns.Add("FormData", typeof(string));
 | 
	
		
			
				|  |  |              data.Columns.Add("Number", typeof(string));
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +            if (IsEntityForm)
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                data.Columns.Add("Description", typeof(string));
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |              if (ParentType == typeof(JobITP))
 | 
	
		
			
				|  |  |              {
 | 
	
		
			
				|  |  |                  data.Columns.Add("Job No", typeof(string));
 | 
	
	
		
			
				|  | @@ -1103,7 +1174,7 @@ namespace PRSDesktop
 | 
	
		
			
				|  |  |                  }
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            data.Columns.Add("Description", typeof(string));
 | 
	
		
			
				|  |  | +            data.Columns.Add("Parent_Description", typeof(string));
 | 
	
		
			
				|  |  |              data.Columns.Add("Created", typeof(DateTime));
 | 
	
		
			
				|  |  |              data.Columns.Add("Created By", typeof(string));
 | 
	
		
			
				|  |  |              data.Columns.Add("Completed", typeof(DateTime));
 | 
	
	
		
			
				|  | @@ -1164,6 +1235,10 @@ namespace PRSDesktop
 | 
	
		
			
				|  |  |                      dataRow["Location_Longitude"] = form.Location.Longitude;
 | 
	
		
			
				|  |  |                      dataRow["FormData"] = form.FormData;
 | 
	
		
			
				|  |  |                      dataRow["Number"] = form.Number;
 | 
	
		
			
				|  |  | +                    if (IsEntityForm && form is IEntityForm eForm)
 | 
	
		
			
				|  |  | +                    {
 | 
	
		
			
				|  |  | +                        dataRow["Description"] = eForm.Description;
 | 
	
		
			
				|  |  | +                    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |                      var desc = new List<string>();
 | 
	
		
			
				|  |  |                      foreach (var col in additionalColumns)
 | 
	
	
		
			
				|  | @@ -1173,7 +1248,7 @@ namespace PRSDesktop
 | 
	
		
			
				|  |  |                              desc.Add(val.ToString() ?? "");
 | 
	
		
			
				|  |  |                      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                    dataRow["Description"] = string.Join(" : ", desc);
 | 
	
		
			
				|  |  | +                    dataRow["Parent_Description"] = string.Join(" : ", desc);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |                      dataRow["Created"] = (form as Entity)!.Created;
 | 
	
		
			
				|  |  |                      dataRow["Created By"] = (form as Entity)!.CreatedBy;
 | 
	
	
		
			
				|  | @@ -1277,7 +1352,7 @@ namespace PRSDesktop
 | 
	
		
			
				|  |  |              LoadDataIntoGrid(
 | 
	
		
			
				|  |  |                  variables, questions,
 | 
	
		
			
				|  |  |                  formData,
 | 
	
		
			
				|  |  | -                formQuery.Columns!.ColumnNames().Where(x => x != "ID" && x.StartsWith("Parent.")).ToList(),
 | 
	
		
			
				|  |  | +                formQuery.Columns!.ColumnNames().Where(x => x.StartsWith("Parent.")).ToList(),
 | 
	
		
			
				|  |  |                  ParentType == typeof(JobITP) ? results.Get<JobITP>() : null);
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -1327,6 +1402,13 @@ namespace PRSDesktop
 | 
	
		
			
				|  |  |                  e.Column.Width = 80;
 | 
	
		
			
				|  |  |                  e.Column.HeaderStyle = Application.Current.Resources["TemplateHeaderStyle"] as Style;
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  | +            else if (value.Path.Path.Equals("Description"))
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                e.Column.Width = 100;
 | 
	
		
			
				|  |  | +                e.Column.HeaderStyle = Application.Current.Resources["TemplateHeaderStyle"] as Style;
 | 
	
		
			
				|  |  | +                e.Column.TextAlignment = TextAlignment.Left;
 | 
	
		
			
				|  |  | +                e.Column.HorizontalHeaderContentAlignment = HorizontalAlignment.Left;
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  |              else if (value.Path.Path.Equals("Location_Timestamp"))
 | 
	
		
			
				|  |  |              {
 | 
	
		
			
				|  |  |                  e.Column = new GridImageColumn();
 | 
	
	
		
			
				|  | @@ -1351,8 +1433,9 @@ namespace PRSDesktop
 | 
	
		
			
				|  |  |                  e.Column.Width = 60;
 | 
	
		
			
				|  |  |                  e.Column.HeaderStyle = Application.Current.Resources["TemplateHeaderStyle"] as Style;
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  | -            else if (value.Path.Path.Equals("Description"))
 | 
	
		
			
				|  |  | +            else if (value.Path.Path.Equals("Parent_Description"))
 | 
	
		
			
				|  |  |              {
 | 
	
		
			
				|  |  | +                e.Column.HeaderText = Categories.FirstOrDefault(x => x.Item2 == FormType)?.Item3 ?? "Parent";
 | 
	
		
			
				|  |  |                  e.Column.TextAlignment = TextAlignment.Left;
 | 
	
		
			
				|  |  |                  e.Column.HorizontalHeaderContentAlignment = HorizontalAlignment.Left;
 | 
	
		
			
				|  |  |                  e.Column.Width = 450;
 |