A view component

vnext
Paul Schneider 8 years ago
parent bd39e900ef
commit 207b0e8664
11 changed files with 258 additions and 190 deletions

@ -1,19 +1,66 @@
using System;
using System.IO;
using System.Security.Claims;
using Microsoft.AspNet.Authorization;
using Microsoft.AspNet.Mvc;
using System.Threading.Tasks;
using System.Web.Routing;
using Microsoft.AspNet.Mvc.ViewComponents;
using Microsoft.AspNet.Razor;
namespace Yavsc.ApiControllers
{
using Models;
[Route("api/pdfestimate"), Authorize]
public class PdfEstimateController : Controller
{
[HttpGet("{id}", Name = "Get")]
ApplicationDbContext dbContext;
DefaultViewComponentHelper helper;
IViewComponentDescriptorCollectionProvider provider;
IViewComponentInvokerFactory factory;
RazorEngineHost host;
RazorTemplateEngine engine;
IViewComponentSelector selector;
public PdfEstimateController(
IViewComponentDescriptorCollectionProvider provider,
IViewComponentSelector selector,
IViewComponentInvokerFactory factory,
ApplicationDbContext context)
{
this.selector = selector;
this.provider = provider;
this.factory = factory;
helper = new DefaultViewComponentHelper(provider, selector, factory);
dbContext = context;
var language = new CSharpRazorCodeLanguage();
host = new RazorEngineHost(language)
{
DefaultBaseClass = "RazorPage",
DefaultClassName = "Estimate",
DefaultNamespace = "Yavsc",
};
// Everyone needs the System namespace, right?
host.NamespaceImports.Add("System");
engine = new RazorTemplateEngine(host);
/*
GeneratorResults razorResult =
engine.GenerateCode(
) */
}
[HttpGet("get/{id}", Name = "Get"), Authorize]
public IActionResult Get(long id)
{
var filename = $"estimate-{id}.pdf";
var cd = new System.Net.Mime.ContentDisposition
{
// for example foo.bak
@ -23,15 +70,43 @@ namespace Yavsc.ApiControllers
// the browser to try to show the file inline
Inline = false,
};
FileInfo fi = new FileInfo(Path.Combine(Startup.UserBillsDirName,filename));
FileStreamResult result = null;
var s = fi.OpenRead();
result = File(s,"application/x-pdf",filename);
return result;
FileInfo fi = new FileInfo(Path.Combine(Startup.UserBillsDirName, filename));
if (!fi.Exists) return Ok(new { Error = "Not generated" });
return File(fi.OpenRead(), "application/x-pdf", filename); ;
}
[HttpGet("GetComponents", Name = "GetComponents")]
public IActionResult GetComponents()
{
return Ok(provider);
}
[HttpGet("estimate-{id}.tex", Name = "GetTex"), Authorize]
public IActionResult GetTex(long id)
{
Response.ContentType = "text/x-tex";
return ViewComponent("Estimate",new object[] { id, false });
}
[HttpGet("gen/{id}")]
public async Task<IActionResult> GeneratePdf(long id)
{
/*
using (TextWriter wr = new StringWriter()) {
ViewComponentContext ctx = new ViewComponentContext(
selector.SelectComponent("Estimate"), new object[]{id},
new ViewContext(),
wr
);
}
*/
return ViewComponent("Estimate",new object[] { id, true } );
}
}
}

