Form Wizard Documentation

This is supplemental documentation for Form Wizard, an add on product for mojoPortal content management system.

Survey Demo

Form Wizard can be used to make forms and surveys.

Using Regular Expression Validation with Form Wizard Pro

Using Custom Form Submission Handlers

This section is primarily targeting developers who would like to extend the functionality of Form Wizard Pro with custom code to do additional processing of the submitted data after the form is submitted. Some experience with C# and Visual Studio is expected.

These are the basic steps involved in implementing a custom handler and plugging it in.

1. Create a Class Library project in Visual Studio, name it whatever you like, in this example I named it MyStuff, so MyStuff will be the default namespace and the class library will compile into an assembly named MyStuff.dll

2. Add a reference to the following:

  • sts.FormWizard.Business.dll
  • sts.FormWizard.Web.UI.dll
  • log4net.dll
  • mojoPortal.Business
  • System.Configuration

3. Create a class, the following example shows how to access the questions, answers, and any submitted files from code. The example just builds a string and then logs it to the mojoPortal log, but you could implement whatever logic you need to do whatever you need to do with the form data. In comments is  example code for sending an email with the submitted files attached.

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
using log4net;
using sts.Business;
using mojoPortal.Business;
namespace MyStuff
{
public class MyCustomFormSubmissionHandlerProvider : FormSubmissionHandlerProvider
{
private static readonly ILog log = LogManager.GetLogger(typeof(MyCustomFormSubmissionHandlerProvider));
public MyCustomFormSubmissionHandlerProvider()
{ }
public override void FormSubmittedEventHandler(object sender, FormSubmissionEventArgs e)
{
if (e == null) return;
if (e.ResponseSet == null) return;
log.Info("MyCustomFormSubmissionHandlerProvider called");
StringBuilder results = new StringBuilder();
results.Append(e.DetailUrl);
results.Append("\r\n");
results.Append("\r\n");
//how to get the site user if the user was authenticated
if (e.User != null)
{
results.Append("submitted by user " + e.User.Name);
results.Append("\r\n");
}
//how to get the questions and answers
List<WebFormQuestion> questionList = WebFormQuestion.GetByForm(e.ResponseSet.FormGuid);
List<WebFormResponse> responses = WebFormResponse.GetByResponseSet(e.ResponseSet.Guid);
foreach (WebFormQuestion question in questionList)
{
if (question.QuestionTypeId == 8) { continue; } //skip instruction block
string response = GetResponse(e.ResponseSet.Guid, question.Guid, responses);
results.Append("\r\n" + question.QuestionText + "\r\n");
results.Append(response);
results.Append("\r\n");
}
// how to get the information about submitted files
if (e.Config.NotificationIncludeFiles && e.ResponseSet.UploadCount > 0)
{
results.Append("\r\n");
List<FileAttachment> attachments = FileAttachment.GetListByItem(e.ResponseSet.Guid);
string[] attachmentPaths = new string[attachments.Count];
string[] attachmentNames = new string[attachments.Count];
int i = 0;
foreach (FileAttachment a in attachments)
{
string downloadPath = e.AttachmentBaseBath + a.ServerFileName;
if (File.Exists(downloadPath))
{
attachmentPaths[i] = downloadPath;
attachmentNames[i] = a.FileName;
results.Append("submitted file " + a.FileName + " stored on disk as " + downloadPath);
results.Append("\r\n");
}
i += 1;
}
}
// how to send an email with the results and file attachments
// you could get settings from config settings if you don't want to hard code it
// you would need references to mojoPortal.Web.dll, mojoPortal.Business.dll, and mojoPortal.Net.dll
// to use this commented code
//string fromAddress = "noreply@yoursite.com";
//string emailTo = "youradddress@yoursite.com";
//string subject = "test";
//Email.Send(
//        SiteUtils.GetSmtpSettings(),
//        fromAddress,
//        string.Empty,
//        emailTo,
//        string.Empty,
//        string.Empty,
//        subject,
//        results.ToString(),
//        false,
//        Email.PriorityNormal,
//        attachmentPaths,
//        attachmentNames);
log.Info(results.ToString());
}
private string GetResponse(Guid responseSetGuid, Guid questionGuid, List<WebFormResponse> responses)
{
foreach (WebFormResponse response in responses)
{
if (
(response.ResponseSetGuid == responseSetGuid)
&& (response.QuestionGuid == questionGuid)
)
{
return response.Response;
}
}
return string.Empty;
}
}
}

4. Compile your class library into a dll and put it in the /bin folder of your mojoPortal site.

5. Next we create a configuration file that plugs the custom handler in so it will be available for Form Wizard Pro. Create a text file named MyStuff.config as follows, and put it in the /FormWizard/SubmissionHandlers folder


<?xml version="1.0" encoding="utf-8" ?>
<FormSubmissionHandlers>
<providers>
  <add name="MyFormSubmissionHandler"
    type="MyStuff.MyCustomFormSubmissionHandlerProvider, MyStuff"
    description="This is my custom form submission handler " />
</providers>
</FormSubmissionHandlers>

6. Finally in the settings for an instance of Form Wizard Pro, you will see your handler in the dropdown list for "Submission Event Handler". Set your handler as the handler for the form and it will be invoked whenever the form is completed.

FormWizard 2.7 Skin Compatibility

The editing of form fields (questions) has changed for Form Wizard 2.7. The controls for each form field now shows up in a pop-up modal. This keeps the visual page size or viewport from changing as the form is edited. It also allows the person editing the form to more easily see which field they are editing.

