SDK Example 4 - Display the Interview and process answers

In Example 3, you examined retrieving the interview HTML fragment. The next step is to display the interview to users, collect the answer data created by the users when they finish the interview, and processing the collected answers.

In this example you will:

  • Create a new MVC project in Visual Studio
  • Retrieve a HotDocs Interview HTML fragment
  • Use the HTML fragment to display a HotDocs Interview to the user
  • Create a new method to handle processing of answers after the interview is completed

Full source code for this example is available at the bottom of the page.

Example Source Code on GitHub

The SdkExample4InterviewDisplay example project is available on GitHub, in the HotDocs-Samples repository.

1. Create a new MVC Project in Visual Studio

Before you start, create a new MVC Application project for this example. Name this new project SdkExample4InterviewDisplay. You will gradually add code to this project until you have a working example.

2. Retrieve the Interview HTML Fragment

Once a new MVC project has been created, the next step is to retrieve the interview fragment, as you did in Example 3.

  1. Edit the HomeController class in the Controllers folder.
  2. In the Index ActionResult method, use the GetInterviewFragment created in Example 3 to get the interview fragment.

The Index ActionResult currently looks as follows:

public ActionResult Index() {     var interviewFragment = GetInterviewFragment(); }

Before using the method, you will need to expand it. Next, you will create a ViewModel to which you can pass the interview fragment.

3. Create a new Interview ViewModel

In MVC, a ViewModel structures the data used to populate the content on a page.  

3.1 Create the ViewModel

To create a new ViewModel,

  1. In Visual Studio, expand the current project folder.
  2. Right-click the Models folder and select Add > Class. The Add New Item dialog appears.
  3. Select Class from the list and enter InterviewModel.cs in the Name field.
  4. Click Add. A new Class is added to the Models folder. This will become our new ViewModel.

The next step is to define the data that is passed to the page.

3.2 Define the ViewModel data

In the InterviewModel created above, you now need to include the Interview fragment, so it can be displayed in the view. As the Interview Fragment is a string of HTML, add a new string property to the model named InterviewFragment:

public string InterviewFragment { get; set; }

The ViewModel is now ready to use in the View.

3.3 Pass the Interview HTML Fragment to a View

Return to the HomeController. You can now add the interview fragment to the model:

var model = new InterviewModel { InterviewFragment = interviewFragment };

Once the model has been created and data assigned to it, you can pass it to the View:

return View(model);

The entire Index method looks like this:

public ActionResult Index() {     var interviewFragment = GetInterviewFragment();     var model = new InterviewModel { InterviewFragment = interviewFragment };     return View(model); }

4. Display the HotDocs Interview in the View

A View for the application was automatically created when the MVC project was created. In the Project, navigate to Views > Home and edit Index.cshtml. It is this page to which you will add the interview.

4.1 Add the View Model Reference

At the top of the page, add a reference to the ViewModel:

@model SdkExample4InterviewDisplay.Models.InterviewModel

This will allow us to use the data contained within the ViewModel elsewhere on the page.

4.2 Insert the Interview HTML Fragment

To insert the HTML fragment, add the following line to the page:

@Html.Raw(Model.InterviewFragment)

This line of code is comprised of two items:

  • Model.InterviewFragment the reference to the interview fragment data that you added to the model in step 3.3. This is the HTML that is passed to the page to embed the interview.
  • Html.Raw a MVC HtmlHelper method that ensures that the characters in the HTML fragment are not encoded, so the interview renders correctly.

5. Add a GetInterviewFile method

Before you can run the interview, you need to supply a method that will return any files required by the interview as it loads. This method will return a FileContentResult, a .Net MVC class that sends a binaryfile as a response to a request.

5.1 Create the GetInterviewFile method

Create a new method, GetInterviewFile, to return a FileStreamResult:

public FileStreamResult GetInterviewFile() {   }

5.2 Get Data from the Query String

When a HotDocs interview calls this method, it also passes back data which you can use to retrieve the correct files. We need to use three items of information to retrieve the files:

  • Template location  the location of the template. In this example, the template will be stored on disk.
  • Filename the name of the template you need to locate.
  • File Type the type of file you need to locate. This could be the text template, component file, or other file.

5.2.1 Get the Template Location

Get the template location from the query string using the loc parameter:

var templateLocationData = Request.QueryString["loc"];

The data for location is encoded when it return from the interview. To convert it into a format that you can use later in the method, use the Template object's Locate method:

var templateLocation = Template.Locate(templateLocationData);

5.2.2 Get the filename

Next, get the file name of the template currently loaded by the interview using the template parameter:

var fileName = Request.QueryString["template"];

5.2.3 Get the file type

Finally, get the type of the requested file using the type parameter:

var fileType = Request.QueryString["type"];

When you use the file type later in the method, you need to use the fileType in several comparisons. To make this easier, convert the fileType to lowercase:

fileType = fileType.ToLower();

5.3 Get the Interview Stream

