From 786d4809b54507b68e767f2946c91c61d4db8b41 Mon Sep 17 00:00:00 2001 From: Gary Sharp Date: Fri, 14 Feb 2025 16:05:23 +1100 Subject: [PATCH] feature: document instance exporting resolves #154 --- Disco.Models/Disco.Models.csproj | 6 + .../Documents/DocumentExportOptions.cs | 180 ++++ .../Documents/DocumentExportRecord.cs | 23 + .../Services/Exporting/ExportOptionField.cs | 13 + .../Services/Exporting/ExportOptionGroup.cs | 14 + .../Services/Exporting/SavedExport.cs | 1 - .../ConfigDocumentTemplateExportModel.cs | 20 + .../UI/Shared/SharedExportFieldsModel.cs | 12 + Disco.Services/Authorization/Claims.cs | 8 + .../DocumentTemplateClaims.cs | 3 + Disco.Services/Disco.Services.csproj | 1 + Disco.Services/Documents/DocumentExport.cs | 335 +++++++ Disco.Services/Exporting/SavedExports.cs | 3 + Disco.Services/Users/UserExtensions.cs | 4 +- .../Controllers/DocumentTemplateController.cs | 57 ++ .../Areas/Config/ConfigAreaRegistration.cs | 5 + .../Controllers/DocumentTemplateController.cs | 61 +- .../Models/DocumentTemplate/ExportModel.cs | 20 + .../Views/DocumentTemplate/Export.cshtml | 219 +++++ .../DocumentTemplate/Export.generated.cs | 871 ++++++++++++++++++ .../Views/DocumentTemplate/Index.cshtml | 4 + .../Views/DocumentTemplate/Index.generated.cs | 35 +- .../Config/Views/DocumentTemplate/Show.cshtml | 4 + .../Views/DocumentTemplate/Show.generated.cs | 81 +- Disco.Web/ClientSource/Style/Config.css | 28 + Disco.Web/ClientSource/Style/Config.less | 40 + Disco.Web/ClientSource/Style/Config.min.css | 2 +- Disco.Web/Disco.Web.csproj | 11 + ...PI.DocumentTemplateController.generated.cs | 84 ++ ...ig.DocumentTemplateController.generated.cs | 32 + Disco.Web/Models/Shared/ExportFieldsModel.cs | 55 ++ 31 files changed, 2194 insertions(+), 38 deletions(-) create mode 100644 Disco.Models/Services/Documents/DocumentExportOptions.cs create mode 100644 Disco.Models/Services/Documents/DocumentExportRecord.cs create mode 100644 Disco.Models/Services/Exporting/ExportOptionField.cs create mode 100644 Disco.Models/Services/Exporting/ExportOptionGroup.cs create mode 100644 Disco.Models/UI/Config/DocumentTemplate/ConfigDocumentTemplateExportModel.cs create mode 100644 Disco.Models/UI/Shared/SharedExportFieldsModel.cs create mode 100644 Disco.Services/Documents/DocumentExport.cs create mode 100644 Disco.Web/Areas/Config/Models/DocumentTemplate/ExportModel.cs create mode 100644 Disco.Web/Areas/Config/Views/DocumentTemplate/Export.cshtml create mode 100644 Disco.Web/Areas/Config/Views/DocumentTemplate/Export.generated.cs create mode 100644 Disco.Web/Models/Shared/ExportFieldsModel.cs diff --git a/Disco.Models/Disco.Models.csproj b/Disco.Models/Disco.Models.csproj index 07d242c7..f9270623 100644 --- a/Disco.Models/Disco.Models.csproj +++ b/Disco.Models/Disco.Models.csproj @@ -60,6 +60,10 @@ + + + + @@ -198,6 +202,7 @@ + @@ -239,6 +244,7 @@ + diff --git a/Disco.Models/Services/Documents/DocumentExportOptions.cs b/Disco.Models/Services/Documents/DocumentExportOptions.cs new file mode 100644 index 00000000..d3d6dac2 --- /dev/null +++ b/Disco.Models/Services/Documents/DocumentExportOptions.cs @@ -0,0 +1,180 @@ +using Disco.Models.Exporting; +using Disco.Models.Services.Exporting; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; + +namespace Disco.Models.Services.Documents +{ + public class DocumentExportOptions : IExportOptions + { + public int Version { get; set; } = 1; + public ExportFormat Format { get; set; } + + [Required] + public List DocumentTemplateIds { get; set; } = new List(); + + [Display(Name = "Latest Instance Only")] + public bool LatestOnly { get; set; } + + // Document Template + [Display(ShortName = "Document Template", Name = "Identifier", Description = "The identifier of the document template")] + public bool Id { get; set; } + [Display(ShortName = "Document Template", Name = "Description", Description = "The description of the document template")] + public bool Description { get; set; } + [Display(ShortName = "Document Template", Name = "Scope", Description = "The scope of the document template")] + public bool Scope { get; set; } + + // Attachment + [Display(ShortName = "Attachment", Name = "Identifier", Description = "The identifier of the document instance")] + public bool AttachmentId { get; set; } + [Display(ShortName = "Attachment", Name = "Created Date", Description = "The date the document instance was created")] + public bool AttachmentCreatedDate { get; set; } + [Display(ShortName = "Attachment", Name = "Created User", Description = "The user who created the document instance")] + public bool AttachmentCreatedUser { get; set; } + [Display(ShortName = "Attachment", Name = "Filename", Description = "The filename of the document instance")] + public bool AttachmentFilename { get; set; } + [Display(ShortName = "Attachment", Name = "Mime Type", Description = "The mime type of the document instance")] + public bool AttachmentMimeType { get; set; } + [Display(ShortName = "Attachment", Name = "Comments", Description = "The comments of the document instance")] + public bool AttachmentComments { get; set; } + + // Device + [Display(ShortName = "Device", Name = "Serial Number", Description = "The serial number of the device associated with the document instance")] + public bool DeviceSerialNumber { get; set; } + [Display(ShortName = "Device", Name = "Asset Number", Description = "The asset number of the device associated with the document instance")] + public bool DeviceAssetNumber { get; set; } + [Display(ShortName = "Device", Name = "Location", Description = "The location of the device associated with the document instance")] + public bool DeviceLocation { get; set; } + [Display(ShortName = "Device", Name = "Computer Name", Description = "The computer name of the device associated with the document instance")] + public bool DeviceComputerName { get; set; } + [Display(ShortName = "Device", Name = "Last Network Logon", Description = "The last recorded time that the device associated with the document instance accessed the network")] + public bool DeviceLastNetworkLogon { get; set; } + [Display(ShortName = "Device", Name = "Created Date", Description = "The date that the device associated with the document instance was created in Disco ICT")] + public bool DeviceCreatedDate { get; set; } + [Display(ShortName = "Device", Name = "First Enrolled Date", Description = "The date that the device associated with the document instance was first enrolled in Disco ICT")] + public bool DeviceFirstEnrolledDate { get; set; } + [Display(ShortName = "Device", Name = "Last Enrolled Date", Description = "The date that the device associated with the document instance was last enrolled in Disco ICT")] + public bool DeviceLastEnrolledDate { get; set; } + [Display(ShortName = "Device", Name = "Enrolment Trusted", Description = "Whether the device associated with the document instance is trusted to complete an unauthenticated enrolment")] + public bool DeviceAllowUnauthenticatedEnrol { get; set; } + [Display(ShortName = "Device", Name = "Decommissioned Date", Description = "The date that the device associated with the document instance was decommissioned in Disco ICT")] + public bool DeviceDecommissionedDate { get; set; } + [Display(ShortName = "Device", Name = "Decommissioned Reason", Description = "The reason that the device associated with the document instance was decommissioned")] + public bool DeviceDecommissionedReason { get; set; } + + public bool HasDeviceOptions() + => DeviceSerialNumber || DeviceAssetNumber || DeviceLocation || DeviceComputerName || + DeviceLastNetworkLogon || DeviceCreatedDate || DeviceFirstEnrolledDate || DeviceLastEnrolledDate || + DeviceAllowUnauthenticatedEnrol || DeviceDecommissionedDate || DeviceDecommissionedReason; + + // Model + [Display(ShortName = "Model", Name = "Identifier", Description = "The identifier of the device model associated with the device")] + public bool ModelId { get; set; } + [Display(ShortName = "Model", Name = "Description", Description = "The description of the device model associated with the device")] + public bool ModelDescription { get; set; } + [Display(ShortName = "Model", Name = "Manufacturer", Description = "The manufacturer of the device model associated with the device")] + public bool ModelManufacturer { get; set; } + [Display(ShortName = "Model", Name = "Model", Description = "The model of the device model associated with the device")] + public bool ModelModel { get; set; } + [Display(ShortName = "Model", Name = "Type", Description = "The type of device model associated with the device")] + public bool ModelType { get; set; } + public bool HasDeviceModelOptions() + => ModelId || ModelDescription || ModelManufacturer || ModelModel || ModelType; + + // Batch + [Display(ShortName = "Batch", Name = "Identifier", Description = "The identifier of the device batch associated with the device")] + public bool BatchId { get; set; } + [Display(ShortName = "Batch", Name = "Name", Description = "The name of the device batch associated with the device")] + public bool BatchName { get; set; } + [Display(ShortName = "Batch", Name = "Purchase Date", Description = "The purchase date of the device batch associated with the device")] + public bool BatchPurchaseDate { get; set; } + [Display(ShortName = "Batch", Name = "Supplier", Description = "The supplier of the device batch associated with the device")] + public bool BatchSupplier { get; set; } + [Display(ShortName = "Batch", Name = "Unit Cost", Description = "The unit cost of the device batch associated with the device")] + public bool BatchUnitCost { get; set; } + [Display(ShortName = "Batch", Name = "Warranty Valid Until Date", Description = "The warranty valid until date of the device batch associated with the device")] + public bool BatchWarrantyValidUntilDate { get; set; } + [Display(ShortName = "Batch", Name = "Insured Date", Description = "The insured date of the device batch associated with the device")] + public bool BatchInsuredDate { get; set; } + [Display(ShortName = "Batch", Name = "Insurance Supplier", Description = "The insurance supplier of the device batch associated with the device")] + public bool BatchInsuranceSupplier { get; set; } + [Display(ShortName = "Batch", Name = "Insured Until Date", Description = "The insured until date of the device batch associated with the device")] + public bool BatchInsuredUntilDate { get; set; } + public bool HasDeviceBatchOptions() + => BatchId || BatchName || BatchPurchaseDate || BatchSupplier || BatchUnitCost || + BatchWarrantyValidUntilDate || BatchInsuredDate || BatchInsuranceSupplier || BatchInsuredUntilDate; + + // Profile + [Display(ShortName = "Profile", Name = "Identifier", Description = "The identifier of the device profile associated with the device")] + public bool ProfileId { get; set; } + [Display(ShortName = "Profile", Name = "Name", Description = "The name of the device profile associated with the device")] + public bool ProfileName { get; set; } + [Display(ShortName = "Profile", Name = "Short Name", Description = "The short name of the device profile associated with the device")] + public bool ProfileShortName { get; set; } + public bool HasDeviceProfileOptions() + => ProfileId || ProfileName || ProfileShortName; + + // Job + [Display(ShortName = "Job", Name = "Identifier", Description = "The identifier of the job associated with the document instance")] + public bool JobId { get; set; } + [Display(ShortName = "Job", Name = "Status", Description = "The status of the job associated with the document instance")] + public bool JobStatus { get; set; } + [Display(ShortName = "Job", Name = "Type", Description = "The type of the job associated with the document instance")] + public bool JobType { get; set; } + [Display(ShortName = "Job", Name = "Sub Types", Description = "The sub types of the job associated with the document instance")] + public bool JobSubTypes { get; set; } + [Display(ShortName = "Job", Name = "Opened Date", Description = "The date the job was opened associated with the document instance")] + public bool JobOpenedDate { get; set; } + [Display(ShortName = "Job", Name = "Opened User", Description = "The user who opened the job associated with the document instance")] + public bool JobOpenedUser { get; set; } + [Display(ShortName = "Job", Name = "Expected Closed Date", Description = "The expected closed date of the job associated with the document instance")] + public bool JobExpectedClosedDate { get; set; } + [Display(ShortName = "Job", Name = "Closed Date", Description = "The date the job was closed associated with the document instance")] + public bool JobClosedDate { get; set; } + [Display(ShortName = "Job", Name = "Closed User", Description = "The user who closed the job associated with the document instance")] + public bool JobClosedUser { get; set; } + public bool HasJobOptions() + => JobId || JobStatus || JobType || JobSubTypes || JobOpenedDate || JobOpenedUser || + JobExpectedClosedDate || JobClosedDate || JobClosedUser; + + // User + [Display(ShortName = "User", Name = "Identifier", Description = "The identifier of the user associated with the document instance")] + public bool UserId { get; set; } + [Display(ShortName = "User", Name = "Display Name", Description = "The display name of the user associated with the document instance")] + public bool UserDisplayName { get; set; } + [Display(ShortName = "User", Name = "Surname", Description = "The surname of the user associated with the document instance")] + public bool UserSurname { get; set; } + [Display(ShortName = "User", Name = "Given Name", Description = "The given name of the user associated with the document instance")] + public bool UserGivenName { get; set; } + [Display(ShortName = "User", Name = "Phone Number", Description = "The phone number of the user associated with the document instance")] + public bool UserPhoneNumber { get; set; } + [Display(ShortName = "User", Name = "Email Address", Description = "The email address of the user associated with the document instance")] + public bool UserEmailAddress { get; set; } + public List UserDetailCustom { get; set; } = new List(); + public bool HasUserOptions() + => UserDisplayName || UserSurname || UserGivenName || UserPhoneNumber || UserEmailAddress || UserDetailCustom.Any(); + + public static DocumentExportOptions DefaultOptions() + { + return new DocumentExportOptions() + { + Format = ExportFormat.Xlsx, + LatestOnly = true, + Id = true, + Description = true, + Scope = true, + AttachmentId = true, + AttachmentCreatedUser = true, + AttachmentCreatedDate = true, + AttachmentComments = true, + DeviceSerialNumber = true, + JobId = true, + JobStatus = true, + JobType = true, + UserId = true, + UserDisplayName = true, + }; + } + } +} diff --git a/Disco.Models/Services/Documents/DocumentExportRecord.cs b/Disco.Models/Services/Documents/DocumentExportRecord.cs new file mode 100644 index 00000000..439d0f68 --- /dev/null +++ b/Disco.Models/Services/Documents/DocumentExportRecord.cs @@ -0,0 +1,23 @@ +using Disco.Models.Exporting; +using Disco.Models.Repository; +using System.Collections.Generic; + +namespace Disco.Models.Services.Documents +{ + public class DocumentExportRecord : IExportRecord + { + public DocumentTemplate DocumentTemplate { get; set; } + public IAttachment Attachment { get; set; } + public IAttachmentTarget AttachmentTarget { get; set; } + + public Device Device { get; set; } + + public Job Job { get; set; } + public string JobStatus { get; set; } + public string JobTypeDescription { get; set; } + public IEnumerable JobSubTypeDescriptions { get; set; } + + public User User { get; set; } + public Dictionary UserCustomDetails { get; set; } + } +} diff --git a/Disco.Models/Services/Exporting/ExportOptionField.cs b/Disco.Models/Services/Exporting/ExportOptionField.cs new file mode 100644 index 00000000..ceab4e57 --- /dev/null +++ b/Disco.Models/Services/Exporting/ExportOptionField.cs @@ -0,0 +1,13 @@ +namespace Disco.Models.Services.Exporting +{ + public class ExportOptionField + { + public string GroupName { get; set; } + public string Name { get; set; } + public string DisplayName { get; set; } + public string Description { get; set; } + public bool Checked { get; set; } + public string Key { get; set; } + public string Value { get; set; } + } +} diff --git a/Disco.Models/Services/Exporting/ExportOptionGroup.cs b/Disco.Models/Services/Exporting/ExportOptionGroup.cs new file mode 100644 index 00000000..7c861832 --- /dev/null +++ b/Disco.Models/Services/Exporting/ExportOptionGroup.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; + +namespace Disco.Models.Services.Exporting +{ + public class ExportOptionGroup : List + { + public string Name { get; set; } + + public ExportOptionGroup(string name) + { + Name = name; + } + } +} diff --git a/Disco.Models/Services/Exporting/SavedExport.cs b/Disco.Models/Services/Exporting/SavedExport.cs index fdb5bc33..e444ea6c 100644 --- a/Disco.Models/Services/Exporting/SavedExport.cs +++ b/Disco.Models/Services/Exporting/SavedExport.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; namespace Disco.Models.Services.Exporting { diff --git a/Disco.Models/UI/Config/DocumentTemplate/ConfigDocumentTemplateExportModel.cs b/Disco.Models/UI/Config/DocumentTemplate/ConfigDocumentTemplateExportModel.cs new file mode 100644 index 00000000..c5bcd484 --- /dev/null +++ b/Disco.Models/UI/Config/DocumentTemplate/ConfigDocumentTemplateExportModel.cs @@ -0,0 +1,20 @@ +using Disco.Models.Services.Documents; +using Disco.Models.Services.Exporting; +using Disco.Models.UI; +using Disco.Models.UI.Shared; +using System; +using System.Collections.Generic; + +namespace Disco.Models.Areas.Config.UI.UserFlag +{ + public interface ConfigDocumentTemplateExportModel : BaseUIModel + { + DocumentExportOptions Options { get; set; } + + Guid? ExportId { get; set; } + ExportResult ExportResult { get; set; } + + List DocumentTemplates { get; set; } + SharedExportFieldsModel Fields { get; set; } + } +} diff --git a/Disco.Models/UI/Shared/SharedExportFieldsModel.cs b/Disco.Models/UI/Shared/SharedExportFieldsModel.cs new file mode 100644 index 00000000..2c5ba465 --- /dev/null +++ b/Disco.Models/UI/Shared/SharedExportFieldsModel.cs @@ -0,0 +1,12 @@ +using Disco.Models.Services.Exporting; +using System.Collections.Generic; + +namespace Disco.Models.UI.Shared +{ + public interface SharedExportFieldsModel : BaseUIModel + where T : IExportOptions + { + T Options { get; set; } + List FieldGroups { get; set; } + } +} diff --git a/Disco.Services/Authorization/Claims.cs b/Disco.Services/Authorization/Claims.cs index f448232c..21345e59 100644 --- a/Disco.Services/Authorization/Claims.cs +++ b/Disco.Services/Authorization/Claims.cs @@ -51,6 +51,7 @@ namespace Disco.Services.Authorization { "Config.DocumentTemplate.Configure", new Tuple, Action, string, string, bool>(c => c.Config.DocumentTemplate.Configure, (c, v) => c.Config.DocumentTemplate.Configure = v, "Configure Document Templates", "Can configure document templates", false) }, { "Config.DocumentTemplate.Create", new Tuple, Action, string, string, bool>(c => c.Config.DocumentTemplate.Create, (c, v) => c.Config.DocumentTemplate.Create = v, "Create Document Templates", "Can create document templates", false) }, { "Config.DocumentTemplate.Delete", new Tuple, Action, string, string, bool>(c => c.Config.DocumentTemplate.Delete, (c, v) => c.Config.DocumentTemplate.Delete = v, "Delete Document Templates", "Can delete document templates", false) }, + { "Config.DocumentTemplate.Export", new Tuple, Action, string, string, bool>(c => c.Config.DocumentTemplate.Export, (c, v) => c.Config.DocumentTemplate.Export = v, "Export Attachment Instances", "Can export document attachment instances", false) }, { "Config.DocumentTemplate.UndetectedPages", new Tuple, Action, string, string, bool>(c => c.Config.DocumentTemplate.UndetectedPages, (c, v) => c.Config.DocumentTemplate.UndetectedPages = v, "Process Undetected Pages", "Can show and assign imported documents which were not undetected", false) }, { "Config.DocumentTemplate.ShowStatus", new Tuple, Action, string, string, bool>(c => c.Config.DocumentTemplate.ShowStatus, (c, v) => c.Config.DocumentTemplate.ShowStatus = v, "Show Document Template Import Status", "Can show the document template import status", false) }, { "Config.DocumentTemplate.Show", new Tuple, Action, string, string, bool>(c => c.Config.DocumentTemplate.Show, (c, v) => c.Config.DocumentTemplate.Show = v, "Show Document Templates", "Can show document templates", false) }, @@ -275,6 +276,7 @@ namespace Disco.Services.Authorization new ClaimNavigatorItem("Config.DocumentTemplate.Configure", false), new ClaimNavigatorItem("Config.DocumentTemplate.Create", false), new ClaimNavigatorItem("Config.DocumentTemplate.Delete", false), + new ClaimNavigatorItem("Config.DocumentTemplate.Export", false), new ClaimNavigatorItem("Config.DocumentTemplate.UndetectedPages", false), new ClaimNavigatorItem("Config.DocumentTemplate.ShowStatus", false), new ClaimNavigatorItem("Config.DocumentTemplate.Show", false), @@ -595,6 +597,7 @@ namespace Disco.Services.Authorization c.Config.DocumentTemplate.Configure = true; c.Config.DocumentTemplate.Create = true; c.Config.DocumentTemplate.Delete = true; + c.Config.DocumentTemplate.Export = true; c.Config.DocumentTemplate.UndetectedPages = true; c.Config.DocumentTemplate.ShowStatus = true; c.Config.DocumentTemplate.Show = true; @@ -990,6 +993,11 @@ namespace Disco.Services.Authorization /// public const string Delete = "Config.DocumentTemplate.Delete"; + /// Export Attachment Instances + /// Can export document attachment instances + /// + public const string Export = "Config.DocumentTemplate.Export"; + /// Process Undetected Pages /// Can show and assign imported documents which were not undetected /// diff --git a/Disco.Services/Authorization/Roles/ClaimGroups/Configuration/DocumentTemplate/DocumentTemplateClaims.cs b/Disco.Services/Authorization/Roles/ClaimGroups/Configuration/DocumentTemplate/DocumentTemplateClaims.cs index 144e32df..0226d52f 100644 --- a/Disco.Services/Authorization/Roles/ClaimGroups/Configuration/DocumentTemplate/DocumentTemplateClaims.cs +++ b/Disco.Services/Authorization/Roles/ClaimGroups/Configuration/DocumentTemplate/DocumentTemplateClaims.cs @@ -26,6 +26,9 @@ [ClaimDetails("Bulk Generate Document Templates", "Can bulk generate document templates")] public bool BulkGenerate { get; set; } + + [ClaimDetails("Export Attachment Instances", "Can export document attachment instances")] + public bool Export { get; set; } [ClaimDetails("Process Undetected Pages", "Can show and assign imported documents which were not undetected")] public bool UndetectedPages { get; set; } diff --git a/Disco.Services/Disco.Services.csproj b/Disco.Services/Disco.Services.csproj index b9971cbb..185d8f98 100644 --- a/Disco.Services/Disco.Services.csproj +++ b/Disco.Services/Disco.Services.csproj @@ -366,6 +366,7 @@ + diff --git a/Disco.Services/Documents/DocumentExport.cs b/Disco.Services/Documents/DocumentExport.cs new file mode 100644 index 00000000..75347431 --- /dev/null +++ b/Disco.Services/Documents/DocumentExport.cs @@ -0,0 +1,335 @@ +using Disco.Data.Repository; +using Disco.Models.Exporting; +using Disco.Models.Repository; +using Disco.Models.Services.Documents; +using Disco.Models.Services.Exporting; +using Disco.Services.Exporting; +using Disco.Services.Tasks; +using Newtonsoft.Json; +using System.Data.Entity; +using System; +using System.Collections.Generic; +using System.Linq; +using Disco.Services.Users; +using Disco.Services.Plugins.Features.DetailsProvider; + +namespace Disco.Services.Documents +{ + public class DocumentExport : IExport + { + public Guid Id { get; set; } + public string Name { get; } = "Document Export"; + public DocumentExportOptions Options { get; set; } + + public string FilenamePrefix { get; } = "DocumentExport"; + public string ExcelWorksheetName { get; } = "DocumentExport"; + public string ExcelTableName { get; } = "Documents"; + + public DocumentExport(DocumentExportOptions options) + { + Id = Guid.NewGuid(); + Options = options; + } + + [JsonConstructor] + public DocumentExport() + : this(DocumentExportOptions.DefaultOptions()) + { + } + + public ExportMetadata BuildMetadata(DiscoDataContext database, List records, IScheduledTaskStatus status) + { + var metadata = new ExportMetadata(Options); + + // Document Template + metadata.Add(o => o.Id, r => r.DocumentTemplate.Id); + metadata.Add(o => o.Description, r => r.DocumentTemplate?.Description); + metadata.Add(o => o.Scope, r => r.DocumentTemplate?.Scope); + + // Attachment + metadata.Add(o => o.AttachmentId, r => r.Attachment.Id); + metadata.Add(o => o.AttachmentCreatedDate, r => r.Attachment.Timestamp); + metadata.Add(o => o.AttachmentCreatedUser, r => r.Attachment.TechUserId); + metadata.Add(o => o.AttachmentFilename, r => r.Attachment.Filename); + metadata.Add(o => o.AttachmentMimeType, r => r.Attachment.MimeType); + metadata.Add(o => o.AttachmentComments, r => r.Attachment.Comments); + + // Device + metadata.Add(o => o.DeviceSerialNumber, r => r.Device?.SerialNumber); + metadata.Add(o => o.DeviceAssetNumber, r => r.Device?.AssetNumber); + metadata.Add(o => o.DeviceLocation, r => r.Device?.Location); + metadata.Add(o => o.DeviceComputerName, r => r.Device?.DeviceDomainId); + metadata.Add(o => o.DeviceLastNetworkLogon, r => r.Device?.LastNetworkLogonDate); + metadata.Add(o => o.DeviceCreatedDate, r => r.Device?.CreatedDate); + metadata.Add(o => o.DeviceFirstEnrolledDate, r => r.Device?.EnrolledDate); + metadata.Add(o => o.DeviceLastEnrolledDate, r => r.Device?.LastEnrolDate); + metadata.Add(o => o.DeviceAllowUnauthenticatedEnrol, r => r.Device?.AllowUnauthenticatedEnrol); + metadata.Add(o => o.DeviceDecommissionedDate, r => r.Device?.DecommissionedDate); + metadata.Add(o => o.DeviceDecommissionedReason, r => r.Device?.DecommissionReason?.ToString()); + + // Model + metadata.Add(o => o.ModelId, r => r.Device?.DeviceModel?.Id); + metadata.Add(o => o.ModelDescription, r => r.Device?.DeviceModel?.Description); + metadata.Add(o => o.ModelManufacturer, r => r.Device?.DeviceModel?.Manufacturer); + metadata.Add(o => o.ModelModel, r => r.Device?.DeviceModel?.Model); + metadata.Add(o => o.ModelType, r => r.Device?.DeviceModel?.ModelType); + + // Batch + metadata.Add(o => o.BatchId, r => r.Device?.DeviceBatch?.Id); + metadata.Add(o => o.BatchName, r => r.Device?.DeviceBatch?.Name); + metadata.Add(o => o.BatchPurchaseDate, r => r.Device?.DeviceBatch?.PurchaseDate); + metadata.Add(o => o.BatchSupplier, r => r.Device?.DeviceBatch?.Supplier); + metadata.Add(o => o.BatchUnitCost, r => r.Device?.DeviceBatch?.UnitCost, Exporter.CsvEncoders.NullableCurrencyEncoder); + metadata.Add(o => o.BatchWarrantyValidUntilDate, r => r.Device?.DeviceBatch?.WarrantyValidUntil); + metadata.Add(o => o.BatchInsuredDate, r => r.Device?.DeviceBatch?.InsuredDate); + metadata.Add(o => o.BatchInsuranceSupplier, r => r.Device?.DeviceBatch?.InsuranceSupplier); + metadata.Add(o => o.BatchInsuredUntilDate, r => r.Device?.DeviceBatch?.InsuredUntil); + + // Profile + metadata.Add(o => o.ProfileId, r => r.Device?.DeviceProfile?.Id); + metadata.Add(o => o.ProfileName, r => r.Device?.DeviceProfile?.Name); + metadata.Add(o => o.ProfileShortName, r => r.Device?.DeviceProfile?.ShortName); + + // Job + metadata.Add(o => o.JobId, r => r.Job?.Id); + metadata.Add(o => o.JobStatus, r => r.JobStatus == null ? null : Job.JobStatusIds.StatusDescriptions.TryGetValue(r.JobStatus, out var jobStatus) ? jobStatus : "Unknown"); + metadata.Add(o => o.JobType, r => r.JobTypeDescription); + metadata.Add(o => o.JobSubTypes, r => r.JobSubTypeDescriptions == null ? null : string.Join(", ", r.JobSubTypeDescriptions)); + metadata.Add(o => o.JobOpenedDate, r => r.Job?.OpenedDate); + metadata.Add(o => o.JobOpenedUser, r => r.Job?.OpenedTechUserId); + metadata.Add(o => o.JobExpectedClosedDate, r => r.Job?.ExpectedClosedDate); + metadata.Add(o => o.JobClosedDate, r => r.Job?.ClosedDate); + metadata.Add(o => o.JobClosedUser, r => r.Job?.ClosedTechUserId); + + // User + metadata.Add(o => o.UserId, r => r.User?.UserId); + metadata.Add(o => o.UserDisplayName, r => r.User?.DisplayName); + metadata.Add(o => o.UserSurname, r => r.User?.Surname); + metadata.Add(o => o.UserGivenName, r => r.User?.GivenName); + metadata.Add(o => o.UserPhoneNumber, r => r.User?.PhoneNumber); + metadata.Add(o => o.UserEmailAddress, r => r.User?.EmailAddress); + + // User Custom Details + if (Options.UserDetailCustom.Any()) + { + foreach (var key in Options.UserDetailCustom.OrderBy(k => k, StringComparer.OrdinalIgnoreCase)) + { + metadata.Add(key, r => r.UserCustomDetails != null && r.UserCustomDetails.TryGetValue(key, out var value) ? value : null); + } + } + + return metadata; + } + + public List BuildRecords(DiscoDataContext database, IScheduledTaskStatus status) + { + var records = new List(); + var documentTemplates = database.DocumentTemplates.Where(t => Options.DocumentTemplateIds.Contains(t.Id)).ToList(); + + foreach (var documentTemplate in documentTemplates) + { + var documentRecords = BuildDocumentRecords(database, documentTemplate, status); + records.AddRange(documentRecords); + } + + return records; + } + + private List BuildDocumentRecords(DiscoDataContext database, DocumentTemplate document, IScheduledTaskStatus status) + { + switch (document.AttachmentType) + { + case AttachmentTypes.Device: + return BuildDeviceDocumentRecords(database, document, status); + case AttachmentTypes.Job: + return BuildJobDocumentRecords(database, document, status); + case AttachmentTypes.User: + return BuildUserDocumentRecords(database, document, status); + default: + throw new NotSupportedException($"Unsupported document scope: {document.Scope}"); + } + } + + private List BuildDeviceDocumentRecords(DiscoDataContext database, DocumentTemplate document, IScheduledTaskStatus status) + { + var query = database.DeviceAttachments + .Include(a => a.Device); + if (Options.HasDeviceBatchOptions()) + query = query.Include(a => a.Device.DeviceBatch); + if (Options.HasDeviceModelOptions()) + query = query.Include(a => a.Device.DeviceModel); + if (Options.HasDeviceProfileOptions()) + query = query.Include(a => a.Device.DeviceProfile); + if (Options.HasUserOptions()) + query = query.Include(a => a.Device.AssignedUser); + + query = query.Where(a => a.DocumentTemplateId == document.Id); + + if (Options.HasUserOptions()) + RefreshAdUsers(database, query.Select(d => d.Device.AssignedUserId).Distinct().ToList(), status); + + status.UpdateStatus(15, "Extracting records from the database"); + var attachments = query.OrderBy(a => a.Timestamp).ToList(); + + if (Options.LatestOnly) + { + attachments.Reverse(); + attachments = attachments.GroupBy(a => a.DeviceSerialNumber).Select(g => g.First()).OrderBy(a => a.Timestamp).ToList(); + } + + var records = attachments.Select(a => new DocumentExportRecord() + { + DocumentTemplate = document, + Attachment = a, + Device = a.Device, + Job = null, + User = a.Device.AssignedUser, + }).ToList(); + + if (Options.UserDetailCustom.Any()) + AddUserCustomDetails(database, records, status); + + return records; + } + + private List BuildJobDocumentRecords(DiscoDataContext database, DocumentTemplate document, IScheduledTaskStatus status) + { + var query = database.JobAttachments + .Include(a => a.Job); + if (Options.JobStatus) + { + query = query + .Include(a => a.Job.JobMetaWarranty) + .Include(a => a.Job.JobMetaNonWarranty) + .Include(a => a.Job.JobMetaInsurance); + } + if (Options.JobType) + query = query.Include(a => a.Job.JobType); + if (Options.JobSubTypes) + query = query.Include(a => a.Job.JobSubTypes); + if (Options.HasDeviceOptions()) + query = query.Include(a => a.Job.Device); + if (Options.HasDeviceBatchOptions()) + query = query.Include(a => a.Job.Device.DeviceBatch); + if (Options.HasDeviceModelOptions()) + query = query.Include(a => a.Job.Device.DeviceModel); + if (Options.HasDeviceProfileOptions()) + query = query.Include(a => a.Job.Device.DeviceProfile); + if (Options.HasUserOptions()) + query = query.Include(a => a.Job.User); + + query = query.Where(a => a.DocumentTemplateId == document.Id); + + if (Options.HasUserOptions()) + RefreshAdUsers(database, query.Select(d => d.Job.UserId).Distinct().ToList(), status); + + status.UpdateStatus(15, "Extracting records from the database"); + var attachments = query.OrderBy(a => a.Timestamp).ToList(); + + if (Options.LatestOnly) + { + attachments.Reverse(); + attachments = attachments.GroupBy(a => a.JobId).Select(g => g.First()).OrderBy(a => a.Timestamp).ToList(); + } + + var records = attachments.Select(a => new DocumentExportRecord() + { + DocumentTemplate = document, + Attachment = a, + Device = a.Job.Device, + Job = a.Job, + User = a.Job.User, + }).ToList(); + + if (Options.UserDetailCustom.Any()) + AddUserCustomDetails(database, records, status); + + return records; + } + + private List BuildUserDocumentRecords(DiscoDataContext database, DocumentTemplate document, IScheduledTaskStatus status) + { + var query = database.UserAttachments + .Include(a => a.User); + + if (Options.HasDeviceOptions()) + query = query.Include(a => a.User.DeviceUserAssignments.Select(u => u.Device)); + if (Options.HasDeviceBatchOptions()) + query = query.Include(a => a.User.DeviceUserAssignments.Select(u => u.Device.DeviceBatch)); + if (Options.HasDeviceModelOptions()) + query = query.Include(a => a.User.DeviceUserAssignments.Select(u => u.Device.DeviceModel)); + if (Options.HasDeviceProfileOptions()) + query = query.Include(a => a.User.DeviceUserAssignments.Select(u => u.Device.DeviceProfile)); + + query = query.Where(a => a.DocumentTemplateId == document.Id); + + if (Options.HasUserOptions()) + RefreshAdUsers(database, query.Select(d => d.UserId).Distinct().ToList(), status); + + status.UpdateStatus(15, "Extracting records from the database"); + var attachments = query.OrderBy(a => a.Timestamp).ToList(); + + if (Options.LatestOnly) + { + attachments.Reverse(); + attachments = attachments.GroupBy(a => a.UserId).Select(g => g.First()).OrderBy(a => a.Timestamp).ToList(); + } + + var records = attachments.Select(a => new DocumentExportRecord() + { + DocumentTemplate = document, + Attachment = a, + Device = a.User.DeviceUserAssignments? + .Where(u => !u.UnassignedDate.HasValue) + .OrderByDescending(u => u.AssignedDate) + .FirstOrDefault()?.Device, + Job = null, + User = a.User, + }).ToList(); + + if (Options.UserDetailCustom.Any()) + AddUserCustomDetails(database, records, status); + + return records; + } + + private static void RefreshAdUsers(DiscoDataContext database, List userIds, IScheduledTaskStatus status) + { + if (!userIds.Any()) + return; + + status.UpdateStatus(5, "Refreshing user details from Active Directory"); + foreach (var userId in userIds) + { + if (string.IsNullOrWhiteSpace(userId)) + continue; + try + { + UserService.GetUser(userId, database, true); + } + catch (Exception) { } // Ignore Errors + } + } + + private static void AddUserCustomDetails(DiscoDataContext database, List records, IScheduledTaskStatus status) + { + if (!records.Any(r => r.User != null)) + return; + status.UpdateStatus(50, "Extracting custom user detail records"); + var detailsService = new DetailsProviderService(database); + var cache = new Dictionary>(StringComparer.Ordinal); + foreach (var record in records) + { + var userId = record.User?.UserId; + if (string.IsNullOrWhiteSpace(userId)) + continue; + if (!cache.TryGetValue(userId, out var details)) + details = detailsService.GetDetails(record.User); + record.UserCustomDetails = details; + } + } + + public ExportResult Export(DiscoDataContext database, IScheduledTaskStatus status) + => Exporter.Export(this, database, status); + } +} diff --git a/Disco.Services/Exporting/SavedExports.cs b/Disco.Services/Exporting/SavedExports.cs index 2ddb7941..83868be5 100644 --- a/Disco.Services/Exporting/SavedExports.cs +++ b/Disco.Services/Exporting/SavedExports.cs @@ -3,12 +3,14 @@ using Disco.Models.Exporting; using Disco.Models.Repository; using Disco.Models.Services.Devices; using Disco.Models.Services.Devices.DeviceFlag; +using Disco.Models.Services.Documents; using Disco.Models.Services.Exporting; using Disco.Models.Services.Jobs; using Disco.Models.Services.Users.UserFlags; using Disco.Services.Authorization; using Disco.Services.Devices; using Disco.Services.Devices.DeviceFlags; +using Disco.Services.Documents; using Disco.Services.Jobs; using Disco.Services.Logging; using Disco.Services.Tasks; @@ -32,6 +34,7 @@ namespace Disco.Services.Exporting RegisterExportType(); RegisterExportType(); RegisterExportType(); + RegisterExportType(); } internal static void RegisterExportType() diff --git a/Disco.Services/Users/UserExtensions.cs b/Disco.Services/Users/UserExtensions.cs index 445b59e6..cb0dfb59 100644 --- a/Disco.Services/Users/UserExtensions.cs +++ b/Disco.Services/Users/UserExtensions.cs @@ -43,7 +43,9 @@ namespace Disco.Services public static List CurrentDeviceUserAssignments(this User u) { - return u.DeviceUserAssignments.Where(dua => !dua.UnassignedDate.HasValue).ToList(); + return u.DeviceUserAssignments? + .Where(dua => !dua.UnassignedDate.HasValue) + .ToList() ?? new List(0); } public static bool CanCreateJob(this User u) diff --git a/Disco.Web/Areas/API/Controllers/DocumentTemplateController.cs b/Disco.Web/Areas/API/Controllers/DocumentTemplateController.cs index 750ba47c..663b0c1a 100644 --- a/Disco.Web/Areas/API/Controllers/DocumentTemplateController.cs +++ b/Disco.Web/Areas/API/Controllers/DocumentTemplateController.cs @@ -5,13 +5,17 @@ using Disco.Services; using Disco.Services.Authorization; using Disco.Services.Documents; using Disco.Services.Documents.ManagedGroups; +using Disco.Services.Exporting; using Disco.Services.Interop.ActiveDirectory; using Disco.Services.Plugins; using Disco.Services.Plugins.Features.DocumentHandlerProvider; using Disco.Services.Tasks; using Disco.Services.Users; +using Disco.Services.Users.UserFlags; using Disco.Services.Web; using Disco.Web.Areas.API.Models.DocumentTemplate; +using Disco.Web.Areas.Config.Models.DocumentTemplate; +using Disco.Web.Extensions; using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; @@ -1444,5 +1448,58 @@ namespace Disco.Web.Areas.API.Controllers return Json(model); } #endregion + + #region Exporting + + [DiscoAuthorize(Claims.Config.DocumentTemplate.Export)] + [HttpPost, ValidateAntiForgeryToken] + public virtual ActionResult Export(ExportModel Model) + { + if (Model == null || Model.Options == null) + throw new ArgumentNullException(nameof(Model)); + + var templateId = default(string); + if (Model.Options.DocumentTemplateIds.Count == 1) + templateId = Model.Options.DocumentTemplateIds.First(); + + // Start Export + var exportContext = new DocumentExport(Model.Options); + var taskContext = ExportTask.ScheduleNowCacheResult(exportContext, id => Url.Action(MVC.Config.DocumentTemplate.Export(templateId, id))); + + // Try waiting for completion + if (taskContext.TaskStatus.WaitUntilFinished(TimeSpan.FromSeconds(1))) + return RedirectToAction(MVC.Config.DocumentTemplate.Export(templateId, taskContext.Id)); + else + return RedirectToAction(MVC.Config.Logging.TaskStatus(taskContext.TaskStatus.SessionId)); + } + + [DiscoAuthorize(Claims.Config.DocumentTemplate.Export)] + public virtual ActionResult ExportRetrieve(Guid id) + { + if (!ExportTask.TryFromCache(id, out var context)) + throw new ArgumentException("The export id specified is invalid, or the export data expired (60 minutes)", nameof(id)); + + if (context.Result == null || context.Result.Result == null) + throw new ArgumentException("The export session is still running, or failed to complete successfully", nameof(id)); + + if (context.Result.RecordCount == 0) + throw new ArgumentException("No records were found to export", nameof(id)); + + var fileStream = context.Result.Result; + + return this.File(fileStream.GetBuffer(), 0, (int)fileStream.Length, context.Result.MimeType, context.Result.Filename); + } + + [DiscoAuthorizeAll(Claims.Config.ManageSavedExports, Claims.Config.DocumentTemplate.Export)] + [HttpPost, ValidateAntiForgeryToken] + public virtual ActionResult SaveExport(ExportModel Model) + { + var export = new DocumentExport(Model.Options); + var savedExport = SavedExports.SaveExport(export, Database, CurrentUser); + + return RedirectToAction(MVC.Config.Export.Create(savedExport.Id)); + } + + #endregion } } diff --git a/Disco.Web/Areas/Config/ConfigAreaRegistration.cs b/Disco.Web/Areas/Config/ConfigAreaRegistration.cs index 43f626a6..0a41c322 100644 --- a/Disco.Web/Areas/Config/ConfigAreaRegistration.cs +++ b/Disco.Web/Areas/Config/ConfigAreaRegistration.cs @@ -64,6 +64,11 @@ namespace Disco.Web.Areas.Config "Config/DocumentTemplate/CreatePackage", new { controller = "DocumentTemplate", action = "CreatePackage" } ); + context.MapRoute( + "Config_DocumentTemplate_Export", + "Config/DocumentTemplate/Export/{id}", + new { controller = "DocumentTemplate", action = "Export", id = UrlParameter.Optional } + ); context.MapRoute( "Config_DocumentTemplate_ImportStatus", "Config/DocumentTemplate/ImportStatus", diff --git a/Disco.Web/Areas/Config/Controllers/DocumentTemplateController.cs b/Disco.Web/Areas/Config/Controllers/DocumentTemplateController.cs index ccc40376..b9c993d8 100644 --- a/Disco.Web/Areas/Config/Controllers/DocumentTemplateController.cs +++ b/Disco.Web/Areas/Config/Controllers/DocumentTemplateController.cs @@ -1,16 +1,19 @@ using Disco.BI.Extensions; using Disco.Data.Repository; +using Disco.Models.Areas.Config.UI.UserFlag; using Disco.Models.Repository; +using Disco.Models.Services.Documents; +using Disco.Models.Services.Exporting; using Disco.Models.UI.Config.DocumentTemplate; using Disco.Services; using Disco.Services.Authorization; using Disco.Services.Documents; using Disco.Services.Documents.ManagedGroups; -using Disco.Services.Expressions; -using Disco.Services.Plugins.Features.ExpressionExtensionProvider; +using Disco.Services.Exporting; using Disco.Services.Plugins.Features.UIExtension; using Disco.Services.Web; using Disco.Web.Areas.Config.Models.DocumentTemplate; +using Disco.Web.Models.Shared; using System; using System.Collections.Generic; using System.Linq; @@ -293,6 +296,60 @@ namespace Disco.Web.Areas.Config.Controllers return View(MVC.Config.DocumentTemplate.Views.BulkGenerate, model); } + [HttpGet] + [DiscoAuthorize(Claims.Config.DocumentTemplate.Export)] + public virtual ActionResult Export(string id, Guid? exportId) + { + var m = new ExportModel() + { + Options = DocumentExportOptions.DefaultOptions(), + DocumentTemplates = Database.DocumentTemplates.OrderBy(d => d.Id).ToList(), + }; + + m.Fields = ExportFieldsModel.Create(m.Options, nameof(DocumentExportOptions.LatestOnly)); + + var userCustomDetailKeys = Database.UserDetails.Where(d => d.Scope == "Details").Select(d => d.Key).Distinct().OrderBy(k => k).ToList(); + if (userCustomDetailKeys.Any()) + { + var group = new ExportOptionGroup("User Custom Details"); + foreach (var key in userCustomDetailKeys) + { + group.Add(new ExportOptionField() + { + GroupName = group.Name, + Name = key, + DisplayName = key.TrimEnd('*', '&'), + Description = $"{key} custom detail for the user associated with the document instance", + Checked = false, + Key = "UserDetailCustom", + Value = key, + }); + } + m.Fields.FieldGroups.Add(group); + } + + if (ExportTask.TryFromCache(exportId, out var context)) + { + m.ExportId = exportId; + m.ExportResult = context.Result; + } + + if (!string.IsNullOrWhiteSpace(id)) + { + var template = m.DocumentTemplates.FirstOrDefault(d => string.Equals(d.Id, id, StringComparison.OrdinalIgnoreCase)); + + if (template != null) + { + m.Options.DocumentTemplateIds.Add(template.Id); + } + } + + // UI Extensions + UIExtensions.ExecuteExtensions(ControllerContext, m); + + return View(m); + } + public virtual ActionResult ExpressionBrowser() { // for backwards compatibility diff --git a/Disco.Web/Areas/Config/Models/DocumentTemplate/ExportModel.cs b/Disco.Web/Areas/Config/Models/DocumentTemplate/ExportModel.cs new file mode 100644 index 00000000..b1c7e961 --- /dev/null +++ b/Disco.Web/Areas/Config/Models/DocumentTemplate/ExportModel.cs @@ -0,0 +1,20 @@ +using Disco.Models.Areas.Config.UI.UserFlag; +using Disco.Models.Services.Documents; +using Disco.Models.Services.Exporting; +using Disco.Models.UI.Shared; +using System; +using System.Collections.Generic; + +namespace Disco.Web.Areas.Config.Models.DocumentTemplate +{ + public class ExportModel : ConfigDocumentTemplateExportModel + { + public DocumentExportOptions Options { get; set; } + + public Guid? ExportId { get; set; } + public ExportResult ExportResult { get; set; } + + public List DocumentTemplates { get; set; } + public SharedExportFieldsModel Fields { get; set; } + } +} diff --git a/Disco.Web/Areas/Config/Views/DocumentTemplate/Export.cshtml b/Disco.Web/Areas/Config/Views/DocumentTemplate/Export.cshtml new file mode 100644 index 00000000..61fbcaf6 --- /dev/null +++ b/Disco.Web/Areas/Config/Views/DocumentTemplate/Export.cshtml @@ -0,0 +1,219 @@ +@using Disco.Web.Areas.Config.Models.DocumentTemplate; +@using Disco.Services.Exporting; +@model ExportModel +@{ + Authorization.Require(Claims.Config.DocumentTemplate.Export); + + ViewBag.Title = Html.ToBreadcrumb("Configuration", MVC.Config.Config.Index(), "Document Templates", MVC.Config.DocumentTemplate.Index(null), "Export"); +} +
+ @using (Html.BeginForm(MVC.API.DocumentTemplate.Export(), FormMethod.Post, new { @data_saveaction = Url.Action(MVC.API.DocumentTemplate.SaveExport()) })) + { + @Html.AntiForgeryToken() +
+

Export Scope

+ + + + + + + + + + + + + +
+ Documents: + + + @{ + var index = 0; + foreach (var document in Model.DocumentTemplates) + { + + + + + + index++; + } + } +
+ + + + + @document.Scope +
+
@Html.LabelFor(m => m.Options.LatestOnly) + @Html.CheckBoxFor(m => m.Options.LatestOnly) +

Uncheck to include all document instances.

+
@Html.LabelFor(m => m.Options.Format) + @Html.DropDownListFor(m => m.Options.Format, Enum.GetNames(typeof(Disco.Models.Exporting.ExportFormat)).Select(v => new SelectListItem() { Value = v, Text = v })) +
+
+
+

Export Fields (Defaults)

+ + @foreach (var optionGroup in Model.Fields.FieldGroups) + { + var optionFields = optionGroup.ToList(); + var itemsPerColumn = (int)Math.Ceiling((double)optionFields.Count / 2); + + + + + + } +
+ @optionGroup.Name + @if (optionFields.Count > 2) + { + ALL | NONE + } + +
+ + + + + +
+
    + @foreach (var optionItem in optionFields.Take(itemsPerColumn)) + { +
  • + +
  • + } +
+
+
    + @foreach (var optionItem in optionFields.Skip(itemsPerColumn)) + { +
  • + +
  • + } +
+
+
+
+
+ + } +
+@if (Model.ExportId.HasValue) +{ +
+ @if (Model.ExportResult.RecordCount == 0) + { +

No records matched the filter criteria

+ } + else + { +

@Model.ExportResult.RecordCount record@(Model.ExportResult.RecordCount != 1 ? "s" : null) were successfully exported.

+ Download Document Instance Export + } +
+ +} +
+