@ -8,16 +8,6 @@ using Microsoft.Extensions.Logging;
using Yavsc.Models.Booking;
using Yavsc.Helpers;
using System;
using System.IO;
using System.Diagnostics;
using System.Text;
using Microsoft.AspNet.Mvc.ViewFeatures;
using Microsoft.AspNet.Razor;
using Microsoft.AspNet.Mvc.ViewEngines;
using Microsoft.AspNet.Mvc.Rendering;
using Microsoft.Extensions.OptionsModel;
using System.Threading.Tasks;
using System.Security.Claims;
namespace Yavsc.Controllers
{
@ -111,23 +101,11 @@ namespace Yavsc.Controllers
return View("Estimate.tex", estimate);
}
class TeOtions : IOptions<MvcViewOptions>
{
public MvcViewOptions Value
{
get
{
return new MvcViewOptions();
}
}
}
[Authorize,Route("Estimate-{id}.pdf")]
public async Task<IActionResult> EstimatePdf(long id)
public IActionResult EstimatePdf(long id)
{
ViewBag.TempDir = Startup.SiteSetup.TempDir;
ViewBag.BillsDir = Startup.UserBillsDirName;
var estimate = _context.Estimates.Include(x => x.Query)
.Include(x => x.Query.Client)
.Include(x => x.Query.PerformerProfile)
@ -136,102 +114,7 @@ namespace Yavsc.Controllers
.Include(e => e.Bill).FirstOrDefault(x => x.Id == id);
if (estimate==null)
throw new Exception("No data");
return View("Estimate.pdf",estimate);
/*
await result.ExecuteResultAsync(ActionContext);
var cd = new System.Net.Mime.ContentDisposition
{
// for example foo.bak
FileName = $"estimate-{id}.pdf",
// always prompt the user for downloading, set to true if you want
// the browser to try to show the file inline
Inline = false,
};
Response.Headers.Add("Content-Disposition", cd.ToString());
return File((Byte[])ViewData["Pdf"], "application/x-pdf"); */
}
/*
[Produces("application/x-pdf"), Authorize, Route("testimate-{id}.pdf")]
public async Task<IActionResult> BadEstimatePdf(long id)
{
var tempDir = Startup.SiteSetup.TempDir;
string name = $"tmpestimtex-{id}";
string fullname = new FileInfo(
System.IO.Path.Combine(tempDir, name)).FullName;
var writer = new System.IO.StringWriter();
try
{
using (StringWriter sw = new StringWriter())
{
Microsoft.AspNet.Mvc.ViewEngines.CompositeViewEngine ve = new CompositeViewEngine(
new TeOtions {}
);
ViewEngineResult viewResult = ve.FindPartialView(ActionContext, $"estimate-{id}.tex");
ViewContext viewContext = new ViewContext(); // ActionContext, viewResult.View, ViewData, TempData, sw);
await viewResult.View.RenderAsync(viewContext);
}
} catch (Exception ex)
{
}
FileInfo fo = new FileInfo(fullname + ".pdf");
if (!fi.Exists)
{
throw new Exception("Source write failed");
}
using (Process p = new Process())
{
p.StartInfo.WorkingDirectory = tempDir;
p.StartInfo = new ProcessStartInfo();
p.StartInfo.UseShellExecute = false;
p.StartInfo.FileName = "/usr/bin/texi2pdf";
p.StartInfo.Arguments = $"--batch --build-dir=.";
p.Start();
using (p.StandardInput)
{
}
p.WaitForExit();
if (p.ExitCode != 0)
{
throw new Exception("Pdf generation failed with exit code:" + p.ExitCode);
}
}
byte[] pdf = null;
if (fo.Exists)
{
using (StreamReader sr = new StreamReader(fo.FullName))
{
pdf = System.IO.File.ReadAllBytes(fo.FullName);
}
fo.Delete();
}
fi.Delete();
var cd = new System.Net.Mime.ContentDisposition
{
// for example foo.bak
FileName = $"estimate-{id}.pdf",
// always prompt the user for downloading, set to true if you want
// the browser to try to show the file inline
Inline = false,
};
Response.Headers.Add("Content-Disposition", cd.ToString());
return File(pdf, "application/x-pdf");
} */
}
}
}

@ -68,7 +68,6 @@ namespace Yavsc.Helpers
/// <summary>
/// Transforms a string of Markdown into HTML.
/// </summary>
/// <param name="helper">HtmlHelper - Not used, but required to make this an extension method.</param>
/// <param name="text">The Markdown that should be transformed.</param>
/// <param name="urlBaseLocation">The url Base Location.</param>
/// <returns>The HTML representation of the supplied Markdown.</returns>