Create the HotDocs Service, using the CreateHotDocsService method from previous examples. We will use the service to retrieve the Interview File.

var service = CreateHotDocsService();

Next, with the service's GetInterviewFile method, you can retrieve the Interview File Stream using the data from the query string:

var interviewFileStream = service.GetInterviewFile(templateLocation, fileName, fileType);

5.4 Get the MIME Type of the file

When retrieving the FileStreamResult, you must supply the MIME type of the file, in addition to the interview contents from the previous step. First, get the name of the output file:

var outputFileName = fileType == "img" ? fileName : fileName + "." + fileType;

If the file is an image, you do not need to append the file type. Often, HotDocs will request a JavaScript file for the template, which necessitates adding a file type of 'js' to the end of the file name.

Using the output file name and a condition to check if only image files have been requested, get the MIME type using the GetMimeType utility method:

var interviewResultMimeType = Util.GetMimeType(outputFileName, fileType == "img");

Now that the MIME type has been retrieved, you can create the FileStreamResult.

5.5 Retrieve the FileContentResult

We can now retrieve and return the requested file, by creating a new FileStreamResult and passing in the interview stream and MIME type :

var interviewFileContentResult = new FileStreamResult(interviewFileStream, interviewResultMimeType);

The full method, with additional comment text, looks like this:

public FileStreamResult GetInterviewFile() {     //File name of template or image     var fileName = Request.QueryString["template"];     //Template location     var templateLocationData = Request.QueryString["loc"];     var templateLocation = Template.Locate(templateLocationData);     //Type of file requested     var fileType = Request.QueryString["type"];     fileType = fileType.ToLower();     //Get interview stream     var service = CreateHotDocsService();     var interviewFileStream = service.GetInterviewFile(templateLocation, fileName, fileType);     //Get MIME type     var outputFileName = fileType == "img" ? fileName : fileName + "." + fileType;     var interviewResultMimeType = Util.GetMimeType(outputFileName, fileType == "img");     var interviewFileContentResult = new FileStreamResult(interviewFileStream, interviewResultMimeType);     return interviewFileContentResult; }

6. Test

To test the interview:

  1. Set the current project as the Startup Project (right-click the SdkExample4InterviewDisplay project in the Solution Explorer and select Startup Project from the drop-down menu)
  2. Press F5 to run the MVC application. The browser opens.
  3. When the browser opens, a HotDocs interview appears.

If you click the Finish button on the interview, an error page appears, as you have not yet created a method to handle the post-assembly action. You will do this in the next step.

7. Create a new Post-interview Processing Action Result

Now that the interview is working, you need to create a new method to handle the answer data posted back when an interview finishes. Generally, a post-interview processing method will need to handle at least the first two of the following:

  • Retrieve the answer data
  • Assemble a document using the answer data
  • Save the answer data

When you create the HotDocs InterviewSettings, one of the parameters is a post-interview URL. In the example above, on clicking Finish, the interview looks for Home/PostInterviewProcessing, which results in the Interview posting back to the HomeController, looking for a method, PostInterviewProcessing. In this example you create this new method and implement answer data retrieval and document assemby.

7.1 Create the PostInterviewProcessing Method

First, create a new ActionResult method, PostInterviewProcessing:

[HttpPost] public ActionResult PostInterviewProcessing() {   }

This will handle the data send back from the interview. Note the HttpPost attribute added before the method definition. This is added so that the ActionResult will accept POST requests, as you are receiving data that is posted back from the interview.

7.2 Retrieving the answer data

7.2.1 Encoded Answers

When the users finishes an interview, the answers they entered into the interview are posted back to the application. We can access the Request.Form and retrieve the encoded answers as below, using the InterviewResponse.GetAnswers method:

var encodedAnswers = InterviewResponse.GetAnswers(Request.Form);

7.2.2 Get Decoded Answers

If you need to retrieve answers as a string, you can also get decoded answers from the interview using the InterviewAnswerSet.GetDecodedInterviewAnswers method, like this:

var newAnswers = InterviewAnswerSet.GetDecodedInterviewAnswers(new StringReader(encodedAnswers)); var newAnswersAsString = newAnswers.ReadToEnd();

7.2.3 Get All Answers, including Round-tripped

To retrieve all answers, including round-tripped answers, you will need to use the HotDocs Service.

var service = CreateHotDocsService(); var allAnswers = service.GetAnswers(new[] { new StringReader(encodedAnswers) }, "logref");

The final method looks like this:

[HttpPost] public ActionResult PostInterviewProcessing() {     var encodedAnswers = InterviewResponse.GetAnswers(Request.Form);     AssembleDocument(encodedAnswers);     var newAnswers = InterviewAnswerSet.GetDecodedInterviewAnswers(new StringReader(encodedAnswers));     var newAnswersAsString = newAnswers.ReadToEnd();      var service = CreateHotDocsService();     var allAnswers = service.GetAnswers(new[] { new StringReader(encodedAnswers) }, "logref");     return null; }

Test

To run the project and test the interview post-back:

  1. Set the current project as the Startup Project (Right-click the SdkExample4InterviewDisplay project, select Startup Project from the drop-down menu)
  2. Press F5 to run the Project. The interview loads in the browser.
  3. When the interview has finished loading, enter answer data into the interview fields.
  4. Click Finish. The Interview finishes. An completed document is written to C:\temp\output.docx.

Example Source Code (C#)

In the HomeController:

using System; using System.Collections.Generic; using System.IO; using System.Web.Mvc; using HotDocs.Sdk; using HotDocs.Sdk.Server; using HotDocs.Sdk.Server.Local; using SdkExample4InterviewDisplay.Models; using Util = HotDocs.Sdk.Util;   namespace SdkExample4InterviewDisplay.Controllers {     /// <summary>     /// This controller displays an interview and shows how to process the answers returned upon completion     /// </summary>     public class HomeController : Controller     {         public ActionResult Index()         {             var interviewFragment = GetInterviewFragment();             var model = new InterviewModel { InterviewFragment = interviewFragment };             return View(model);         }           private static string GetInterviewFragment()         {                         var template = CreateTemplate();             var interviewSettings = GetInterviewSettings();             var service = CreateHotDocsService();             var interview = service.GetInterview(template, null, interviewSettings, null, "logref");             return interview.HtmlFragment;         }           private static InterviewSettings GetInterviewSettings()         {             string postInterviewUrl = "Home/PostInterviewProcessing";             string interviewRuntimeUrl = "http://localhost/HDServerFiles/js";             string interviewStylesheetUrl = "http://localhost/HDServerFiles/stylesheets";             string interviewFileUrl = "Home/GetInterviewFile";               var interviewSettings = new InterviewSettings(postInterviewUrl, interviewRuntimeUrl, interviewStylesheetUrl, interviewFileUrl) { RoundTripUnusedAnswers = true };             return interviewSettings;         }           [HttpPost]         public ActionResult PostInterviewProcessing()         {             //Retrieve encoded answers             var encodedAnswers = InterviewResponse.GetAnswers(Request.Form);                          //If you need to parse new answers out of result but not roundtripped ones then             var newAnswers = InterviewAnswerSet.GetDecodedInterviewAnswers(new StringReader(encodedAnswers));             var newAnswersAsString = newAnswers.ReadToEnd();               //If you need all answers including roundtripped             var service = CreateHotDocsService();             var allAnswers = service.GetAnswers(new[] { new StringReader(encodedAnswers) }, "logref");               //If no need to use answers outside of assembly then just assemble using encoded ones             AssembleDocument(encodedAnswers);             return null;         }           /// <summary>         /// This is called repeatedly by the interview JS to retrieve the files needed for the interview         /// </summary>         /// <returns></returns>         public FileStreamResult GetInterviewFile()         {             //File name of template or image             var fileName = Request.QueryString["template"];                      //Template location             var templateLocationData = Request.QueryString["loc"];             var templateLocation = Template.Locate(templateLocationData);               //Type of file requested             var fileType = Request.QueryString["type"];             fileType = fileType.ToLower();               //Get interview stream                         var service = CreateHotDocsService();             var interviewFileStream = service.GetInterviewFile(templateLocation, fileName, fileType);               //Get MIME type                                       var outputFileName = fileType == "img" ? fileName : fileName + "." + fileType;             var interviewResultMimeType = Util.GetMimeType(outputFileName, fileType == "img");               var interviewFileStreamResult = new FileStreamResult(interviewFileStream, interviewResultMimeType);             return interviewFileStreamResult;         }           private static Template CreateTemplate()         {             const string packagePath = @"C:\temp\HelloWorld.hdpkg";             string packageId = Guid.NewGuid().ToString();             var templateLocation = new PackagePathTemplateLocation(packageId, packagePath);               var template = new Template(templateLocation);             return template;         }           private static Services CreateHotDocsService()         {             const string tempDirectoryPath = @"C:\temp\";             var service = new Services(tempDirectoryPath);             return service;         }           private static void AssembleDocument(string encryptedAnswers)         {             var service = CreateHotDocsService();             var template = CreateTemplate();             var assembleDocumentSettings = new AssembleDocumentSettings();               using (var answers = new StringReader(encryptedAnswers))             {                 var assembledDocumentResult = service.AssembleDocument(template, answers, assembleDocumentSettings, "logref");                 using (var fileStream = System.IO.File.Create(@"c:\temp\output" + assembledDocumentResult.Document.FileExtension))                 {                     assembledDocumentResult.Document.Content.CopyTo(fileStream);                 }             }         }           private static AnswerCollection CreateAnswerCollection()         {             var answerCollection = new AnswerCollection();               //Text answers             var answer = answerCollection.CreateAnswer<TextValue>("TextExample-t");             answer.SetValue<TextValue>("World");               return answerCollection;         }     } }

In the Index View:

@model SdkExample4InterviewDisplay.Models.InterviewModel

<div>

    @Html.Raw(Model.InterviewFragment)

</div>