Forms - 2 Step Process

Posted by Community Admin on 04-Aug-2018 16:20

Forms - 2 Step Process

All Replies

Posted by Community Admin on 11-May-2015 00:00

Is it possible to create a form that when submitted goes to another web page with a larger form, but completes the details already submitted if that makes sense?  At sort of two stage form?

Posted by Community Admin on 14-May-2015 00:00

Hi David,

You can try implement such functionality using the Forms events.

Subscribing for IFormEntryCreatedEvent you can redirect to a page you need and you have the information filled-in in for the form entry created.

I hope this helps.

Regards,
Svetoslav Manchev
Telerik

 
Do you want to have your say in the Sitefinity development roadmap? Do you want to know when a feature you requested is added or when a bug fixed? Explore the Telerik Sitefinity CMS Ideas&Feedback Portal and vote to affect the priority of the items
 

Posted by Community Admin on 15-May-2015 00:00

Another approach would be to register the RadTabStrip and RadMultiPage as form controls. Using this approach you would create one form that spans multiple tabs. This is something I have read about but never tried. However, you can find more information from the links below. 

http://www.sitefinity.com/developer-network/forums/general-discussions-/forms-creation-customization-of-complex-forms​

http://www.sitefinity.com/developer-network/forums/developing-with-sitefinity/radtabstrip-and-radmultipage

Posted by Community Admin on 15-May-2015 00:00

I have not try this myself...but, plan to.

It's by Tim Williamson. See here: github.com/.../timw255.Sitefinity and take a look at the video too.

Posted by Community Admin on 15-May-2015 00:00

If you are unable to get the RadTabStrip and RadMultiPage to work in form designer you could also build a widget in Visual Studio that save​ data to the Sitefinity form.  I suspect this approach will be easier to implement. For an example see below:

http://blog.falafel.com/forms-in-sitefinity/​

 

 

Posted by Community Admin on 15-Jul-2015 00:00

Hi Svetoslav,

 

Still struggling with the best way to achieve this in Sitefinity. 

I think in form1 I need a custom submit button that will redirect to the page with form2 on it and add the datafields from form1 in a querystring.

for form2 I either need to subscribe to the IFormEntryCreatedEvent and populate the fields from the querystring, or create a custom form and use it's initialise function to populate the fields form the query string.

Given the forms are already created what is the best (i.e. least amount of coding) required to get these forms to talk to one another.  And should I even be using Querystring for this?

Posted by Community Admin on 15-Jul-2015 00:00

@David

Can you elaborate a bit?

Do you need to STORE the data from Form 1, or are you just looking for like "Step 1" of a multipage form?

Like you could use a Wizard to do a multi-step and save the results back to an SF Form

Or if you want to do the querystring thing, you should be putting the FormId in the querystring and loading the resulting data onto Form 2, not literally passing the data fields themselves.

 Can you just give me more details?

Posted by Community Admin on 16-Jul-2015 00:00

Hi Steve,

 Yes I do want to store the data from Form 1, which is why I would like to use a Sitefinity form.  Basically it asks the house number and postcode of a property on the home page step 1, which then should redirect to Form 2 fill in the house number and postcode and then they have to fill in name, numbers, and other details.  I believe some people might give up at Form 2, so I want to keep the data in Form 1 for marketing and analysis.

Posted by Community Admin on 16-Jul-2015 00:00

Okay, if it was ME....I would make a single form to store all the data

Basically the backend form designer is pointless, just a shell to store the data.  So put all the fields in there you want, maybe even prefix with like Form1 or Form2 on the developer names.