@ -1,4 +1,12 @@
using System;
using System.IO;
using System.Linq;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Mvc;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.Rendering;
using Microsoft.AspNet.Mvc.ViewEngines;
using Microsoft.AspNet.Mvc.ViewFeatures;
namespace Yavsc.Helpers
{
@ -11,5 +19,32 @@ namespace Yavsc.Helpers
return string.Join(separator, items);
}
public static string RenderViewToString(
this Controller controller, IViewEngine engine,
IHttpContextAccessor httpContextAccessor,
string viewName, object model)
{
using (var sw = new StringWriter())
{
if (engine == null)
throw new InvalidOperationException("no engine");
// try to find the specified view
controller.TryValidateModel(model);
ViewEngineResult viewResult = engine.FindPartialView(controller.ActionContext, viewName);
// create the associated context
ViewContext viewContext = new ViewContext();
viewContext.ActionDescriptor = controller.ActionContext.ActionDescriptor;
viewContext.HttpContext = controller.ActionContext.HttpContext;
viewContext.TempData = controller.TempData;
viewContext.View = viewResult.View;
viewContext.Writer = sw;
// write the render view with the given context to the stringwriter
viewResult.View.RenderAsync(viewContext);
viewResult.EnsureSuccessful();
return sw.GetStringBuilder().ToString();
}
}
}
}

@ -0,0 +1,58 @@
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNet.Mvc;
using Microsoft.Data.Entity;
using Yavsc.Models;
using Yavsc.Models.Billing;
using Yavsc.ViewModels.Gen;
namespace Yavsc.ViewComponents
{
public class EstimateViewComponent : ViewComponent
{
ApplicationDbContext dbContext;
public EstimateViewComponent(ApplicationDbContext dbContext)
{
this.dbContext = dbContext;
}
public async Task<IViewComponentResult> InvokeAsync(long id, bool toPdf = false)
{
Estimate estimate =
dbContext.Estimates.Include(x => x.Query)
.Include(x => x.Query.Client)
.Include(x => x.Query.PerformerProfile)
.Include(x => x.Query.PerformerProfile.OrganizationAddress)
.Include(x => x.Query.PerformerProfile.Performer)
.Include(e => e.Bill).FirstOrDefault(x => x.Id == id);
if (estimate == null)
throw new Exception("No data");
if (toPdf)
{
string tex = null;
var oldWriter = ViewComponentContext.ViewContext.Writer;
using (var writer = new StringWriter())
{
this.ViewComponentContext.ViewContext.Writer = writer;
var resultTex = View("Estimate_tex", estimate);
resultTex.Execute(this.ViewComponentContext);
tex = writer.ToString();
}
ViewComponentContext.ViewContext.Writer = oldWriter;
return this.View("Estimate_pdf",
new PdfGenerationViewModel{
TeXSource = tex,
DestDir = Startup.UserBillsDirName,
BaseFileName = $"estimate-{id}"
} );
}
return this.View("Estimate_tex", estimate);
}
}
}

@ -0,0 +1,14 @@
using System.ComponentModel.DataAnnotations;
namespace Yavsc.ViewModels.Gen
{
public class PdfGenerationViewModel
{
[Required]
public string TeXSource { get; set; }
[Required]
public string BaseFileName { get; set; }
[Required]
public string DestDir { get; set; }
}
}

@ -39,6 +39,11 @@
<a asp-action="Index">@SR["Back to List"]</a> |
@{ var filenametex = $"estimate-{Model.Id}.tex" ;
var filenamepdf = $"estimate-{Model.Id}.pdf" ;}
<a href="~/do/@filenametex" >Export au format LaTeX</a>
<a href="~/do/@filenamepdf" >Export au format Pdf</a>
<a href="~/api/pdfestimate/gettex/@Model.Id" >Export au format LaTeX</a> |
<form action="~/api/pdfestimate/gen/@Model.Id" method="GET">
<input type="submit" value="Générer le Pdf"/>
</form>
</p>

