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.
- Edit the HomeController class in the Controllers folder.
- 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,
- In Visual Studio, expand the current project folder.
- Right-click the Models folder and select Add > Class. The Add New Item dialog appears.
- Select Class from the list and enter InterviewModel.cs in the Name field.
- 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:
- 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)
- Press F5 to run the MVC application. The browser opens.
- 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:
v
ar 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:
- Set the current project as the Startup Project (Right-click the SdkExample4InterviewDisplay project, select Startup Project from the drop-down menu)
- Press F5 to run the Project. The interview loads in the browser.
- When the interview has finished loading, enter answer data into the interview fields.
- 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>