So then you make a custom widget that would allow someone to fill in the few first fields, on submit save that data into the SF form, maybe have a custom field there to store a hash or token to access the form (so you're not exposing the ID).

So then if the page loads with this token (a valid one) in the querystring, then load up page 2, and on save fill in the rest of the data to the form...make sense?

 Steve

Posted by Community Admin on 16-Jul-2015 00:00

So I would have Widget 1 on the home screen (that would write partial data to a form), then Widget 2 on the contact page that would load the data if a token was passed in the query string.

That's certainly an option.  It might be me, but I end up feeling that Sitefinity just becomes a set of database routines.  It felt great creating the contact form in 2 minutes in the designer, but now because I want some quite ordinary behaviour in the web world, I'm pretty much writing ASP.net from scratch, with database helpers?  Perhaps I'm being too negative?

Posted by Community Admin on 16-Jul-2015 00:00

My way is just one way...only because I'd want to keep all the form data together.  You can certainly just MAKE 2 forms in the backend, and then override the default forms widget to extend it with any extra functionality you'd need (like checking a querystring to load stuff in).

You're talking about something even more advanced than a 2 page form right, that's not something supported out of the box.  I can hack a 2 page form in with layouts and javascript, but this would take just a bit of code.

You are being too negative, it's not that bad! :)

Posted by Community Admin on 16-Jul-2015 00:00

I think the simplest approach (at least in my mind) is to use the asp.net MultiView control and build one widget ​containing both forms.  What makes this simpler is that you don't have to pass values from one page to the other.  In the codebehind you can access the form controls from both views, even while a view is inactive. When the ActiveViewChanged event is called you get the values you want from Form1 and assign them to the controls you want in Form2.  This also gives you the option to not save to the database until the save button on Form2 is clicked. The Form1 save button would merely change the active view. An event handler would than populate Form2 from Form1. 

Your'e gripes are legitimate but apply to all content management systems. 

 

https://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.multiview.activeviewchanged(v=vs.100).aspx

 

http://www.beansoftware.com/ASP.NET-Tutorials/MultiView-Control.aspx

Posted by Community Admin on 16-Jul-2015 00:00

@ch Forms are on separate pages though

Posted by Community Admin on 16-Jul-2015 00:00

@Steve, my bad Form1 is on the home page and Form2 is on the contact page.

Posted by Community Admin on 17-Jul-2015 00:00

I think I'm going to go down the event route, and see if I can add any field with a QS in it's name to a query string.

One thing I also need is a hidden field to say which page should be redirected to and to add the querystring.

The documentation seem to indicated there are ReadOnly and HiddenModes for form controls in the advanced section, but I can't find them.  Is this version specific, I'm using 7?

docs.sitefinity.com/for-developers-make-a-field-in-a-form-response-hidden-or-read-only-on-the-frontend

 

Posted by Community Admin on 20-Jul-2015 00:00

OK so I've used the event (see below) to build a query string and redirect.  Now, how do I read these values in form2.  Is there an event which renders the form Svetoslav?

public static void FormsEventHandler(IFormEntryCreatedEvent eventInfo)

var controls = eventInfo.Controls;
string qs="";
var redirect = eventInfo.Controls.Where(c => c.FieldName == "Redirect").FirstOrDefault();
if (redirect!=null)
//get page to redirect to
String url = redirect.Title;
//build query string
var qsControls = eventInfo.Controls.Where(c => c.FieldName !=null && c.FieldName.Left(2) == "QS");
foreach(var control in qsControls)

qs += control.FieldName+"="+control.Value+"&";


//Do the redirect
HttpContext.Current.Response.Redirect(url+"?"+qs, true);

Posted by Community Admin on 20-Jul-2015 00:00

@David

Override the default form control\widget...then add your custom version to the toolbox to drop on the page. ​You now have full access to the form\widget.  It has internal events to use.

 IMO, the above is rife with problems...injection, and no encoding on the values...like I said above, if you pass just the ID, you can then safely re-load the data in the inherited control.

Posted by Community Admin on 22-Jul-2015 00:00

Thanks, Steve, I think if I was writing this again I would go down that route, now I've got my head around the whole process.  I've added some url encode/decode and a quick check for special chars, etc.  Albeit the opportunity for injection doesn't seem to great as I'm not calling anything from the database?  I've attach the pasted the files below in case it helps anyone, it would be nice to have this kind of 2 step form process built in.  Probably to seasoned asp.net devs this is no problem, but to me it felt like a lot of work to achieve relatively little.

 

formsevents.cs

using System;
using System.Linq;
using System.Web;
using Telerik.Sitefinity.Abstractions;
using Telerik.Sitefinity.Data;
using Telerik.Sitefinity.Services;
using Telerik.Sitefinity.Modules.Forms.Events;
namespace SitefinityWebApp.Events

public class FormEvents

public static void Start()

Bootstrapper.Initialized += new EventHandler<ExecutedEventArgs>(Bootstrapper_Initialized);

public static void Bootstrapper_Initialized(object sender, ExecutedEventArgs args)

if (args.CommandName == "Bootstrapped")

EventHub.Subscribe<IFormEntryCreatedEvent>(evt => FormsEventHandler(evt));


public static void FormsEventHandler(IFormEntryCreatedEvent eventInfo)

var controls = eventInfo.Controls;
string qs="";
var redirect = eventInfo.Controls.Where(c => c.FieldName == "Redirect").FirstOrDefault();
if (redirect!=null)
//get page to redirect to
String url = redirect.Title;
//build query string
var qsControls = eventInfo.Controls.Where(c => c.FieldName !=null && c.FieldName.Left(2) == "QS");
foreach(var control in qsControls)

qs += control.FieldName+"="+HttpUtility.UrlEncode((string)control.Value)+"&";


//Do the redirect
HttpContext.Current.Response.Redirect(url+"?"+qs, true);



 smartformscontrol.cs

using System;
using System.Linq;
using System.Web;
using Telerik.Sitefinity.Modules.Forms.Web.UI;
using Telerik.Sitefinity.Web.UI.Fields;
using System.Text.RegularExpressions;
namespace SitefinityWebApp.Widgets.SmartFormsControl

public class SmartFormsControl : FormsControl

protected override void OnInit(EventArgs e)

base.OnInit(e);
this.Page.PreRenderComplete += new EventHandler(this.Page_PreRenderComplete);

protected void Page_PreRenderComplete(object sender, EventArgs e)

this.PrepopulateForm();

protected void PrepopulateForm()

var request = HttpContext.Current.Request;
foreach (String key in request.QueryString.AllKeys)

if (key != null && key.Left(2)=="QS")

// Check key contains only lower case or upper case letters or numbers,
// the apostrophe, a dot, or white space. Also check it is
// between 0 and 40 characters long
if (!Regex.IsMatch(HttpUtility.UrlDecode(key), @"^[a-zA-Z0-9'.\s]0,40$") || !Regex.IsMatch(HttpUtility.UrlDecode(request.QueryString[key]), @"^[a-zA-Z0-9'.\s]0,40$")) break;
FieldControl ctrl = (FieldControl)this.FieldControls.Where(c => c.MetaField.FieldName == key).FirstOrDefault();
if (ctrl != null)


ctrl.Value = HttpUtility.UrlDecode(request.QueryString[key]);






Posted by Community Admin on 22-Jul-2015 00:00

Oh, and still haven't figured out how to hide a field in a form yet, anyone?

Posted by Community Admin on 23-Jul-2015 00:00

Instead of overriding the Form Widget you might find it simpler to create a user control in Visual Studio. That is the approach I have used in the past. The following article and documentation explains how:

 http://blog.falafel.com/forms-in-sitefinity/

http://docs.sitefinity.com/for-developers-create-form-responses

I will now describe how I would modify the widget created in Falafels tutorial to give you the same functionality you are after. 

In this example the Falafel form will be split in 2. The first form will collect and save just the user's name and email. The second form will collect and save the user's name, email and message. In addition the second form will pre-populate with the values collected in the first form.

The first step is to create both forms in Sitefinity's Form Designer.  Don't forget form1 contains just 2 of the fields and form2 contains all of the fields.

Form1.ascx

<div class="demo-section">
    <h2>Contact Us</h2>
    <ul id="fieldlist">
        <li>
            <asp:Label ID="lblName" runat="server">Name</asp:Label><br />
            <asp:TextBox ID="txtName" runat="server" CssClass="k-textbox"></asp:TextBox>
        </li>
        <li>
            <asp:Label ID="lblEmail" runat="server">Email</asp:Label><br />
            <asp:TextBox ID="txtEmail" runat="server" CssClass="k-textbox"></asp:TextBox>
        </li>
        <li>
            <asp:Button ID="btnSubmit" runat="server" OnClick="btnSubmit_Click" CssClass="k-primary k-button" Text="Submit" />
        </li>
    </ul>
</div>

Form1.ascx.cs (notice I added a response.redirect to the contact page. You will likely have to change the url it redirects to.

protected void btnSubmit_Click(object sender, EventArgs e)
    var manager = FormsManager.GetManager();
    var form = manager.GetFormByName("sf_contactus");
  
    Dictionary<string, string> dict = new Dictionary<string, string>();
    dict.Add("FormTextBox_C001", txtName.Text.Trim());
    dict.Add("FormTextBox_C003", txtEmail.Text.Trim());
  
    FormEntry entry = manager.CreateFormEntry(form.EntriesTypeName);
    foreach (var item in dict)
    
        entry.SetValue(item.Key, item.Value);
    
  
    HttpContext context = HttpContext.Current; if (context != null)
    
        entry.IpAddress = context.Request.UserHostAddress;
        entry.UserId = SecurityManager.GetCurrentUser().UserId;
    
  
    if (AppSettings.CurrentSettings.Multilingual)
    
        entry.Language = System.Globalization.CultureInfo.CurrentUICulture.Name;
    
  
    form.FormEntriesSeed = form.FormEntriesSeed + 1L;
    entry.ReferralCode = form.FormEntriesSeed.ToString();
    entry.SubmittedOn = System.DateTime.UtcNow;
  
    manager.SaveChanges();
    Response.Redirect("/Contact/?name=" + txtName.Text.Trim() + "&email=" + txtEmail.Text.Trim());
 

 

Form2.ascx

<div class="demo-section">
    <h2>Contact Us</h2>
    <ul id="fieldlist">
        <li>
            <asp:Label ID="lblName" runat="server">Name</asp:Label><br />
            <asp:TextBox ID="txtName" runat="server" CssClass="k-textbox"></asp:TextBox>
        </li>
        <li>
            <asp:Label ID="lblEmail" runat="server">Email</asp:Label><br />
            <asp:TextBox ID="txtEmail" runat="server" CssClass="k-textbox"></asp:TextBox>
        </li>
        <li>
            <asp:Label ID="lblMessage" runat="server">Message</asp:Label><br />
            <asp:TextBox ID="txtMessage" runat="server" TextMode="MultiLine" CssClass="k-textbox"></asp:TextBox>
        </li>
        <li>
            <asp:Button ID="btnSubmit" runat="server" OnClick="btnSubmit_Click" CssClass="k-primary k-button" Text="Submit" />
        </li>
    </ul>
</div>

  Form2.ascx.cs (notice I added Page_Load event handler to the tutorials sample, this is where the code for pre-populating the form takes place)

protected void Page_Load(object sender, EventArgs e)
       
    if (!Page.IsPostBack)
    
        txtName.Text = Request.QueryString["name"].ToString();
        txtEmail.Text = Request.QueryString["email"].ToString();
 
    
 
protected void btnSubmit_Click(object sender, EventArgs e)
    var manager = FormsManager.GetManager();
    var form = manager.GetFormByName("sf_contactus");
  
    Dictionary<string, string> dict = new Dictionary<string, string>();
    dict.Add("FormTextBox_C001", txtName.Text.Trim());
    dict.Add("FormTextBox_C003", txtEmail.Text.Trim());
    dict.Add("FormTextBox_C004", txtMessage.Text.Trim());
  
    FormEntry entry = manager.CreateFormEntry(form.EntriesTypeName);
    foreach (var item in dict)
    
        entry.SetValue(item.Key, item.Value);
    
  
    HttpContext context = HttpContext.Current; if (context != null)
    
        entry.IpAddress = context.Request.UserHostAddress;
        entry.UserId = SecurityManager.GetCurrentUser().UserId;
    
  
    if (AppSettings.CurrentSettings.Multilingual)
    
        entry.Language = System.Globalization.CultureInfo.CurrentUICulture.Name;
    
  
    form.FormEntriesSeed = form.FormEntriesSeed + 1L;
    entry.ReferralCode = form.FormEntriesSeed.ToString();
    entry.SubmittedOn = System.DateTime.UtcNow;
  
    manager.SaveChanges();


Additional Tips:
Do not use Sitefinity Thunder to create the Widgets.  Just create a normal Usercontrol for the Widgets.

In the second form after the data is saved use Response.Redirect to take the user to a thank you page.

There is a bug in the tutorial that I corrected in code I posted. I corrected it by replacing

dict.Add("FormTextBox_C003", txtEmail.Text.Trim());
dict.Add("FormTextBox_C004", txtEmail.Text.Trim());

with

dict.Add("FormTextBox_C003", txtEmail.Text.Trim());
dict.Add("FormTextBox_C004", txtMessage.Text.Trim());

Posted by Community Admin on 23-Jul-2015 00:00

Ch,

It's certainly another route, but it is still quite a bit of effort code to achieve.

Also, I tried to make it so that the code would work throughout the site and easily copied to another site, so the redirect url is obtained from a hidden form field, and it will process any field that starts with "QS".  That way if I want the same functionality again I don't have to write any more widgets/forms, etc.  I just drop a hidden field on "Redirect" and name the form field QSsomething, and I don't need to do anything on the backend, if that makes sense.

 

Posted by Community Admin on 24-Jul-2015 00:00

For the redirect try using a property instead of a hidden field.  You can than set the property when you add the Widget to the page.

 Form1.ascx.cs with property for redirect url

public string DestinationUrl
   get;set;
  
protected void btnSubmit_Click(object sender, EventArgs e)
    var manager = FormsManager.GetManager();
    var form = manager.GetFormByName("sf_contactus");
    
    Dictionary<string, string> dict = new Dictionary<string, string>();
    dict.Add("FormTextBox_C001", txtName.Text.Trim());
    dict.Add("FormTextBox_C003", txtEmail.Text.Trim());
    
    FormEntry entry = manager.CreateFormEntry(form.EntriesTypeName);
    foreach (var item in dict)
    
        entry.SetValue(item.Key, item.Value);
    
    
    HttpContext context = HttpContext.Current; if (context != null)
    
        entry.IpAddress = context.Request.UserHostAddress;
        entry.UserId = SecurityManager.GetCurrentUser().UserId;
    
    
    if (AppSettings.CurrentSettings.Multilingual)
    
        entry.Language = System.Globalization.CultureInfo.CurrentUICulture.Name;
    
    
    form.FormEntriesSeed = form.FormEntriesSeed + 1L;
    entry.ReferralCode = form.FormEntriesSeed.ToString();
    entry.SubmittedOn = System.DateTime.UtcNow;
    
    manager.SaveChanges();
    Response.Redirect(this.DestinationUrl + "/?name=" + txtName.Text.Trim() + "&email=" + txtEmail.Text.Trim());
   

 

Posted by Community Admin on 24-Jul-2015 00:00

[quote]Oh, and still haven't figured out how to hide a field in a form yet, anyone?[/quote]

You can probably create a custom user control with an asp:HiddenField.

 I did it where I add a value to it via javascript. But, it can certainly be repurposed so that a value can be added when you drop the field in the UI form designer or with via javascript on the page.

 The hidden field would display in your form responses on the back-end as well.

And, as an asp:HiddenField, it won't show up on the front end.

 Is this what you mean by "hide a field in the form?"

 

Posted by Community Admin on 27-Jul-2015 00:00

Gregory,

I was going to go down that road, but the documentation indicated this is a feature already in Sitefinity?  But I can't find it?

docs.sitefinity.com/for-developers-make-a-field-in-a-form-response-hidden-or-read-only-on-the-frontend

 

Posted by Community Admin on 27-Jul-2015 00:00

Interesting. I did not know such a feature existed. I will take a look at that.

 When I needed it, and couldn't find any documentation quickly, I went the "hard way" route with creating the control. Again, nothing fancy as it is limited in its current state. I needed something quickly and rushed through it.

 I'll take a look at that feature and see if I find anything and report back.

Posted by Community Admin on 01-Aug-2015 00:00

Well if the feature does exist, it's certainly difficult to find (hidden almost!).  I think it is version specific, would be nice to have this confirmed by Sitefinity though.

In the meantime I've just set the class of the field to hide in the form control, and then just added this to my CSS:

.hide
visibility: hidden;

This thread is closed