Exporting document instances...

+
+
+ @if (Authorization.Has(Claims.Config.ManageSavedExports)) + { + + } + else + { + + } + + +
diff --git a/Disco.Web/Areas/Config/Views/DocumentTemplate/Export.generated.cs b/Disco.Web/Areas/Config/Views/DocumentTemplate/Export.generated.cs new file mode 100644 index 00000000..4e6309d2 --- /dev/null +++ b/Disco.Web/Areas/Config/Views/DocumentTemplate/Export.generated.cs @@ -0,0 +1,871 @@ +#pragma warning disable 1591 +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Disco.Web.Areas.Config.Views.DocumentTemplate +{ + using System; + using System.Collections.Generic; + using System.IO; + using System.Linq; + using System.Net; + using System.Text; + using System.Web; + using System.Web.Helpers; + using System.Web.Mvc; + using System.Web.Mvc.Ajax; + using System.Web.Mvc.Html; + using System.Web.Routing; + using System.Web.Security; + using System.Web.UI; + using System.Web.WebPages; + using Disco; + using Disco.Models.Repository; + using Disco.Services; + using Disco.Services.Authorization; + + #line 2 "..\..\Areas\Config\Views\DocumentTemplate\Export.cshtml" + using Disco.Services.Exporting; + + #line default + #line hidden + using Disco.Services.Web; + using Disco.Web; + + #line 1 "..\..\Areas\Config\Views\DocumentTemplate\Export.cshtml" + using Disco.Web.Areas.Config.Models.DocumentTemplate; + + #line default + #line hidden + using Disco.Web.Extensions; + + [System.CodeDom.Compiler.GeneratedCodeAttribute("RazorGenerator", "2.0.0.0")] + [System.Web.WebPages.PageVirtualPathAttribute("~/Areas/Config/Views/DocumentTemplate/Export.cshtml")] + public partial class Export : Disco.Services.Web.WebViewPage + { + public Export() + { + } + public override void Execute() + { + + #line 4 "..\..\Areas\Config\Views\DocumentTemplate\Export.cshtml" + + Authorization.Require(Claims.Config.DocumentTemplate.Export); + + ViewBag.Title = Html.ToBreadcrumb("Configuration", MVC.Config.Config.Index(), "Document Templates", MVC.Config.DocumentTemplate.Index(null), "Export"); + + + #line default + #line hidden +WriteLiteral("\r\n\r\n"); + + + #line 10 "..\..\Areas\Config\Views\DocumentTemplate\Export.cshtml" + + + #line default + #line hidden + + #line 10 "..\..\Areas\Config\Views\DocumentTemplate\Export.cshtml" + using (Html.BeginForm(MVC.API.DocumentTemplate.Export(), FormMethod.Post, new { @data_saveaction = Url.Action(MVC.API.DocumentTemplate.SaveExport()) })) + { + + + #line default + #line hidden + + #line 12 "..\..\Areas\Config\Views\DocumentTemplate\Export.cshtml" + Write(Html.AntiForgeryToken()); + + + #line default + #line hidden + + #line 12 "..\..\Areas\Config\Views\DocumentTemplate\Export.cshtml" + + + + #line default + #line hidden +WriteLiteral(" \r\n

