diff --git a/Disco.Data/Migrations/202102110443550_DBv21.Designer.cs b/Disco.Data/Migrations/202102110443550_DBv21.Designer.cs new file mode 100644 index 00000000..e1259568 --- /dev/null +++ b/Disco.Data/Migrations/202102110443550_DBv21.Designer.cs @@ -0,0 +1,27 @@ +// +namespace Disco.Data.Migrations +{ + using System.Data.Entity.Migrations; + using System.Data.Entity.Migrations.Infrastructure; + using System.Resources; + + public sealed partial class DBv21 : IMigrationMetadata + { + private readonly ResourceManager Resources = new ResourceManager(typeof(DBv21)); + + string IMigrationMetadata.Id + { + get { return "202102110443550_DBv21"; } + } + + string IMigrationMetadata.Source + { + get { return null; } + } + + string IMigrationMetadata.Target + { + get { return Resources.GetString("Target"); } + } + } +} diff --git a/Disco.Data/Migrations/202102110443550_DBv21.cs b/Disco.Data/Migrations/202102110443550_DBv21.cs new file mode 100644 index 00000000..052565d2 --- /dev/null +++ b/Disco.Data/Migrations/202102110443550_DBv21.cs @@ -0,0 +1,34 @@ +namespace Disco.Data.Migrations +{ + using System; + using System.Data.Entity.Migrations; + + public partial class DBv21 : DbMigration + { + public override void Up() + { + AddColumn("dbo.UserAttachments", "HandlerId", c => c.String(maxLength: 30)); + AddColumn("dbo.UserAttachments", "HandlerReferenceId", c => c.String(maxLength: 50)); + AddColumn("dbo.UserAttachments", "HandlerData", c => c.String()); + AddColumn("dbo.JobAttachments", "HandlerId", c => c.String(maxLength: 30)); + AddColumn("dbo.JobAttachments", "HandlerReferenceId", c => c.String(maxLength: 50)); + AddColumn("dbo.JobAttachments", "HandlerData", c => c.String()); + AddColumn("dbo.DeviceAttachments", "HandlerId", c => c.String(maxLength: 30)); + AddColumn("dbo.DeviceAttachments", "HandlerReferenceId", c => c.String(maxLength: 50)); + AddColumn("dbo.DeviceAttachments", "HandlerData", c => c.String()); + } + + public override void Down() + { + DropColumn("dbo.DeviceAttachments", "HandlerData"); + DropColumn("dbo.DeviceAttachments", "HandlerReferenceId"); + DropColumn("dbo.DeviceAttachments", "HandlerId"); + DropColumn("dbo.JobAttachments", "HandlerData"); + DropColumn("dbo.JobAttachments", "HandlerReferenceId"); + DropColumn("dbo.JobAttachments", "HandlerId"); + DropColumn("dbo.UserAttachments", "HandlerData"); + DropColumn("dbo.UserAttachments", "HandlerReferenceId"); + DropColumn("dbo.UserAttachments", "HandlerId"); + } + } +} diff --git a/Disco.Data/Migrations/202102110443550_DBv21.resx b/Disco.Data/Migrations/202102110443550_DBv21.resx new file mode 100644 index 00000000..81b8e6d4 --- /dev/null +++ b/Disco.Data/Migrations/202102110443550_DBv21.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 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 + + \ No newline at end of file diff --git a/Disco.Models/Repository/Attachment/IAttachment.cs b/Disco.Models/Repository/Attachment/IAttachment.cs index 3684df7e..7d6e19bd 100644 --- a/Disco.Models/Repository/Attachment/IAttachment.cs +++ b/Disco.Models/Repository/Attachment/IAttachment.cs @@ -20,5 +20,9 @@ namespace Disco.Models.Repository string DocumentTemplateId { get; set; } AttachmentTypes AttachmentType { get; } + + string HandlerId { get; set; } + string HandlerReferenceId { get; set; } + string HandlerData { get; set; } } } diff --git a/Disco.Models/Repository/Device/DeviceAttachment.cs b/Disco.Models/Repository/Device/DeviceAttachment.cs index 2cd5ff79..feede63e 100644 --- a/Disco.Models/Repository/Device/DeviceAttachment.cs +++ b/Disco.Models/Repository/Device/DeviceAttachment.cs @@ -21,6 +21,12 @@ namespace Disco.Models.Repository public string DocumentTemplateId { get; set; } + [StringLength(30)] + public string HandlerId { get; set; } + [StringLength(50)] + public string HandlerReferenceId { get; set; } + public string HandlerData { get; set; } + [NotMapped] public object Reference { get { return DeviceSerialNumber; } } diff --git a/Disco.Models/Repository/Device/DeviceBatchAttachment.cs b/Disco.Models/Repository/Device/DeviceBatchAttachment.cs index 9fa3143f..ae251666 100644 --- a/Disco.Models/Repository/Device/DeviceBatchAttachment.cs +++ b/Disco.Models/Repository/Device/DeviceBatchAttachment.cs @@ -18,7 +18,14 @@ namespace Disco.Models.Repository public DateTime Timestamp { get; set; } [Required, StringLength(500)] public string Comments { get; set; } - + + [NotMapped] + public string HandlerId { get => null; set { } } + [NotMapped] + public string HandlerReferenceId { get => null; set { } } + [NotMapped] + public string HandlerData { get => null; set { } } + [NotMapped] public object Reference => DeviceBatchId; diff --git a/Disco.Models/Repository/Job/JobAttachment.cs b/Disco.Models/Repository/Job/JobAttachment.cs index e81ab2b1..6445dac6 100644 --- a/Disco.Models/Repository/Job/JobAttachment.cs +++ b/Disco.Models/Repository/Job/JobAttachment.cs @@ -22,6 +22,12 @@ namespace Disco.Models.Repository public string DocumentTemplateId { get; set; } + [StringLength(30)] + public string HandlerId { get; set; } + [StringLength(50)] + public string HandlerReferenceId { get; set; } + public string HandlerData { get; set; } + [NotMapped] public object Reference { get { return JobId; } } diff --git a/Disco.Models/Repository/User/UserAttachment.cs b/Disco.Models/Repository/User/UserAttachment.cs index f2f8f7af..6c4b9584 100644 --- a/Disco.Models/Repository/User/UserAttachment.cs +++ b/Disco.Models/Repository/User/UserAttachment.cs @@ -21,6 +21,12 @@ namespace Disco.Models.Repository public string DocumentTemplateId { get; set; } + [StringLength(30)] + public string HandlerId { get; set; } + [StringLength(50)] + public string HandlerReferenceId { get; set; } + public string HandlerData { get; set; } + [NotMapped] public object Reference { get { return UserId; } } diff --git a/Disco.Services/Attachments/AttachmentActionExtensions.cs b/Disco.Services/Attachments/AttachmentActionExtensions.cs index b3f6b322..b67d5cb5 100644 --- a/Disco.Services/Attachments/AttachmentActionExtensions.cs +++ b/Disco.Services/Attachments/AttachmentActionExtensions.cs @@ -105,6 +105,9 @@ namespace Disco.Services #endregion public static DeviceAttachment CreateAttachment(this Device Device, DiscoDataContext Database, User CreatorUser, string Filename, string MimeType, string Comments, Stream Content, DocumentTemplate DocumentTemplate = null, Image PdfThumbnail = null) + => Device.CreateAttachment(Database, CreatorUser, Filename, DateTime.Now, MimeType, Comments, Content, DocumentTemplate, PdfThumbnail); + + public static DeviceAttachment CreateAttachment(this Device Device, DiscoDataContext Database, User CreatorUser, string Filename, DateTime timestamp, string MimeType, string Comments, Stream Content, DocumentTemplate DocumentTemplate = null, Image PdfThumbnail = null, string HandlerId = null, string HandlerReferenceId = null, string HandlerData = null) { if (string.IsNullOrEmpty(MimeType) || MimeType.Equals("unknown/unknown", StringComparison.OrdinalIgnoreCase)) MimeType = Interop.MimeTypes.ResolveMimeType(Filename); @@ -115,8 +118,11 @@ namespace Disco.Services TechUserId = CreatorUser.UserId, Filename = Filename, MimeType = MimeType, - Timestamp = DateTime.Now, - Comments = Comments + Timestamp = timestamp, + Comments = Comments, + HandlerId = HandlerId, + HandlerReferenceId = HandlerReferenceId, + HandlerData = HandlerData, }; if (DocumentTemplate != null) @@ -136,6 +142,9 @@ namespace Disco.Services } public static JobAttachment CreateAttachment(this Job Job, DiscoDataContext Database, User CreatorUser, string Filename, string MimeType, string Comments, Stream Content, DocumentTemplate DocumentTemplate = null, Image PdfThumbnail = null) + => Job.CreateAttachment(Database, CreatorUser, Filename, DateTime.Now, MimeType, Comments, Content, DocumentTemplate, PdfThumbnail); + + public static JobAttachment CreateAttachment(this Job Job, DiscoDataContext Database, User CreatorUser, string Filename, DateTime Timestamp, string MimeType, string Comments, Stream Content, DocumentTemplate DocumentTemplate = null, Image PdfThumbnail = null, string HandlerId = null, string HandlerReferenceId = null, string HandlerData = null) { if (string.IsNullOrEmpty(MimeType) || MimeType.Equals("unknown/unknown", StringComparison.OrdinalIgnoreCase)) MimeType = Interop.MimeTypes.ResolveMimeType(Filename); @@ -146,8 +155,11 @@ namespace Disco.Services TechUserId = CreatorUser.UserId, Filename = Filename, MimeType = MimeType, - Timestamp = DateTime.Now, - Comments = Comments + Timestamp = Timestamp, + Comments = Comments, + HandlerId = HandlerId, + HandlerReferenceId = HandlerReferenceId, + HandlerData = HandlerData, }; if (DocumentTemplate != null) @@ -167,6 +179,9 @@ namespace Disco.Services } public static UserAttachment CreateAttachment(this User User, DiscoDataContext Database, User CreatorUser, string Filename, string MimeType, string Comments, Stream Content, DocumentTemplate DocumentTemplate = null, Image PdfThumbnail = null) + => User.CreateAttachment(Database, CreatorUser, Filename, DateTime.Now, MimeType, Comments, Content, DocumentTemplate, PdfThumbnail); + + public static UserAttachment CreateAttachment(this User User, DiscoDataContext Database, User CreatorUser, string Filename, DateTime Timestamp, string MimeType, string Comments, Stream Content, DocumentTemplate DocumentTemplate = null, Image PdfThumbnail = null, string HandlerId = null, string HandlerReferenceId = null, string HandlerData = null) { if (string.IsNullOrEmpty(MimeType) || MimeType.Equals("unknown/unknown", StringComparison.OrdinalIgnoreCase)) MimeType = Interop.MimeTypes.ResolveMimeType(Filename); @@ -177,8 +192,11 @@ namespace Disco.Services TechUserId = CreatorUser.UserId, Filename = Filename, MimeType = MimeType, - Timestamp = DateTime.Now, - Comments = Comments + Timestamp = Timestamp, + Comments = Comments, + HandlerId = HandlerId, + HandlerReferenceId = HandlerReferenceId, + HandlerData = HandlerData, }; if (DocumentTemplate != null) diff --git a/Disco.Services/Documents/AttachmentImport/Importer.cs b/Disco.Services/Documents/AttachmentImport/Importer.cs index 4be8b0a6..8ec3c8f3 100644 --- a/Disco.Services/Documents/AttachmentImport/Importer.cs +++ b/Disco.Services/Documents/AttachmentImport/Importer.cs @@ -165,12 +165,12 @@ namespace Disco.Services.Documents.AttachmentImport } } - public static bool ImportPdfAttachment(this DocumentUniqueIdentifier Identifier, DiscoDataContext Database, string PdfFilename) + public static IAttachment ImportPdfAttachment(this DocumentUniqueIdentifier Identifier, DiscoDataContext Database, string PdfFilename) { return ImportPdfAttachment(Identifier, Database, PdfFilename, null); } - public static bool ImportPdfAttachment(this DocumentUniqueIdentifier Identifier, DiscoDataContext Database, string PdfFilename, Image Thumbnail) + public static IAttachment ImportPdfAttachment(this DocumentUniqueIdentifier Identifier, DiscoDataContext Database, string PdfFilename, Image Thumbnail) { using (var pdfStream = File.OpenRead(PdfFilename)) { @@ -178,17 +178,20 @@ namespace Disco.Services.Documents.AttachmentImport } } - public static bool ImportPdfAttachment(this DocumentUniqueIdentifier Identifier, DiscoDataContext Database, Stream PdfContent) + public static IAttachment ImportPdfAttachment(this DocumentUniqueIdentifier Identifier, DiscoDataContext Database, Stream PdfContent) { return ImportPdfAttachment(Identifier, Database, PdfContent, null, new List() { Identifier }); } - public static bool ImportPdfAttachment(this DocumentUniqueIdentifier Identifier, DiscoDataContext Database, Stream PdfContent, Image Thumbnail) + public static IAttachment ImportPdfAttachment(this DocumentUniqueIdentifier Identifier, DiscoDataContext Database, Stream PdfContent, Image Thumbnail) { return ImportPdfAttachment(Identifier, Database, PdfContent, Thumbnail, new List() { Identifier }); } - public static bool ImportPdfAttachment(this DocumentUniqueIdentifier Identifier, DiscoDataContext Database, Stream PdfContent, Image Thumbnail, List PageIdentifiers) + public static IAttachment ImportPdfAttachment(this DocumentUniqueIdentifier Identifier, DiscoDataContext Database, Stream PdfContent, Image Thumbnail, List PageIdentifiers) + => Identifier.ImportPdfAttachment(Database, PdfContent, Thumbnail, PageIdentifiers, null, null, null, null); + + public static IAttachment ImportPdfAttachment(this DocumentUniqueIdentifier Identifier, DiscoDataContext Database, Stream PdfContent, Image Thumbnail, List PageIdentifiers, DateTime? Timestamp, string HandlerId, string HandlerReferenceId, string HandlerData) { string filename; string comments; @@ -196,16 +199,32 @@ namespace Disco.Services.Documents.AttachmentImport if (Identifier.DocumentTemplate == null) { - filename = $"{Identifier.Target.AttachmentReferenceId.Replace('\\', '_')}_{Identifier.TimeStamp:yyyyMMdd-HHmmss}.pdf"; - comments = $"Uploaded: {Identifier.TimeStamp:s}"; + if (Timestamp.HasValue) + { + filename = $"{Identifier.Target.AttachmentReferenceId.Replace('\\', '_')}_{Timestamp:yyyyMMdd-HHmmss}.pdf"; + comments = $"Completed: {Timestamp:s}"; + } + else + { + filename = $"{Identifier.Target.AttachmentReferenceId.Replace('\\', '_')}_{Identifier.TimeStamp:yyyyMMdd-HHmmss}.pdf"; + comments = $"Uploaded: {Identifier.TimeStamp:s}"; + } } else { - filename = $"{Identifier.DocumentTemplateId}_{Identifier.TimeStamp:yyyyMMdd-HHmmss}.pdf"; - comments = string.Format("Generated: {0:s}", Identifier.TimeStamp); + if (Timestamp.HasValue) + { + filename = $"{Identifier.DocumentTemplateId}_{Timestamp:yyyyMMdd-HHmmss}.pdf"; + comments = $"Generated: {Identifier.TimeStamp:s}; Completed: {Timestamp:s}"; + } + else + { + filename = $"{Identifier.DocumentTemplateId}_{Identifier.TimeStamp:yyyyMMdd-HHmmss}.pdf"; + comments = $"Generated: {Identifier.TimeStamp:s}"; + } } - User creatorUser = UserService.GetUser(Identifier.CreatorId, Database, true); + User creatorUser = UserService.GetUser(Identifier.CreatorId, Database, false); if (creatorUser == null) { // No Creator User (or Username invalid) @@ -216,18 +235,18 @@ namespace Disco.Services.Documents.AttachmentImport { case AttachmentTypes.Device: Device d = (Device)Identifier.Target; - attachment = d.CreateAttachment(Database, creatorUser, filename, DocumentTemplate.PdfMimeType, comments, PdfContent, Identifier.DocumentTemplate, Thumbnail); + attachment = d.CreateAttachment(Database, creatorUser, filename, Timestamp ?? DateTime.Now, DocumentTemplate.PdfMimeType, comments, PdfContent, Identifier.DocumentTemplate, Thumbnail, HandlerId, HandlerReferenceId, HandlerData); break; case AttachmentTypes.Job: Job j = (Job)Identifier.Target; - attachment = j.CreateAttachment(Database, creatorUser, filename, DocumentTemplate.PdfMimeType, comments, PdfContent, Identifier.DocumentTemplate, Thumbnail); + attachment = j.CreateAttachment(Database, creatorUser, filename, Timestamp ?? DateTime.Now, DocumentTemplate.PdfMimeType, comments, PdfContent, Identifier.DocumentTemplate, Thumbnail, HandlerId, HandlerReferenceId, HandlerData); break; case AttachmentTypes.User: User u = (User)Identifier.Target; - attachment = u.CreateAttachment(Database, creatorUser, filename, DocumentTemplate.PdfMimeType, comments, PdfContent, Identifier.DocumentTemplate, Thumbnail); + attachment = u.CreateAttachment(Database, creatorUser, filename, Timestamp ?? DateTime.Now, DocumentTemplate.PdfMimeType, comments, PdfContent, Identifier.DocumentTemplate, Thumbnail, HandlerId, HandlerReferenceId, HandlerData); break; default: - return false; + return null; } if (Identifier.DocumentTemplate != null && !string.IsNullOrWhiteSpace(Identifier.DocumentTemplate.OnImportAttachmentExpression)) @@ -242,7 +261,8 @@ namespace Disco.Services.Documents.AttachmentImport SystemLog.LogException("Document Importer - OnImportAttachmentExpression", ex); } } - return true; + + return attachment; } } } diff --git a/Disco.Services/Extensions/EnumerableExtensions.cs b/Disco.Services/Extensions/EnumerableExtensions.cs index 066c1eb4..7f6448e0 100644 --- a/Disco.Services/Extensions/EnumerableExtensions.cs +++ b/Disco.Services/Extensions/EnumerableExtensions.cs @@ -1,4 +1,6 @@ -using System.Collections.Generic; +using System.Collections; +using System.Collections.Generic; +using System.Linq; namespace Disco { @@ -26,4 +28,61 @@ namespace Disco } } + + public static class OneOf + { + public static OneOf Create(T instance) + => OneOf.Create(instance); + } + public struct OneOf : IEnumerable + { + private readonly T instance; + + private OneOf(T instance) + { + this.instance = instance; + } + + public static OneOf Create(T instance) + => new OneOf(instance); + + public IEnumerator GetEnumerator() + => new OneOfEnumerator(instance); + + IEnumerator IEnumerable.GetEnumerator() + => new OneOfEnumerator(instance); + + private struct OneOfEnumerator : IEnumerator + { + private readonly T instance; + private bool moved; + + public OneOfEnumerator(T instance) + { + this.instance = instance; + moved = false; + } + + public T Current => instance; + + object IEnumerator.Current => instance; + + public void Dispose() { } + + public bool MoveNext() + { + if (!moved) + { + moved = true; + return true; + } + return false; + } + + public void Reset() + { + moved = false; + } + } + } } diff --git a/Disco.Services/Plugins/Features/DocumentHandlerProvider/DocumentHandlerProviderFeature.cs b/Disco.Services/Plugins/Features/DocumentHandlerProvider/DocumentHandlerProviderFeature.cs new file mode 100644 index 00000000..2560bdf1 --- /dev/null +++ b/Disco.Services/Plugins/Features/DocumentHandlerProvider/DocumentHandlerProviderFeature.cs @@ -0,0 +1,32 @@ +using Disco.Models.Repository; +using Disco.Models.Services.Documents; +using System; +using System.Collections.Generic; +using System.Web.Mvc; + +namespace Disco.Services.Plugins.Features.DocumentHandlerProvider +{ + [PluginFeatureCategory(DisplayName = "Document Handler")] + public abstract class DocumentHandlerProviderFeature : PluginFeature + { + public abstract string HandlerTitle { get; } + public abstract string HandlerDescription { get; } + + public abstract bool CanHandle(DocumentTemplate template); + public abstract bool CanHandle(DocumentTemplatePackage templatePackage); + public abstract bool CanHandle(DocumentTemplate template, IAttachmentTarget target); + public abstract bool CanHandle(DocumentTemplatePackage templatePackage, IAttachmentTarget target); + public abstract bool CanHandleBulk(DocumentTemplate template); + public abstract bool CanHandleBulk(DocumentTemplatePackage templatePackage); + + public abstract Type GenerationOptionsUi { get; } + public virtual string GenerationOptionsIcon => "file-text-o"; + public abstract object GetGenerationOptionsUiModel(DocumentTemplate template, IAttachmentTarget target, User targetUser, User techUser); + public abstract object GetGenerationOptionsUiModel(DocumentTemplatePackage templatePackage, IAttachmentTarget target, User targetUser, User techUser); + + public abstract ActionResult Handle(DocumentTemplate template, IAttachmentTarget target, User targetUser, User techUser); + public abstract ActionResult Handle(DocumentTemplatePackage templatePackage, IAttachmentTarget target, User targetUser, User techUser); + public abstract ActionResult HandleBulk(DocumentTemplate template, IList targets, User targetUser, User techUser); + public abstract ActionResult HandleBulk(DocumentTemplatePackage templatePackage, IList targets, User targetUser, User techUser); + } +} diff --git a/Disco.Web/Areas/API/Controllers/DocumentTemplateController.cs b/Disco.Web/Areas/API/Controllers/DocumentTemplateController.cs index 4febe843..fc008764 100644 --- a/Disco.Web/Areas/API/Controllers/DocumentTemplateController.cs +++ b/Disco.Web/Areas/API/Controllers/DocumentTemplateController.cs @@ -765,5 +765,66 @@ namespace Disco.Web.Areas.API.Controllers #endregion + #region Handlers + [HttpPost] + public virtual ActionResult GenerateDocumentHandlerUi(string templateId, string targetId, string handlerId) + { + Disco.Services.DocumentTemplateExtensions.GetTemplateAndTarget(Database, Authorization, templateId, targetId, out var template, out var target, out var targetUser); + + var handlerManifest = Plugins.GetPluginFeature(handlerId, typeof(DocumentHandlerProviderFeature)); + + using (var handler = handlerManifest.CreateInstance()) + { + if (!handler.CanHandle(template, target)) + throw new NotSupportedException("Handler does not support this Document Template and Target"); + + var handlerPartialView = handler.GenerationOptionsUi; + + if (handlerPartialView == null) + throw new NotSupportedException("Handler does not have a Generation Options UI"); + + + + var model = handler.GetGenerationOptionsUiModel(template, target, targetUser, CurrentUser); + + return this.PrecompiledPartialView(handlerPartialView, model); + } + } + + [HttpPost] + public virtual ActionResult DocumentHandlers(string templateId, string targetId) + { + Disco.Services.DocumentTemplateExtensions.GetTemplateAndTarget(Database, Authorization, templateId, targetId, out var template, out var target, out _); + + var handlers = Plugins.GetPluginFeatures(typeof(DocumentHandlerProviderFeature)) + .SelectMany(f => + { + using (var handler = f.CreateInstance()) + { + if (handler.CanHandle(template, target)) + return OneOf.Create(new DocumentHandlerModel() + { + Id = f.Id, + Title = handler.HandlerTitle, + Description = handler.HandlerDescription, + UiUrl = handler.GenerationOptionsUi == null ? null : Url.Action(MVC.API.DocumentTemplate.GenerateDocumentHandlerUi(template.Id, target.AttachmentReferenceId, f.Id)), + Icon = handler.GenerationOptionsIcon, + }); + }; + return Enumerable.Empty(); + }).ToList(); + + var model = new DocumentHandlersModel() + { + TemplateId = template.Id, + TemplateName = template.Description, + TargetId = target.AttachmentReferenceId, + TargetName = target.ToString(), + Handlers = handlers, + }; + + return Json(model); + } + #endregion } } diff --git a/Disco.Web/Areas/API/Models/DocumentTemplate/DocumentHandlersModel.cs b/Disco.Web/Areas/API/Models/DocumentTemplate/DocumentHandlersModel.cs new file mode 100644 index 00000000..75961340 --- /dev/null +++ b/Disco.Web/Areas/API/Models/DocumentTemplate/DocumentHandlersModel.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; + +namespace Disco.Web.Areas.API.Models.DocumentTemplate +{ + public class DocumentHandlersModel + { + public string TemplateId { get; set; } + public string TemplateName { get; set; } + public string TargetId { get; set; } + public string TargetName { get; set; } + + public List Handlers { get; set; } + } + + public class DocumentHandlerModel + { + public string Id { get; set; } + public string Title { get; set; } + public string Description { get; set; } + public string UiUrl { get; set; } + public string Icon { get; set; } + } +} \ No newline at end of file diff --git a/Disco.Web/ClientSource/Scripts/Modules/Disco-DocumentGenerator.js b/Disco.Web/ClientSource/Scripts/Modules/Disco-DocumentGenerator.js new file mode 100644 index 00000000..0ff24e76 --- /dev/null +++ b/Disco.Web/ClientSource/Scripts/Modules/Disco-DocumentGenerator.js @@ -0,0 +1,136 @@ +(function (window, document, $) { + $(function () { + let $generationHost = null; + const $container = $('#Document_Generation_Container'); + const $control = $container.find('#Document_Generate'); + const targetId = $container.attr('data-targetid'); + const targetType = $container.attr('data-targettype'); + const generatePdfUrl = $container.attr('data-generatepdfurl'); + const generatePackageUrl = $container.attr('data-generatepackageurl'); + const handlersPresent = $container.attr('data-handlerspresent') === 'true'; + const handlersUrl = $container.attr('data-handlersurl'); + let $handlersDialog = null; + + const downloadPdf = function (templateId) { + + let url; + if (templateId.lastIndexOf('Package:', 0) === 0) + url = generatePackageUrl + templateId.substring(8); + else + url = generatePdfUrl + templateId; + url = url + '?TargetId=' + targetId; + + if ($.connection && $.connection.hub && $.connection.hub.transport && + $.connection.hub.transport.name == 'foreverFrame') { + // SignalR active with foreverFrame transport - use popup window + window.open(url, '_blank', 'height=150,width=250,location=no,menubar=no,resizable=no,scrollbars=no,status=no,toolbar=no'); + } else { + // use iFrame + if (!$generationHost) { + $generationHost = $('