@ -1,51 +0,0 @@
@using System.Reflection;
@using System.IO;
@using Microsoft.Extensions.WebEncoders;
@using System.Diagnostics;
@using System.Text;
@using Yavsc.Formatters;
@model Estimate
@{
if (ViewBag.TempDir==null) { throw new InvalidOperationException(); }
ViewBag.Pdf = "";
ViewBag.TeX = "";
var writer = new System.IO.StringWriter();
var content = await Html.PartialAsync("Estimate.tex", Model );
content.WriteTo(writer, new TexEncoder());
var contentStr = writer.ToString();
string name = $"estimate-{Model.Id}";
string fullname = new FileInfo(
System.IO.Path.Combine(ViewBag.TempDir,name)).FullName;
string ofullname = new FileInfo(
System.IO.Path.Combine(ViewBag.BillsDir,name)).FullName;
FileInfo fi = new FileInfo(fullname + ".tex");
FileInfo fo = new FileInfo(ofullname + ".pdf");
using (StreamWriter sw = new StreamWriter (fi.FullName))
{
sw.Write (contentStr);
}
if (!fi.Exists)
{
throw new Exception ("Source write failed");
}
using (Process p = new Process ()) {
p.StartInfo.WorkingDirectory = ViewBag.TempDir;
p.StartInfo = new ProcessStartInfo ();
p.StartInfo.UseShellExecute = false;
p.StartInfo.FileName = "/usr/bin/texi2pdf";
p.StartInfo.Arguments = $"--batch --build-dir=. -o {fo.FullName} {fi.FullName}";
p.Start ();
p.WaitForExit ();
if (p.ExitCode != 0) {
throw new Exception ("Pdf generation failed with exit code:" + p.ExitCode);
}
}
ViewBag.Success = fo.Exists;
fi.Delete();
var uri = $"~/api/pdfestimate/{Model.Id}";
}
<a href="@uri" >@uri</a>

@ -0,0 +1,50 @@
@using System.Reflection
@using System.IO
@using Microsoft.Extensions.WebEncoders
@using System.Diagnostics
@using System.Text
@using Yavsc.Formatters
@model Yavsc.ViewModels.Gen.PdfGenerationViewModel
@{
string errorMsg = null;
var billdir = Model.DestDir;
var tempdir = Startup.SiteSetup.TempDir;
string name = Model.BaseFileName;
string fullname = new FileInfo(
System.IO.Path.Combine(tempdir,name)).FullName;
string ofullname = new FileInfo(
System.IO.Path.Combine(billdir,name)).FullName;
FileInfo fi = new FileInfo(fullname + ".tex");
FileInfo fo = new FileInfo(ofullname + ".pdf");
using (StreamWriter sw = new StreamWriter (fi.FullName))
{
sw.Write (Model.TeXSource);
}
if (!fi.Exists)
{
errorMsg = "Source write failed";
}
else {
using (Process p = new Process ()) {
p.StartInfo.WorkingDirectory = tempdir;
p.StartInfo = new ProcessStartInfo ();
p.StartInfo.UseShellExecute = false;
p.StartInfo.FileName = "/usr/bin/texi2pdf";
p.StartInfo.Arguments = $"--batch --build-dir=. -o {fo.FullName} {fi.FullName}";
p.Start ();
p.WaitForExit ();
if (p.ExitCode != 0) {
errorMsg = $"Pdf generation failed with exit code: {p.ExitCode}";
}
}
fi.Delete();
}
ViewBag.GenSuccess = fo.Exists;
}
@if (ViewBag.GenSuccess) {
@($"{name}.pdf")
} else {
@errorMsg
<text>Something went wrong ...</text>
}

@ -1,6 +1,6 @@
@model Estimate
@using Yavsc.Helpers
@using System.Globalization
@model Estimate
@{
Layout = null;
var pro = Model.Query.PerformerProfile;
@ -10,8 +10,7 @@
var proaddr = Model.Query?.PerformerProfile.OrganizationAddress.Address;
var proaddrn = (proaddr!=null) ? proaddr.NewLinesWith("\\\\\n") : null ;
var proaddrm = (proaddr!=null) ? proaddr.NewLinesWith(" - ") : null ;
}
\documentclass[french,11pt]{article}
}\documentclass[french,11pt]{article}
\usepackage{eurosym}
\usepackage{babel}
\usepackage[T1]{fontenc}

@ -140,5 +140,6 @@
"postrestore": "echo after restoring packages",
"prepublish": "gulp min",
"postpublish": "echo \" . ./contrib/postPublish.sh # to push in prod.\""
}
},
"embed": "Views/**/*.cshtml"
}
Loading…