Export Scope

\r\n \r\n \r\n" + +" \r\n Documents:\r\n \r\n " + +" \r\n \r\n"); + + + #line 22 "..\..\Areas\Config\Views\DocumentTemplate\Export.cshtml" + + + #line default + #line hidden + + #line 22 "..\..\Areas\Config\Views\DocumentTemplate\Export.cshtml" + + var index = 0; + foreach (var document in Model.DocumentTemplates) + { + + + #line default + #line hidden +WriteLiteral(" \r\n " + +" \r\n \r\n " + +" \r\n " + +" \r\n"); + + + #line 40 "..\..\Areas\Config\Views\DocumentTemplate\Export.cshtml" + index++; + } + + + #line default + #line hidden +WriteLiteral("\r\n
\r\n \r\n " + +" \r\n " + +" (index + + #line default + #line hidden +, 1851), false) +); + +WriteLiteral(">"); + + + #line 34 "..\..\Areas\Config\Views\DocumentTemplate\Export.cshtml" + Write(document.Description); + + + #line default + #line hidden +WriteLiteral("\r\n \r\n"); + +WriteLiteral(" "); + + + #line 37 "..\..\Areas\Config\Views\DocumentTemplate\Export.cshtml" + Write(document.Scope); + + + #line default + #line hidden +WriteLiteral("\r\n
\r\n \r\n \r\n \r\n "); + + + #line 47 "..\..\Areas\Config\Views\DocumentTemplate\Export.cshtml" + Write(Html.LabelFor(m => m.Options.LatestOnly)); + + + #line default + #line hidden +WriteLiteral("\r\n \r\n"); + +WriteLiteral(" "); + + + #line 49 "..\..\Areas\Config\Views\DocumentTemplate\Export.cshtml" + Write(Html.CheckBoxFor(m => m.Options.LatestOnly)); + + + #line default + #line hidden +WriteLiteral("\r\n

Uncheck to include all document instances.

\r\n " + +" \r\n \r\n \r\n " + +" "); + + + #line 54 "..\..\Areas\Config\Views\DocumentTemplate\Export.cshtml" + Write(Html.LabelFor(m => m.Options.Format)); + + + #line default + #line hidden +WriteLiteral("\r\n \r\n"); + +WriteLiteral(" "); + + + #line 56 "..\..\Areas\Config\Views\DocumentTemplate\Export.cshtml" + Write(Html.DropDownListFor(m => m.Options.Format, Enum.GetNames(typeof(Disco.Models.Exporting.ExportFormat)).Select(v => new SelectListItem() { Value = v, Text = v }))); + + + #line default + #line hidden +WriteLiteral("\r\n \r\n \r\n \r\n " + +" \r\n"); + +WriteLiteral(" \r\n

Export Fields (Defaults)

\r\n \r\n"); + + + #line 64 "..\..\Areas\Config\Views\DocumentTemplate\Export.cshtml" + + + #line default + #line hidden + + #line 64 "..\..\Areas\Config\Views\DocumentTemplate\Export.cshtml" + foreach (var optionGroup in Model.Fields.FieldGroups) + { + var optionFields = optionGroup.ToList(); + var itemsPerColumn = (int)Math.Ceiling((double)optionFields.Count / 2); + + + #line default + #line hidden +WriteLiteral(" \r\n \r\n"); + +WriteLiteral(" "); + + + #line 70 "..\..\Areas\Config\Views\DocumentTemplate\Export.cshtml" + Write(optionGroup.Name); + + + #line default + #line hidden +WriteLiteral("\r\n"); + + + #line 71 "..\..\Areas\Config\Views\DocumentTemplate\Export.cshtml" + + + #line default + #line hidden + + #line 71 "..\..\Areas\Config\Views\DocumentTemplate\Export.cshtml" + if (optionFields.Count > 2) + { + + + #line default + #line hidden +WriteLiteral(" ALL | NONE\r\n"); + + + #line 74 "..\..\Areas\Config\Views\DocumentTemplate\Export.cshtml" + } + + + #line default + #line hidden +WriteLiteral(" \r\n \r\n " + +" \r\n \r\n"); + + + #line 82 "..\..\Areas\Config\Views\DocumentTemplate\Export.cshtml" + + + #line default + #line hidden + + #line 82 "..\..\Areas\Config\Views\DocumentTemplate\Export.cshtml" + foreach (var optionItem in optionFields.Take(itemsPerColumn)) + { + + + #line default + #line hidden +WriteLiteral(" (optionItem.Description + + #line default + #line hidden +, 4517), false) +); + +WriteLiteral(">\r\n (optionItem.Name + + #line default + #line hidden +, 4635), false) +); + +WriteAttribute("name", Tuple.Create(" name=\"", 4652), Tuple.Create("\"", 4703) +, Tuple.Create(Tuple.Create("", 4659), Tuple.Create("Options.", 4659), true) + + #line 85 "..\..\Areas\Config\Views\DocumentTemplate\Export.cshtml" + , Tuple.Create(Tuple.Create("", 4667), Tuple.Create(optionItem.Key ?? optionItem.Name + + #line default + #line hidden +, 4667), false) +); + +WriteAttribute("value", Tuple.Create(" value=\"", 4704), Tuple.Create("\"", 4741) + + #line 85 "..\..\Areas\Config\Views\DocumentTemplate\Export.cshtml" + , Tuple.Create(Tuple.Create("", 4712), Tuple.Create(optionItem.Value ?? "true" + + #line default + #line hidden +, 4712), false) +); + +WriteLiteral(" "); + + + #line 85 "..\..\Areas\Config\Views\DocumentTemplate\Export.cshtml" + Write((optionItem.Checked) ? "checked " : null); + + + #line default + #line hidden +WriteLiteral(" />(optionItem.Name + + #line default + #line hidden +, 4809), false) +); + +WriteLiteral(">"); + + + #line 85 "..\..\Areas\Config\Views\DocumentTemplate\Export.cshtml" + Write(optionItem.DisplayName); + + + #line default + #line hidden +WriteLiteral("\r\n \r\n"); + + + #line 87 "..\..\Areas\Config\Views\DocumentTemplate\Export.cshtml" + } + + + #line default + #line hidden +WriteLiteral(" \r\n " + +" \r\n \r\n \r\n"); + + + #line 92 "..\..\Areas\Config\Views\DocumentTemplate\Export.cshtml" + + + #line default + #line hidden + + #line 92 "..\..\Areas\Config\Views\DocumentTemplate\Export.cshtml" + foreach (var optionItem in optionFields.Skip(itemsPerColumn)) + { + + + #line default + #line hidden +WriteLiteral(" (optionItem.Description + + #line default + #line hidden +, 5422), false) +); + +WriteLiteral(">\r\n (optionItem.Name + + #line default + #line hidden +, 5540), false) +); + +WriteAttribute("name", Tuple.Create(" name=\"", 5557), Tuple.Create("\"", 5608) +, Tuple.Create(Tuple.Create("", 5564), Tuple.Create("Options.", 5564), true) + + #line 95 "..\..\Areas\Config\Views\DocumentTemplate\Export.cshtml" + , Tuple.Create(Tuple.Create("", 5572), Tuple.Create(optionItem.Key ?? optionItem.Name + + #line default + #line hidden +, 5572), false) +); + +WriteAttribute("value", Tuple.Create(" value=\"", 5609), Tuple.Create("\"", 5646) + + #line 95 "..\..\Areas\Config\Views\DocumentTemplate\Export.cshtml" + , Tuple.Create(Tuple.Create("", 5617), Tuple.Create(optionItem.Value ?? "true" + + #line default + #line hidden +, 5617), false) +); + +WriteLiteral(" "); + + + #line 95 "..\..\Areas\Config\Views\DocumentTemplate\Export.cshtml" + Write((optionItem.Checked) ? "checked " : null); + + + #line default + #line hidden +WriteLiteral(" />(optionItem.Name + + #line default + #line hidden +, 5714), false) +); + +WriteLiteral(">"); + + + #line 95 "..\..\Areas\Config\Views\DocumentTemplate\Export.cshtml" + Write(optionItem.DisplayName); + + + #line default + #line hidden +WriteLiteral("\r\n \r\n"); + + + #line 97 "..\..\Areas\Config\Views\DocumentTemplate\Export.cshtml" + } + + + #line default + #line hidden +WriteLiteral(@" + + +
\r\n " + +" \r\n \r\n
+ + + +"); + + + #line 105 "..\..\Areas\Config\Views\DocumentTemplate\Export.cshtml" + + } + + + #line default + #line hidden +WriteLiteral(" \r\n \r\n"); + +WriteLiteral(" \r\n"); + + + #line 177 "..\..\Areas\Config\Views\DocumentTemplate\Export.cshtml" + } + + + #line default + #line hidden +WriteLiteral("\r\n"); + + + #line 179 "..\..\Areas\Config\Views\DocumentTemplate\Export.cshtml" + if (Model.ExportId.HasValue) +{ + + + #line default + #line hidden +WriteLiteral(" \r\n"); + + + #line 182 "..\..\Areas\Config\Views\DocumentTemplate\Export.cshtml" + + + #line default + #line hidden + + #line 182 "..\..\Areas\Config\Views\DocumentTemplate\Export.cshtml" + if (Model.ExportResult.RecordCount == 0) + { + + + #line default + #line hidden +WriteLiteral("

No records matched the filter criteria

\r\n"); + + + #line 185 "..\..\Areas\Config\Views\DocumentTemplate\Export.cshtml" + } + else + { + + + #line default + #line hidden +WriteLiteral("

"); + + + #line 188 "..\..\Areas\Config\Views\DocumentTemplate\Export.cshtml" + Write(Model.ExportResult.RecordCount); + + + #line default + #line hidden +WriteLiteral(" record"); + + + #line 188 "..\..\Areas\Config\Views\DocumentTemplate\Export.cshtml" + Write(Model.ExportResult.RecordCount != 1 ? "s" : null); + + + #line default + #line hidden +WriteLiteral(" were successfully exported.

\r\n"); + +WriteLiteral(" (Url.Action(MVC.API.DocumentTemplate.ExportRetrieve(Model.ExportId.Value)) + + #line default + #line hidden +, 9612), false) +); + +WriteLiteral(" class=\"button\""); + +WriteLiteral(">Download Document Instance Export\r\n"); + + + #line 190 "..\..\Areas\Config\Views\DocumentTemplate\Export.cshtml" + } + + + #line default + #line hidden +WriteLiteral(" \r\n"); + +WriteLiteral(@" +"); + + + #line 204 "..\..\Areas\Config\Views\DocumentTemplate\Export.cshtml" +} + + + #line default + #line hidden +WriteLiteral("\r\n

Exporting document instances...

\r\n\r\n\r\n"); + + + #line 209 "..\..\Areas\Config\Views\DocumentTemplate\Export.cshtml" + + + #line default + #line hidden + + #line 209 "..\..\Areas\Config\Views\DocumentTemplate\Export.cshtml" + if (Authorization.Has(Claims.Config.ManageSavedExports)) + { + + + #line default + #line hidden +WriteLiteral(" Save Export\r\n"); + + + #line 212 "..\..\Areas\Config\Views\DocumentTemplate\Export.cshtml" + } + else + { + + + #line default + #line hidden +WriteLiteral(" Save Export\r\n"); + + + #line 216 "..\..\Areas\Config\Views\DocumentTemplate\Export.cshtml" + } + + + #line default + #line hidden +WriteLiteral("\r\n Export Now\r\n\r\n"); + + } + } +} +#pragma warning restore 1591 diff --git a/Disco.Web/Areas/Config/Views/DocumentTemplate/Index.cshtml b/Disco.Web/Areas/Config/Views/DocumentTemplate/Index.cshtml index 5fac049e..b52859a4 100644 --- a/Disco.Web/Areas/Config/Views/DocumentTemplate/Index.cshtml +++ b/Disco.Web/Areas/Config/Views/DocumentTemplate/Index.cshtml @@ -120,6 +120,10 @@ else { @Html.ActionLinkButton("Expression Browser", MVC.Config.Expressions.Browser()) } + @if (Authorization.Has(Claims.Config.DocumentTemplate.Export)) + { + @Html.ActionLinkButton("Export Instances", MVC.Config.DocumentTemplate.Export()) + } @if (Model.DocumentTemplates.Count > 2 && Authorization.HasAll(Claims.Config.DocumentTemplate.Create, Claims.Config.DocumentTemplate.Configure)) { @Html.ActionLinkButton("Create Package", MVC.Config.DocumentTemplate.CreatePackage()) diff --git a/Disco.Web/Areas/Config/Views/DocumentTemplate/Index.generated.cs b/Disco.Web/Areas/Config/Views/DocumentTemplate/Index.generated.cs index e79f784b..625c865c 100644 --- a/Disco.Web/Areas/Config/Views/DocumentTemplate/Index.generated.cs +++ b/Disco.Web/Areas/Config/Views/DocumentTemplate/Index.generated.cs @@ -629,7 +629,7 @@ WriteLiteral(" "); #line 123 "..\..\Areas\Config\Views\DocumentTemplate\Index.cshtml" - if (Model.DocumentTemplates.Count > 2 && Authorization.HasAll(Claims.Config.DocumentTemplate.Create, Claims.Config.DocumentTemplate.Configure)) + if (Authorization.Has(Claims.Config.DocumentTemplate.Export)) { @@ -637,14 +637,14 @@ WriteLiteral(" "); #line hidden #line 125 "..\..\Areas\Config\Views\DocumentTemplate\Index.cshtml" - Write(Html.ActionLinkButton("Create Package", MVC.Config.DocumentTemplate.CreatePackage())); + Write(Html.ActionLinkButton("Export Instances", MVC.Config.DocumentTemplate.Export())); #line default #line hidden #line 125 "..\..\Areas\Config\Views\DocumentTemplate\Index.cshtml" - + } @@ -654,7 +654,7 @@ WriteLiteral(" "); #line 127 "..\..\Areas\Config\Views\DocumentTemplate\Index.cshtml" - if (Authorization.HasAll(Claims.Config.DocumentTemplate.Create, Claims.Config.DocumentTemplate.Configure)) + if (Model.DocumentTemplates.Count > 2 && Authorization.HasAll(Claims.Config.DocumentTemplate.Create, Claims.Config.DocumentTemplate.Configure)) { @@ -662,13 +662,38 @@ WriteLiteral(" "); #line hidden #line 129 "..\..\Areas\Config\Views\DocumentTemplate\Index.cshtml" - Write(Html.ActionLinkButton("Create Document Template", MVC.Config.DocumentTemplate.Create())); + Write(Html.ActionLinkButton("Create Package", MVC.Config.DocumentTemplate.CreatePackage())); #line default #line hidden #line 129 "..\..\Areas\Config\Views\DocumentTemplate\Index.cshtml" + + } + + + #line default + #line hidden +WriteLiteral(" "); + + + #line 131 "..\..\Areas\Config\Views\DocumentTemplate\Index.cshtml" + if (Authorization.HasAll(Claims.Config.DocumentTemplate.Create, Claims.Config.DocumentTemplate.Configure)) + { + + + #line default + #line hidden + + #line 133 "..\..\Areas\Config\Views\DocumentTemplate\Index.cshtml" + Write(Html.ActionLinkButton("Create Document Template", MVC.Config.DocumentTemplate.Create())); + + + #line default + #line hidden + + #line 133 "..\..\Areas\Config\Views\DocumentTemplate\Index.cshtml" } diff --git a/Disco.Web/Areas/Config/Views/DocumentTemplate/Show.cshtml b/Disco.Web/Areas/Config/Views/DocumentTemplate/Show.cshtml index 6699557a..79b49c7b 100644 --- a/Disco.Web/Areas/Config/Views/DocumentTemplate/Show.cshtml +++ b/Disco.Web/Areas/Config/Views/DocumentTemplate/Show.cshtml @@ -1052,6 +1052,10 @@ { @Html.ActionLinkButton("Expression Browser", MVC.Config.Expressions.Browser()) } + @if (Authorization.Has(Claims.Config.DocumentTemplate.Export)) + { + @Html.ActionLinkButton("Export Instances", MVC.Config.DocumentTemplate.Export(Model.DocumentTemplate.Id, null)) + } @if (canBulkGenerate) { if (Model.DocumentTemplate.Scope == DocumentTemplate.DocumentTemplateScopes.User) diff --git a/Disco.Web/Areas/Config/Views/DocumentTemplate/Show.generated.cs b/Disco.Web/Areas/Config/Views/DocumentTemplate/Show.generated.cs index c5f1c6b9..828575bf 100644 --- a/Disco.Web/Areas/Config/Views/DocumentTemplate/Show.generated.cs +++ b/Disco.Web/Areas/Config/Views/DocumentTemplate/Show.generated.cs @@ -3198,6 +3198,31 @@ WriteLiteral(" "); #line 1055 "..\..\Areas\Config\Views\DocumentTemplate\Show.cshtml" + if (Authorization.Has(Claims.Config.DocumentTemplate.Export)) + { + + + #line default + #line hidden + + #line 1057 "..\..\Areas\Config\Views\DocumentTemplate\Show.cshtml" + Write(Html.ActionLinkButton("Export Instances", MVC.Config.DocumentTemplate.Export(Model.DocumentTemplate.Id, null))); + + + #line default + #line hidden + + #line 1057 "..\..\Areas\Config\Views\DocumentTemplate\Show.cshtml" + + } + + + #line default + #line hidden +WriteLiteral(" "); + + + #line 1059 "..\..\Areas\Config\Views\DocumentTemplate\Show.cshtml" if (canBulkGenerate) { if (Model.DocumentTemplate.Scope == DocumentTemplate.DocumentTemplateScopes.User) @@ -3207,14 +3232,14 @@ WriteLiteral(" "); #line default #line hidden - #line 1059 "..\..\Areas\Config\Views\DocumentTemplate\Show.cshtml" + #line 1063 "..\..\Areas\Config\Views\DocumentTemplate\Show.cshtml" Write(Html.ActionLinkButton("Bulk Generate", MVC.Config.DocumentTemplate.BulkGenerate(Model.DocumentTemplate.Id))); #line default #line hidden - #line 1059 "..\..\Areas\Config\Views\DocumentTemplate\Show.cshtml" + #line 1063 "..\..\Areas\Config\Views\DocumentTemplate\Show.cshtml" } else @@ -3239,16 +3264,16 @@ WriteLiteral(" id=\"dialogBulkGenerate\""); WriteLiteral(" class=\"hiddenDialog dialog-bulk-generate\""); -WriteAttribute("title", Tuple.Create(" title=\"", 62552), Tuple.Create("\"", 62603) -, Tuple.Create(Tuple.Create("", 62560), Tuple.Create("Bulk", 62560), true) -, Tuple.Create(Tuple.Create(" ", 62564), Tuple.Create("Generate:", 62565), true) +WriteAttribute("title", Tuple.Create(" title=\"", 62787), Tuple.Create("\"", 62838) +, Tuple.Create(Tuple.Create("", 62795), Tuple.Create("Bulk", 62795), true) +, Tuple.Create(Tuple.Create(" ", 62799), Tuple.Create("Generate:", 62800), true) - #line 1064 "..\..\Areas\Config\Views\DocumentTemplate\Show.cshtml" - , Tuple.Create(Tuple.Create(" ", 62574), Tuple.Create(Model.DocumentTemplate.Id + #line 1068 "..\..\Areas\Config\Views\DocumentTemplate\Show.cshtml" + , Tuple.Create(Tuple.Create(" ", 62809), Tuple.Create(Model.DocumentTemplate.Id #line default #line hidden -, 62575), false) +, 62810), false) ); WriteLiteral(">\r\n \r\n"); - #line 1066 "..\..\Areas\Config\Views\DocumentTemplate\Show.cshtml" + #line 1070 "..\..\Areas\Config\Views\DocumentTemplate\Show.cshtml" #line default #line hidden - #line 1066 "..\..\Areas\Config\Views\DocumentTemplate\Show.cshtml" + #line 1070 "..\..\Areas\Config\Views\DocumentTemplate\Show.cshtml" switch (Model.DocumentTemplate.Scope) { case "Device": @@ -3306,7 +3331,7 @@ WriteLiteral(" class=\"example3 code\""); WriteLiteral(">01234567;ABCD9876;8VQ6G2R\r\n \r\n"); - #line 1082 "..\..\Areas\Config\Views\DocumentTemplate\Show.cshtml" + #line 1086 "..\..\Areas\Config\Views\DocumentTemplate\Show.cshtml" break; case "Job": @@ -3345,7 +3370,7 @@ WriteLiteral(" class=\"example3 code\""); WriteLiteral(">86;99;44\r\n \r\n"); - #line 1097 "..\..\Areas\Config\Views\DocumentTemplate\Show.cshtml" + #line 1101 "..\..\Areas\Config\Views\DocumentTemplate\Show.cshtml" break; } @@ -3355,13 +3380,13 @@ WriteLiteral(">86;99;44\r\n \r\n" WriteLiteral(" \r\n"); - #line 1100 "..\..\Areas\Config\Views\DocumentTemplate\Show.cshtml" + #line 1104 "..\..\Areas\Config\Views\DocumentTemplate\Show.cshtml" #line default #line hidden - #line 1100 "..\..\Areas\Config\Views\DocumentTemplate\Show.cshtml" + #line 1104 "..\..\Areas\Config\Views\DocumentTemplate\Show.cshtml" using (Html.BeginForm(MVC.API.DocumentTemplate.BulkGenerate(Model.DocumentTemplate.Id), FormMethod.Post)) { @@ -3391,7 +3416,7 @@ WriteLiteral(" data-val-required=\"Identifiers are required\""); WriteLiteral(">\r\n"); - #line 1104 "..\..\Areas\Config\Views\DocumentTemplate\Show.cshtml" + #line 1108 "..\..\Areas\Config\Views\DocumentTemplate\Show.cshtml" if (Model.TemplatePageCount > 1 && Model.TemplatePageCount % 2 != 0) { @@ -3420,7 +3445,7 @@ WriteLiteral(">Insert Blank Pages for Double-Sided Printing\r\n " \r\n"); - #line 1109 "..\..\Areas\Config\Views\DocumentTemplate\Show.cshtml" + #line 1113 "..\..\Areas\Config\Views\DocumentTemplate\Show.cshtml" } } @@ -3430,7 +3455,7 @@ WriteLiteral(">Insert Blank Pages for Double-Sided Printing\r\n WriteLiteral(" \r\n"); - #line 1112 "..\..\Areas\Config\Views\DocumentTemplate\Show.cshtml" + #line 1116 "..\..\Areas\Config\Views\DocumentTemplate\Show.cshtml" @@ -3469,7 +3494,7 @@ WriteLiteral(@"