The modals provided by jQueryUI are used for Non-Bootstrap skins. For the sake of this document, Artisteer skins are considered Non-Bootstrap skins. 

CSS

    .draghandle { 
text-align: center; 
background-image: none;
} 
.draghandle::before { 
content: "≡";
font-size: 30px;
line-height: 0.8;
}
button[class^="fw-option-"] {
display: block;
} 

theme.skin

<%@ Register Namespace="sts.FormWizard.Web.UI" Assembly="sts.FormWizard.Web.UI" TagPrefix="stsfw" %>
    <stsfw:FormWizardDisplaySettings runat="server" 
UseHtml5SliderControl="true" 
SaveQuestionProgressHtml="<span class='fa fa-spinner fa-spin'></span><i>Saving...</i>" 
InstructionBlockFormat="<div class='settingrow fw-instructions'>{0}</div>" 
OptionUpButtonCssClass="fw-option-up ui-button" 
OptionDownButtonCssClass="fw-option-down ui-button" 
OptionEditButtonCssClass="fw-option-edit ui-button" 
OptionDeleteButtonCssClass="fw-option-delete ui-button" 
OptionAZSortButtonCssClass="fw-option-sort-az ui-button" 
OptionZASortButtonCssClass="fw-option-sort-za ui-button" 
OptionResetSortButtonCssClass="fw-option-sort-reset ui-button" 
OptionUpButtonInnerHtml="<span class='ui-icon ui-icon-triangle-1-n'></span>" 
OptionDownButtonInnerHtml="<span class='ui-icon ui-icon-triangle-1-s'></span>" 
OptionEditButtonInnerHtml="<span class='ui-icon ui-icon-pencil'></span>" 
OptionDeleteButtonInnerHtml="<span class='ui-icon ui-icon-trash'></span>" 
OptionAZSortButtonInnerHtml="<span class='ui-icon ui-icon-arrowthickstop-1-n'></span>" 
OptionZASortButtonInnerHtml="<span class='ui-icon ui-icon-arrowthickstop-1-s'></span>" 
OptionResetSortButtonInnerHtml="<span class='ui-icon ui-icon-shuffle'></span>" 
QuestionEditButtonCssClass="ui-button" 
QuestionEditButtonInnerHtml="<span class='ui-icon ui-icon-pencil' title='{0}'></span>" 
QuestionDeleteButtonCssClass="ui-button" 
QuestionDeleteButtonInnerHtml="<span class='ui-icon ui-icon-trash' title='{0}'></span>" 
QuestionExportButtonCssClass="ui-button" 
QuestionExportButtonInnerHtml="<span class='ui-icon ui-icon-extlink' title='{0}'></span>" 
QuestionEditorModalTopMarkup="<div id='qemodal' title='{0}' style='clear: both;; display: none;'><h3>{0}</h3>" 
QuestionEditorModalBottomMarkup="</div>" 
QuestionEditorModalOpenScript="$(function () { $.colorbox({inline: true, href: '#qemodal', width: 'auto', height: 'auto', onOpen: function() {$('#aspnetForm').append($('#cboxOverlay'));$('#aspnetForm').append($('#colorbox'));$('#qemodal').css('display', '');}, onCleanup: function(){ $('#qemodal').hide(); ReloadQuestions();} }); });" 
RenderFieldset="false"
ReorderItemCssClass="fwp-reorder-list__item panel panel-default panel-body"
ReorderTemplateCssClass="fwp-reorder-list__template panel panel-default panel-body"
/> 
<portal:BasePanel runat="server" SkinID="FWPOptionControls" CssClass="floatpanel" />

Retrieving Form Submissions with SQL

Sometimes a site owner may want to retreive form submissions directly from SQL Server. The script below will create a stored procedure which takes a single parameter (@ModuleId).

DECLARE  
      @ModuleId int = 8018,
      @params NVARCHAR(MAX),
      @columns NVARCHAR(MAX) = N'',
      @sql NVARCHAR(MAX); 
   
  SELECT @columns += N', p.' + QUOTENAME([QuestionText]) 
  FROM ( 
      SELECT [QuestionText], [SortOrder]
      FROM [sts_WebFormQuestion] q
      LEFT JOIN [sts_WebForm] form ON [form].[Guid] = [q].[FormGuid]
      WHERE [form].[ModuleId] = @ModuleId
      AND [QuestionTypeId] != 8
  ) AS x ORDER BY [SortOrder]; 
   
  SET @sql = N' 
  SELECT [Guid] AS [SubmissionId], [CreatedUtc] AS [SubmissionDate],' + STUFF(@columns, 1, 2, '') + ' 
  FROM ( 
   
          SELECT [rs].[Guid], [QuestionText], [Response], [rs].[CreatedUtc]
          FROM [sts_WebFormResponseSet] rs
          LEFT JOIN [sts_WebFormResponse] r ON r.[ResponseSetGuid] = rs.[Guid]
          LEFT JOIN [sts_WebFormQuestion] q ON q.[Guid] = r.[QuestionGuid]
          LEFT JOIN [sts_WebForm] form ON form.[Guid] = rs.[FormGuid]
          WHERE [form].[ModuleId] = @ModuleId
          AND [QuestionTypeId] != 8 
  ) AS j PIVOT ( 
       MAX([Response]) FOR [QuestionText] 
       IN (' + STUFF(REPLACE(@columns, ', p.[', ',['), 1, 1, '') + ') 
  ) AS p' 
   
  SET @params = N'@ModuleId int'; 

EXEC sp_executesql @sql, @params, @ModuleId;