Nibble

Setting up a poll on your umbraco site using Contour

Umbraco’s formbuilder Contour is great for adding forms to your website! It doesn’t stop at contact forms and questionnaires, in this post I’ll show you how you can add a poll to your site using Contour.

Step 1: Setting up the form

Using Contour’s slick interface it’s super easy to create the form, simply navigate to the Contour section (if Contour is installed, otherwise first install it from the package repo) and create a new form.

On that new form setup a new field of the type ‘radiobutton list’ and we’ll make this a mandatory field since you’ll have to select an option before you can submit your vote.

image

Once the field is added you can simply setup the different options

image

Once the options are setup simply save the form and you should end with something like this:

image 

Step 2: Displaying the results

Ok now we have a form that will capture the votes. Next we need a way of displaying the results. Contour has some default xslt extensions you can use to fetch the submitted records.

But what I need to do is to first fetch all the different options, for that I’ll just create a custom xslt extensions that just returns some xml that has an overview of all questions and answers on a form

        public static XPathNodeIterator PossibleAnswers(string guid)
        {
            FormStorage fs = new FormStorage();
 
            Form f = fs.GetForm(new Guid(guid));
            fs.Dispose();
 
            XmlDocument d = new XmlDocument();
            XmlNode root = d.CreateElement("questions");
 
            foreach (Field fld in f.AllFields)
            {
 
                if (fld.FieldType.SupportsPrevalues 
                    && fld.PreValueSource.Type.GetPreValues(fld).Count > 0)
                {
                    XmlNode question = d.CreateElement("question");
                    XmlNode caption = d.CreateElement("caption");
 
                    XmlCDataSection questionvalue = d.CreateCDataSection(fld.Caption);
                    caption.AppendChild(questionvalue);
 
                    question.AppendChild(caption);
 
                    XmlNode answers = d.CreateElement("answers");
 
                    foreach (PreValue pv in fld.PreValueSource.Type.GetPreValues(fld))
                    {
                        XmlNode answer = d.CreateElement("answer");
                        XmlAttribute answerId = d.CreateAttribute("id");
                        answerId.Value = pv.Id.ToString();
                        answer.Attributes.Append(answerId);
                        XmlCDataSection value = d.CreateCDataSection(pv.Value);
                        answer.AppendChild(value);
                        answers.AppendChild(answer);
                    }
 
                    question.AppendChild(answers);
                    root.AppendChild(question);
                }
 
            }
 
            d.AppendChild(root);
 
            return d.CreateNavigator().Select(".");
        }

Then we’ll write some xslt that will use this extensions to output a list of questions with their different options. We also need to know how many records have been submitted and how many ‘votes’ each answers has. To see the total number of submitted records for a form we can use the default contour xslt extension umbraco.contour:GetRecordsFromForm(‘form id’) , the total number of votes(records) we can get by counting the records count(umbraco.contour:GetRecordsFromForm(‘form id’)/uformrecord) then the only think left is to count the number of votes each option has…

The end result of the xslt that will output the results:

<xsl:param name="currentPage"/>
 
<xsl:param name="formid" select="/macro//formid" />
    
<xsl:template match="/">
  
    <xsl:if test="$formid">
     <xsl:variable name="records" select="umbraco.contour:GetRecordsFromForm($formid)"/>
     <xsl:variable name="numberOfRecords" select="count($records/uformrecord)" />
    
   <div class="results" style="width:300px">
       <h3>Poll results</h3>
     
     <xsl:for-each select="contourpoll:PossibleAnswers($formid)//question">
    <xsl:variable name="numberOfAnswers" select="count(./answer)" />
       
    <h4><xsl:value-of select="caption"/></h4>
      <ul style="list-style:none;padding-left:0px;margin-left:0px;">
        <xsl:for-each select=".//answer">
      <xsl:sort select="count($records//value [@key = current()/@id])"
                data-type="number" order="descending"/>
 
      <xsl:variable name="currentId" select="@id" />
      <xsl:variable name="currentVotes" select="count($records//value [@key = $currentId])" />
      <xsl:variable name="currentPercentage"
                        select="round (($currentVotes div $numberOfRecords) * 100)"/>
      
      <li>
          <xsl:value-of select="."/>
          <div class="scorebarcontainer" style="width:100%;background-color:#eee;height:20px">
            <div class="scorebar"
                    style="width:{$currentPercentage}%;background-color:#ccc;height:20px">
            </div>
          </div>
      </li>
   </xsl:for-each>
  </ul>
       </xsl:for-each>
    </div>
      
    </xsl:if>
</xsl:template>

 

And how this will look:

image

 

Step 3: Only show form if you haven’t voted yet

Ok so now we have a form that will be used to submit the votes and an xslt macro used to output the results

So depending on if the user has submitted a vote yet we need to show the poll or the results.

So once the user submits the form we’ll set a cookie, and we can do that by hooking into the Contour event model

using System;
using Umbraco.Forms.Core.Services;
 
namespace Contour.Addons.ScoreCalclulator
{
    public class SetCookieOnSubmit: umbraco.BusinessLogic.ApplicationBase {
 
