qol: option to view/print generated document inline

This commit is contained in:
Gary Sharp
2026-03-01 14:15:27 +11:00
parent 892299a791
commit 4f7f6db804
14 changed files with 170 additions and 28 deletions
@@ -1774,7 +1774,7 @@ namespace Disco.Web.Areas.API.Controllers
}
[HttpPost, ValidateAntiForgeryToken]
public virtual ActionResult Generate(string id, string targetId)
public virtual ActionResult Generate(string id, string targetId, bool? inline = null)
{
Disco.Services.DocumentTemplateExtensions.GetTemplateAndTarget(Database, Authorization, id, targetId, out var template, out var target, out _);
@@ -1787,7 +1787,7 @@ namespace Disco.Web.Areas.API.Controllers
}
Database.SaveChanges();
return File(document, "application/pdf", $"{template.Id}_{target.AttachmentReferenceId.Replace('\\', '_')}_{timestamp:yyyyMMdd-HHmmss}.pdf");
return File(document, "application/pdf", (inline ?? false) ? null : $"{template.Id}_{target.AttachmentReferenceId.Replace('\\', '_')}_{timestamp:yyyyMMdd-HHmmss}.pdf");
}
[DiscoAuthorize(Claims.Config.DocumentTemplate.Delete)]
@@ -19,12 +19,12 @@ namespace Disco.Web.Areas.API.Controllers
{
public partial class DocumentTemplatePackageController : AuthorizedDatabaseController
{
const string pDescription = "description";
const string pScope = "scope";
const string pFilterExpression = "filterexpression";
const string pOnGenerateExpression = "ongenerateexpression";
const string pIsHidden = "ishidden";
const string pInsertBlankPages = "insertblankpages";
private const string pDescription = "description";
private const string pScope = "scope";
private const string pFilterExpression = "filterexpression";
private const string pOnGenerateExpression = "ongenerateexpression";
private const string pIsHidden = "ishidden";
private const string pInsertBlankPages = "insertblankpages";
[DiscoAuthorize(Claims.Config.DocumentTemplate.Configure)]
[HttpPost, ValidateAntiForgeryToken]
@@ -396,7 +396,7 @@ namespace Disco.Web.Areas.API.Controllers
}
[HttpPost, ValidateAntiForgeryToken]
public virtual ActionResult Generate(string id, string targetId)
public virtual ActionResult Generate(string id, string targetId, bool? inline = false)
{
if (string.IsNullOrWhiteSpace(id))
throw new ArgumentNullException(nameof(id));
@@ -435,7 +435,7 @@ namespace Disco.Web.Areas.API.Controllers
}
Database.SaveChanges();
return File(document, "application/pdf", $"{package.Id}_{target.AttachmentReferenceId.Replace('\\', '_')}_{timestamp:yyyyMMdd-HHmmss}.pdf");
return File(document, "application/pdf", (inline ?? false) ? null : $"{package.Id}_{target.AttachmentReferenceId.Replace('\\', '_')}_{timestamp:yyyyMMdd-HHmmss}.pdf");
}
[DiscoAuthorize(Claims.Config.DocumentTemplate.Delete)]
@@ -12,6 +12,7 @@
const handlersPackageUrl = $container.attr('data-handlerspackageurl');
let $handlersDialog = null;
let lastTemplateId = null;
let lastTemplateName = null;
const downloadPdf = function (templateId) {
let action = generatePdfUrl;
@@ -35,6 +36,36 @@
form.submit();
}
const viewPdf = function (templateId, templateName) {
let action = generatePdfUrl;
if (templateId.lastIndexOf('Package:', 0) === 0) {
templateId = templateId.substring(8);
action = generatePackageUrl;
}
const $dialog = $('<div id="Document_Generation_View_Dialog" class="dialog">')
.appendTo(document.body)
.dialog({
resizable: false,
modal: true,
autoOpen: true,
width: 850,
height: 700,
title: 'Document: ' + templateName,
close: function () {
$dialog.dialog('destroy').remove();
}
});
const $iframe = $('<iframe>').appendTo($dialog);
const $iframeContents = $iframe.contents()[0];
$iframeContents.body.innerHTML = '<form method="post"><input type="hidden" name="__RequestVerificationToken" value="' + document.body.dataset.antiforgery + '"><input type="hidden" name="id"><input type="hidden" name="targetId"><input type="hidden" name="inline" value="True"></form>';
const form = $iframeContents.forms[0];
form.action = action;
form.id.value = templateId;
form.targetId.value = targetId;
form.submit();
}
const updateHandlers = function (templateId) {
let action = handlersUrl;
if (templateId.lastIndexOf('Package:', 0) === 0) {
@@ -71,6 +102,7 @@
var templateId = $control.val();
if (templateId) {
lastTemplateId = templateId;
lastTemplateName = $control[0].selectedOptions[0].label;
if (handlersPresent) {
if (!$handlersDialog) {
$handlersDialog = $container.find('#Document_Generation_Dialog');
@@ -91,7 +123,15 @@
downloadPdf(lastTemplateId);
$handlersDialog.dialog('close');
return false;
})
});
if (navigator.pdfViewerEnabled) {
$handlersDialog.find('#Document_Generation_Dialog_View').css('display', 'block').on('click', e => {
e.preventDefault();
$handlersDialog.dialog('close');
viewPdf(lastTemplateId, lastTemplateName);
return false;
});
}
const $handlerPicker = $handlersDialog.find('.handlerPicker');
const $Document_Generation_Dialog_Download_Container = $handlersDialog.find('#Document_Generation_Dialog_Download_Container');
const $Document_Generation_Dialog_HandlerUI = $handlersDialog.find('#Document_Generation_Dialog_HandlerUI');
@@ -1 +1 @@
(function(n,t,i){i(function(){let u=null;const r=i("#Document_Generation_Container"),f=r.find("#Document_Generate"),e=r.attr("data-targetid"),p=r.attr("data-targettype"),h=r.attr("data-generatepdfurl"),c=r.attr("data-generatepackageurl"),l=r.attr("data-handlerspresent")==="true",a=r.attr("data-handlersurl"),v=r.attr("data-handlerspackageurl");let n=null,o=null;const s=function(n){let f=h;n.lastIndexOf("Package:",0)===0&&(n=n.substring(8),f=c);u||(u=i("<iframe>").attr("title","Document Generation Host").addClass("hidden").appendTo("body").contents(),u[0].body.innerHTML='<form method="post"><input type="hidden" name="__RequestVerificationToken" value="'+t.body.dataset.antiforgery+'"><input type="hidden" name="id"><input type="hidden" name="targetId"><\/form>');const r=u[0].forms[0];r.action=f;r.id.value=n;r.targetId.value=e;r.submit()},y=function(r){let f=a;r.lastIndexOf("Package:",0)===0&&(r=r.substring(8),f=v);const o=n.find(".handlerPicker"),s=n.find("#Document_Generation_Dialog_Handlers_Loading");o.find("div.handler").remove();s.show();var u=new FormData;u.append("__RequestVerificationToken",t.body.dataset.antiforgery);u.append("id",decodeURI(r));u.append("targetId",decodeURI(e));fetch(f,{method:"POST",body:u}).then(n=>n.json()).then(n=>{s.hide(),i.each(n.Handlers,(n,t)=>{i('<div class="handler">').text(t.Title).attr({"data-id":t.Id,"data-uiurl":t.UiUrl}).prepend(i('<i class="fa fa-fw fa-lg">').addClass("fa-"+t.Icon)).appendTo(o)})})};f.change(function(){var u=f.val();if(u){if(o=u,l){if(!n){n=r.find("#Document_Generation_Dialog");n.dialog({width:750,height:500,resizable:!1,modal:!0,autoOpen:!1,buttons:{Cancel:function(){i(this).dialog("close")}}});n.find("#Document_Generation_Dialog_Download").click(t=>(t.preventDefault(),s(o),n.dialog("close"),!1));const f=n.find(".handlerPicker"),e=n.find("#Document_Generation_Dialog_Download_Container"),u=n.find("#Document_Generation_Dialog_HandlerUI");f.on("click","div[data-id]",n=>{f.find("div").removeClass("selected");const r=i(n.currentTarget);r.addClass("selected");const o=r.attr("data-id");if(o==="download")e.show(),u.hide(),u.empty();else{e.hide();u.empty();u.show();const i=r.attr("data-uiurl"),n=new FormData;n.append("__RequestVerificationToken",t.body.dataset.antiforgery);fetch(i,{method:"POST",body:n}).then(n=>n.text()).then(n=>{u.html(n)})}})}const e=n.find(".handlerPicker"),c=n.find("#Document_Generation_Dialog_Download_Container"),h=n.find("#Document_Generation_Dialog_HandlerUI");e.find("div").removeClass("selected");e.find("div[data-id=download]").addClass("selected");c.show();h.hide();h.empty();n.dialog("option","title","Generate Document: "+f[0].selectedOptions[0].label);n.dialog("open");y(u)}else s(u);f.val("").blur()}})})})(window,document,$);
(function(n,t,i){i(function(){let f=null;const r=i("#Document_Generation_Container"),u=r.find("#Document_Generate"),e=r.attr("data-targetid"),b=r.attr("data-targettype"),s=r.attr("data-generatepdfurl"),h=r.attr("data-generatepackageurl"),a=r.attr("data-handlerspresent")==="true",v=r.attr("data-handlersurl"),y=r.attr("data-handlerspackageurl");let n=null,o=null,c=null;const l=function(n){let u=s;n.lastIndexOf("Package:",0)===0&&(n=n.substring(8),u=h);f||(f=i("<iframe>").attr("title","Document Generation Host").addClass("hidden").appendTo("body").contents(),f[0].body.innerHTML='<form method="post"><input type="hidden" name="__RequestVerificationToken" value="'+t.body.dataset.antiforgery+'"><input type="hidden" name="id"><input type="hidden" name="targetId"><\/form>');const r=f[0].forms[0];r.action=u;r.id.value=n;r.targetId.value=e;r.submit()},p=function(n,r){let f=s;n.lastIndexOf("Package:",0)===0&&(n=n.substring(8),f=h);const o=i('<div id="Document_Generation_View_Dialog" class="dialog">').appendTo(t.body).dialog({resizable:!1,modal:!0,autoOpen:!0,width:850,height:700,title:"Document: "+r,close:function(){o.dialog("destroy").remove()}}),l=i("<iframe>").appendTo(o),c=l.contents()[0];c.body.innerHTML='<form method="post"><input type="hidden" name="__RequestVerificationToken" value="'+t.body.dataset.antiforgery+'"><input type="hidden" name="id"><input type="hidden" name="targetId"><input type="hidden" name="inline" value="True"><\/form>';const u=c.forms[0];u.action=f;u.id.value=n;u.targetId.value=e;u.submit()},w=function(r){let f=v;r.lastIndexOf("Package:",0)===0&&(r=r.substring(8),f=y);const o=n.find(".handlerPicker"),s=n.find("#Document_Generation_Dialog_Handlers_Loading");o.find("div.handler").remove();s.show();var u=new FormData;u.append("__RequestVerificationToken",t.body.dataset.antiforgery);u.append("id",decodeURI(r));u.append("targetId",decodeURI(e));fetch(f,{method:"POST",body:u}).then(n=>n.json()).then(n=>{s.hide(),i.each(n.Handlers,(n,t)=>{i('<div class="handler">').text(t.Title).attr({"data-id":t.Id,"data-uiurl":t.UiUrl}).prepend(i('<i class="fa fa-fw fa-lg">').addClass("fa-"+t.Icon)).appendTo(o)})})};u.change(function(){var f=u.val();if(f){if(o=f,c=u[0].selectedOptions[0].label,a){if(!n){if(n=r.find("#Document_Generation_Dialog"),n.dialog({width:750,height:500,resizable:!1,modal:!0,autoOpen:!1,buttons:{Cancel:function(){i(this).dialog("close")}}}),n.find("#Document_Generation_Dialog_Download").click(t=>(t.preventDefault(),l(o),n.dialog("close"),!1)),navigator.pdfViewerEnabled)n.find("#Document_Generation_Dialog_View").css("display","block").on("click",t=>(t.preventDefault(),n.dialog("close"),p(o,c),!1));const f=n.find(".handlerPicker"),e=n.find("#Document_Generation_Dialog_Download_Container"),u=n.find("#Document_Generation_Dialog_HandlerUI");f.on("click","div[data-id]",n=>{f.find("div").removeClass("selected");const r=i(n.currentTarget);r.addClass("selected");const o=r.attr("data-id");if(o==="download")e.show(),u.hide(),u.empty();else{e.hide();u.empty();u.show();const i=r.attr("data-uiurl"),n=new FormData;n.append("__RequestVerificationToken",t.body.dataset.antiforgery);fetch(i,{method:"POST",body:n}).then(n=>n.text()).then(n=>{u.html(n)})}})}const e=n.find(".handlerPicker"),h=n.find("#Document_Generation_Dialog_Download_Container"),s=n.find("#Document_Generation_Dialog_HandlerUI");e.find("div").removeClass("selected");e.find("div[data-id=download]").addClass("selected");h.show();s.hide();s.empty();n.dialog("option","title","Generate Document: "+u[0].selectedOptions[0].label);n.dialog("open");w(f)}else l(f);u.val("").blur()}})})})(window,document,$);
@@ -12,6 +12,7 @@
const handlersPackageUrl = $container.attr('data-handlerspackageurl');
let $handlersDialog = null;
let lastTemplateId = null;
let lastTemplateName = null;
const downloadPdf = function (templateId) {
let action = generatePdfUrl;
@@ -35,6 +36,36 @@
form.submit();
}
const viewPdf = function (templateId, templateName) {
let action = generatePdfUrl;
if (templateId.lastIndexOf('Package:', 0) === 0) {
templateId = templateId.substring(8);
action = generatePackageUrl;
}
const $dialog = $('<div id="Document_Generation_View_Dialog" class="dialog">')
.appendTo(document.body)
.dialog({
resizable: false,
modal: true,
autoOpen: true,
width: 850,
height: 700,
title: 'Document: ' + templateName,
close: function () {
$dialog.dialog('destroy').remove();
}
});
const $iframe = $('<iframe>').appendTo($dialog);
const $iframeContents = $iframe.contents()[0];
$iframeContents.body.innerHTML = '<form method="post"><input type="hidden" name="__RequestVerificationToken" value="' + document.body.dataset.antiforgery + '"><input type="hidden" name="id"><input type="hidden" name="targetId"><input type="hidden" name="inline" value="True"></form>';
const form = $iframeContents.forms[0];
form.action = action;
form.id.value = templateId;
form.targetId.value = targetId;
form.submit();
}
const updateHandlers = function (templateId) {
let action = handlersUrl;
if (templateId.lastIndexOf('Package:', 0) === 0) {
@@ -71,6 +102,7 @@
var templateId = $control.val();
if (templateId) {
lastTemplateId = templateId;
lastTemplateName = $control[0].selectedOptions[0].label;
if (handlersPresent) {
if (!$handlersDialog) {
$handlersDialog = $container.find('#Document_Generation_Dialog');
@@ -91,7 +123,15 @@
downloadPdf(lastTemplateId);
$handlersDialog.dialog('close');
return false;
})
});
if (navigator.pdfViewerEnabled) {
$handlersDialog.find('#Document_Generation_Dialog_View').css('display', 'block').on('click', e => {
e.preventDefault();
$handlersDialog.dialog('close');
viewPdf(lastTemplateId, lastTemplateName);
return false;
});
}
const $handlerPicker = $handlersDialog.find('.handlerPicker');
const $Document_Generation_Dialog_Download_Container = $handlersDialog.find('#Document_Generation_Dialog_Download_Container');
const $Document_Generation_Dialog_HandlerUI = $handlersDialog.find('#Document_Generation_Dialog_HandlerUI');
+16 -1
View File
@@ -6087,11 +6087,26 @@ textarea.block {
}
#Document_Generation_Dialog .details #Document_Generation_Dialog_Download_Container {
text-align: center;
padding-top: 3em;
}
#Document_Generation_Dialog .details #Document_Generation_Dialog_Download_Container button {
display: block;
margin: 3em auto;
}
#Document_Generation_Dialog .details #Document_Generation_Dialog_Download_Container #Document_Generation_Dialog_View {
display: none;
}
#Document_Generation_Dialog .details #Document_Generation_Dialog_HandlerUI {
display: none;
}
#Document_Generation_View_Dialog {
padding: 0;
overflow: hidden;
}
#Document_Generation_View_Dialog iframe {
border: none;
width: 100%;
height: 100%;
}
ul.list-group {
background-color: #fff;
border: 1px solid #ddd;
File diff suppressed because one or more lines are too long
+16 -1
View File
@@ -1560,11 +1560,26 @@ textarea.block {
}
#Document_Generation_Dialog .details #Document_Generation_Dialog_Download_Container {
text-align: center;
padding-top: 3em;
}
#Document_Generation_Dialog .details #Document_Generation_Dialog_Download_Container button {
display: block;
margin: 3em auto;
}
#Document_Generation_Dialog .details #Document_Generation_Dialog_Download_Container #Document_Generation_Dialog_View {
display: none;
}
#Document_Generation_Dialog .details #Document_Generation_Dialog_HandlerUI {
display: none;
}
#Document_Generation_View_Dialog {
padding: 0;
overflow: hidden;
}
#Document_Generation_View_Dialog iframe {
border: none;
width: 100%;
height: 100%;
}
ul.list-group {
background-color: #fff;
border: 1px solid #ddd;
+20 -1
View File
@@ -1650,7 +1650,15 @@ textarea.block {
#Document_Generation_Dialog_Download_Container {
text-align: center;
padding-top: 3em;
button {
display: block;
margin: 3em auto;
}
#Document_Generation_Dialog_View {
display: none;
}
}
#Document_Generation_Dialog_HandlerUI {
@@ -1659,6 +1667,17 @@ textarea.block {
}
}
#Document_Generation_View_Dialog {
padding: 0;
overflow: hidden;
iframe {
border: none;
width: 100%;
height: 100%;
}
}
ul.list-group {
background-color: @white;
border: 1px solid #ddd;
File diff suppressed because one or more lines are too long
@@ -750,6 +750,7 @@ namespace Disco.Web.Areas.API.Controllers
{
public readonly string id = "id";
public readonly string targetId = "targetId";
public readonly string inline = "inline";
}
static readonly ActionParamsClass_Delete s_params_Delete = new ActionParamsClass_Delete();
[GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode]
@@ -1321,15 +1322,16 @@ namespace Disco.Web.Areas.API.Controllers
}
[NonAction]
partial void GenerateOverride(T4MVC_System_Web_Mvc_ActionResult callInfo, string id, string targetId);
partial void GenerateOverride(T4MVC_System_Web_Mvc_ActionResult callInfo, string id, string targetId, bool? inline);
[NonAction]
public override System.Web.Mvc.ActionResult Generate(string id, string targetId)
public override System.Web.Mvc.ActionResult Generate(string id, string targetId, bool? inline)
{
var callInfo = new T4MVC_System_Web_Mvc_ActionResult(Area, Name, ActionNames.Generate);
ModelUnbinderHelpers.AddRouteValues(callInfo.RouteValueDictionary, "id", id);
ModelUnbinderHelpers.AddRouteValues(callInfo.RouteValueDictionary, "targetId", targetId);
GenerateOverride(callInfo, id, targetId);
ModelUnbinderHelpers.AddRouteValues(callInfo.RouteValueDictionary, "inline", inline);
GenerateOverride(callInfo, id, targetId, inline);
return callInfo;
}
@@ -303,6 +303,7 @@ namespace Disco.Web.Areas.API.Controllers
{
public readonly string id = "id";
public readonly string targetId = "targetId";
public readonly string inline = "inline";
}
static readonly ActionParamsClass_Delete s_params_Delete = new ActionParamsClass_Delete();
[GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode]
@@ -492,15 +493,16 @@ namespace Disco.Web.Areas.API.Controllers
}
[NonAction]
partial void GenerateOverride(T4MVC_System_Web_Mvc_ActionResult callInfo, string id, string targetId);
partial void GenerateOverride(T4MVC_System_Web_Mvc_ActionResult callInfo, string id, string targetId, bool? inline);
[NonAction]
public override System.Web.Mvc.ActionResult Generate(string id, string targetId)
public override System.Web.Mvc.ActionResult Generate(string id, string targetId, bool? inline)
{
var callInfo = new T4MVC_System_Web_Mvc_ActionResult(Area, Name, ActionNames.Generate);
ModelUnbinderHelpers.AddRouteValues(callInfo.RouteValueDictionary, "id", id);
ModelUnbinderHelpers.AddRouteValues(callInfo.RouteValueDictionary, "targetId", targetId);
GenerateOverride(callInfo, id, targetId);
ModelUnbinderHelpers.AddRouteValues(callInfo.RouteValueDictionary, "inline", inline);
GenerateOverride(callInfo, id, targetId, inline);
return callInfo;
}
@@ -21,6 +21,7 @@
<div class="details">
<div id="Document_Generation_Dialog_Download_Container">
<button id="Document_Generation_Dialog_Download" type="button" class="button">Download Document</button>
<button id="Document_Generation_Dialog_View" type="button" class="button">View/Print Document</button>
</div>
<div id="Document_Generation_Dialog_HandlerUI">
</div>
@@ -211,15 +211,23 @@ WriteLiteral(" type=\"button\"");
WriteLiteral(" class=\"button\"");
WriteLiteral(">Download Document</button>\r\n </div>\r\n <div" +
"");
WriteLiteral(">Download Document</button>\r\n <button");
WriteLiteral(" id=\"Document_Generation_Dialog_View\"");
WriteLiteral(" type=\"button\"");
WriteLiteral(" class=\"button\"");
WriteLiteral(">View/Print Document</button>\r\n </div>\r\n <d" +
"iv");
WriteLiteral(" id=\"Document_Generation_Dialog_HandlerUI\"");
WriteLiteral(">\r\n </div>\r\n </div>\r\n </div>\r\n");
#line 29 "..\..\Views\Shared\_GenerateDocumentControl.cshtml"
#line 30 "..\..\Views\Shared\_GenerateDocumentControl.cshtml"
}
@@ -228,7 +236,7 @@ WriteLiteral(">\r\n </div>\r\n </div>\r\n
WriteLiteral(" </div>\r\n");
#line 31 "..\..\Views\Shared\_GenerateDocumentControl.cshtml"
#line 32 "..\..\Views\Shared\_GenerateDocumentControl.cshtml"
Html.BundleDeferred("~/ClientScripts/Modules/Disco-DocumentGenerator");
}