/// <summary> /// This method is used by both WS and Cloud implementations of AssembleDocument to convert an AssemblyResult to an AssembleDocumentResult. /// </summary> /// <param name="template">The template associated with the <c>AssemblyResult</c>.</param> /// <param name="asmResult">The <c>AssemblyResult</c> to convert.</param> /// <param name="docType">The type of document contained in the result.</param> /// <returns>An <c>AssembleDocumentResult</c>, which contains the same document as the <c>asmResult</c>.</returns> internal static AssembleDocumentResult ConvertAssemblyResult(Template template, AssemblyResult asmResult, DocumentType docType) { AssembleDocumentResult result = null; MemoryStream document = null; StreamReader ansRdr = null; List <NamedStream> supportingFiles = new List <NamedStream>(); // Create the list of pending assemblies. IEnumerable <Template> pendingAssemblies = asmResult.PendingAssemblies == null ? new List <Template>() : from pa in asmResult.PendingAssemblies select new Template( Path.GetFileName(pa.TemplateName), template.Location.Duplicate(), pa.Switches); for (int i = 0; i < asmResult.Documents.Length; i++) { switch (asmResult.Documents[i].Format) { case OutputFormat.Answers: ansRdr = new StreamReader(new MemoryStream(asmResult.Documents[i].Data)); break; case OutputFormat.JPEG: case OutputFormat.PNG: // If the output document is plain HTML, we might also get additional image files in the // AssemblyResult that we need to pass on to the caller. supportingFiles.Add(new NamedStream(asmResult.Documents[i].FileName, new MemoryStream(asmResult.Documents[i].Data))); break; default: document = new MemoryStream(asmResult.Documents[i].Data); if (docType == DocumentType.Native) { docType = Document.GetDocumentType(asmResult.Documents[i].FileName); } break; } } if (document != null) { result = new AssembleDocumentResult( new Document(template, document, docType, supportingFiles.ToArray(), asmResult.UnansweredVariables), ansRdr == null ? null : ansRdr.ReadToEnd(), pendingAssemblies.ToArray(), asmResult.UnansweredVariables ); } return(result); }
/// <summary> /// Assemble a document from the given template, answers and settings. /// </summary> /// <param name="template">An instance of the Template class.</param> /// <param name="answers"> /// Either an XML answer string, or a string containing encoded /// interview answers as posted from a HotDocs browser interview. /// </param> /// <param name="settings">An instance of the AssembleDocumentResult class.</param> /// <include file="../Shared/Help.xml" path="Help/string/param[@name='logRef']" /> /// <returns>An AssemblyResult object containing all the files and data resulting from the request.</returns> public AssembleDocumentResult AssembleDocument(Template template, TextReader answers, AssembleDocumentSettings settings, string logRef = "") { if (template == null) { throw new ArgumentNullException("template"); } using (var client = new HttpClient()) { AssembleDocumentResult adr = null; var packageTemplateLocation = (PackageTemplateLocation)template.Location; var packageId = packageTemplateLocation.PackageID; var of = ConvertFormat(settings.Format); var timestamp = DateTime.UtcNow; var hmac = HMAC.CalculateHMAC( SigningKey, timestamp, SubscriberId, packageId, template.FileName, false, logRef, of, settings.Settings); var urlBuilder = new StringBuilder().AppendFormat("{0}/assemble/{1}/{2}/{3}?" + "format={4}&" + "encodefilenames={5}&" + "billingref={6}{7}", HostAddress, SubscriberId, packageId, Uri.EscapeDataString(template.FileName), of, true, Uri.EscapeDataString(logRef), GetRetrieveFromHubParam()); if (settings.Settings != null) { foreach (var kv in settings.Settings) { urlBuilder.AppendFormat("&{0}={1}", kv.Key, kv.Value ?? ""); } } var request = new HttpRequestMessage { RequestUri = new Uri(urlBuilder.ToString()), Method = HttpMethod.Post }; request.Headers.Add("x-hd-date", timestamp.ToString("yyyy-MM-ddTHH:mm:ssZ")); request.Headers.Authorization = new AuthenticationHeaderValue("basic", hmac); var stringContent = answers == null ? new StringContent(string.Empty) : new StringContent(answers.ReadToEnd()); request.Content = stringContent; request.Content.Headers.TryAddWithoutValidation("Content-Type", "text/xml"); var result = client.SendAsync(request).Result; var parser = new MultipartMimeParser(); HandleStatusCode(result); var streamResult = result.Content.ReadAsStreamAsync().Result; var streams = new List <MemoryStream>(); using (var resultsStream = new MemoryStream()) { // Each part is written to a file whose name is specified in the content-disposition // header, except for the AssemblyResult part, which has a file name of "meta0.xml", // and is parsed into an AssemblyResult object. parser.WritePartsToStreams( streamResult, h => { var fileName = GetFileNameFromHeaders(h); if (fileName == null) { return(Stream.Null); } if (fileName.Equals("meta0.xml", StringComparison.OrdinalIgnoreCase)) { return(resultsStream); } var stream = new MemoryStream(); streams.Add(stream); return(stream); }, (new ContentType(result.Content.Headers.ContentType.ToString())).Boundary); if (resultsStream.Position <= 0) { return(null); } resultsStream.Position = 0; var serializer = new XmlSerializer(typeof(AssemblyResult)); var asmResult = (AssemblyResult)serializer.Deserialize(resultsStream); if (asmResult == null) { return(adr); } for (var i = 0; i < asmResult.Documents.Length; i++) { asmResult.Documents[i].Data = streams[i].ToArray(); streams[i].Dispose(); } adr = Util.ConvertAssemblyResult(template, asmResult, settings.Format); } return(adr); } }