        public SetCookieOnSubmit()
        {
            RecordService.RecordSubmitted +=
                new EventHandler<Umbraco.Forms.Core.RecordEventArgs>(RecordService_RecordSubmitted); 
        }
 
        void RecordService_RecordSubmitted(object sender, Umbraco.Forms.Core.RecordEventArgs e)
        {
            umbraco.library.setCookie(e.Form.Id.ToString(), "1"); 
        }
 
 
    }
}

So this will basically set a cookie with the current submitted form id as key.

Next on the template where we put our poll we just check if that cookie exists.

The pollId is getting pulled from a page property value, on my homepage I have this property:

image

Which will render like this (so the content editor can select the poll he wants to display)

image

On the template instead of simply dropping the form macro I first simply check if there is a poll id and then depending on the cookie show the form macro or the results macro

<%
              
string pollId = umbraco.presentation.nodeFactory.Node.GetCurrent().GetProperty("activePoll").Value;
             
if(!string.IsNullOrEmpty(pollId)){
 
   if (Request.Cookies[pollId] == null) { %>
                    
  <umbraco:Macro FormGuid="[$activePoll]"
                  Alias="umbracoContour.RenderForm" runat="server"></umbraco:Macro>
                   
<% }
 
else {%>
                    
  <umbraco:Macro formid="[$activePoll]"
                 Alias="ContourPollResults" runat="server"></umbraco:Macro>
                   
<% }} %>

 

The Result

 

Download the sourcefiles here

19 Comments so far

  1. Warren Buckley on September 28th, 2010

    Hiya Tim,
    Great tutorial and really shows how extensible Contour is for Umbraco.

    I was wondering how to take this tutorial to the next step, basically I need users to submit the poll answers themeselves.

    For example a user uploads a screenshot of a website. It gets added to the voting/poll form as an answer so that other people can vote on that particular site/answer in the poll.

    Would be amazing if it could be done.
    Warren :)

  2. Tim Geyssens on September 28th, 2010

    @Warren, sure instead of fixed static options you can just use a prevalue source (this can be umbraco documents, a database table, … ). And you could create an additional form where people can submit new answers themselves (hook a workflow to it that creates umbraco documents)

  3. Warren Buckley on September 28th, 2010

    @Tim Thanks. Feel a bit dumb now. It makes perfect sense :)

  4. carlos on December 10th, 2010

    Am I missing something.
    What is the macro//formid?
    And when I try to save the XSLT for the form, I get a “contourpoll” is not defined.

    Any help is greatly appreciated.

    Thanks in advance

  5. carlos on December 10th, 2010

    Nevermind I think I got it. Our alias is different.

  6. carlos on December 10th, 2010

    So I tried adding the code into our Template, it won’t render the page and it sent me an error “An error occurred while processing your request.”
    We followed everything exactly, except we did not put the it in our homepage template. This is an issue.
    Please advice.
    Thanks

  7. carlos on December 10th, 2010

    Also is there a way to pull it to another XSLT file. I know about the include, but with currentPage already being used. I get in error.

  8. carlos on December 10th, 2010

    Please specify to wrap you code in the template in a form tag. Found that out.

    Also, How do you render the results on the page. Right now it doesn’t render the results.

  9. carlos on December 10th, 2010

    FYI TO ALL.
    You have to add the alias “formid” to the Macro for the ContourPollResults Macro of the ContourPollResults.xslt

  10. Davide on March 8th, 2011

    Great tutorial !!!

    I’m trying contour for a new website and this use is interesting.

    I tried to download the example code, but the download link not work.

  11. dimitris chrysomallis on August 12th, 2011

    Hi,

    How should I use the downloadable solution?

    Should i just copy the dll into the bin directory?

    Thanks

  12. dimitris chrysomallis on September 13th, 2011

    Hi,

    I get an error:

    Error parsing XSLT file: \xslt\ContourPollResults.xslt

    I have set formid on the macro.

    Thanks

  13. Harm on April 26th, 2012

    Used this on an older 4.0 website and (after changing target framework and references for .net 3.5) it all worked perfectly.

    Thank you!

  14. Dave on July 10th, 2012

    Hi,

    I get an error:

    Error parsing XSLT file: \xslt\ContourPollResults.xslt

    I have set formid on the macro.

    What is the line that needs to be added to the xlstextensions for poll results? there’s already a contour line in there.

    Thanks

  15. bob baty-barr on July 11th, 2012

    here is another question i have…

    the template example…

    this line is throwing errors - object not set to an instace of….

    for pages without a poll specified.

    string pollId = umbraco.presentation.nodeFactory.Node.GetCurrent().GetProperty(”activePoll”).Value;

    the complete code chunk is this…

  16. Anders H on July 11th, 2012

    I think this will help
    , works for me

  17. Anders H on July 11th, 2012

    ext assembly=”Contour.Addons.Poll” type=”Contour.Addons.Poll.xsltExtensions” alias=”contourpoll”

  18. Hardy Wang on September 5th, 2012

    It is very cool.

    Is there a Razor version available?

  19. Anders Birkmose on January 2nd, 2014

    Hi
    This worked for me
    1) Add to xsltextensions.config:

    2)
    in the template you need to change the macro alias to the new razor renderer

Leave a Reply