Nibble

Archive for October, 2010

Codegarden Contour session, add forms without coding 0

At this years codegarden I did a session on Contour, missed codegarden or didn’t attend the session, well you can watch it now…  It’s an intro to the different parts in umbraco contour (form designer, entries viewer, workflows, data sources, prevalue sources, security, …)

Be sure to check out http://stream.umbraco.org/ for some more codegarden sessions and even Umbraco UK festival ones…

Using the Umbraco Content picker as a media picker (or other tree picker) 3

Ever wanted to add a content picker or media picker to your custom controls?

Well in case of a content picker you can simply use the umbraco.controls.ContentPicker control

A nice thing about this control is that you can also feed it a different app and tree alias, turning it into a different kind of picker.

To use it as a media picker:

umbraco.controls.ContentPicker mp = new umbraco.controls.ContentPicker();
 
mp.AppAlias = "media";
mp.TreeAlias = "media";

Extending umbraco contour, creating a custom prevalue source type 7

Yet another post in the Contour series, in this one I’ll outline how I created a new prevalue source type.

By default there are a number of prevalue source types in contour (fetch prevalues from a database table, from umbraco documents, from umbraco datatype prevalues, …), but it’s also possible to extend Contour and create your own fieldtypes, datasource types, field setting types, …. and of course prevalue source types.

So imagine we have a simple text file containing all cities in Belgium and of course we don’t want to manually create a cities dropdown and start copy pasting the contents. Instead we’ll setup a new prevalue source where I can upload the textfile and then hook this prevalue source to the dropdown we wish.

The code to do this (the custom prevalue source type) is actually pretty simple, by first looking at the contour shared sourcecode (which contains the sourcecode for all default elements in contour I have the code in a couple of minutes) here it is:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Umbraco.Forms.Core;
using System.IO;
 
namespace Contour.Addons.ScoreCalclulator
{
    public class GetValuesFromTextFile: FieldPreValueSourceType 
    {
        [Umbraco.Forms.Core.Attributes.Setting("TextFile", 
            description = "File containing the prevalues (seperated by linebreak)", 
            control = "Umbraco.Forms.Core.FieldSetting.File")]
        public string TextFile { get; set; }
 
        public GetValuesFromTextFile()
        {
            this.Id = new Guid("35C2053E-CBF7-4793-B27C-6E97B7671A2D");
            this.Name = "Get values from textfile";
            this.Description = "Upload textfile that contains the prevalues (seperated by linebreak)";
        }
 
        public override List<PreValue> GetPreValues(Field field)
        {
            List<PreValue> result = new List<PreValue>();
 
            try
            {             
                int sort = 0;
 
                TextReader tr = 
                    new StreamReader(HttpContext.Current.Server.MapPath(TextFile));
                string[] values = tr.ReadToEnd().Split(‘\n’);
 
                foreach (string value in values)
                {
                    if (!string.IsNullOrEmpty(value.Trim()))
                    {
                        PreValue pv = new PreValue();
                        pv.Id = sort;
                        pv.Value = value;
                        if (field != null)
                        {
                            pv.Field = field.Id;
                        }
                        pv.SortOrder = sort;
                        result.Add(pv);
                        sort++;
                    }
                }
            }
            catch (Exception ex)
            {
                Umbraco.Forms.Data.LogHelper.Error(
                    "Get values from textfile provider: " + ex.ToString());
            }
 
            return result;
        }
 
        public override List<Exception> ValidateSettings()
        {
            List<Exception> exs = new List<Exception>();
 
            if (string.IsNullOrEmpty(TextFile))
                exs.Add(new Exception("’TextFile’ setting not filled out’"));
 
            return exs;
        }
    }
}

 

The important things to notice here is that we have a single setting which is the TextFile settings and it’s using the Umbraco.Forms.Core.FieldSetting.File control so it will render an upload control.

Then in the GetPrevalues method we’ll simpy read the file and loop trough all values(which should be seperated by a linebreak).

And that’s it, if I now compile this code and place the assembly in the bin directory of the umbraco installation I will be able to select the new prevalue source type when creating a new prevalue source

image

And once I select the type I’ll be able to upload the text file

image

Now once the new prevalue source has been setup I’ll be able to add a new field to my form that uses this source.

image

And after adding the field I should be able to see the prevalues (even in the form designer)

image

Download the sourcecode here:

And the text file with cities I used:

A look at prevalue sources in umbraco contour 2

After the post about data sources in contour this post outlines another feature of contour, prevalue sources.

A prevalue source allows you to setup a source that you can hook to a field type that supports prevalues (dropdown, checkbox list, radio button list).

Let’s say we have an existing database table that has a lists of all countries, well instead of having to setup these countries manually when we want a countries dropdown we can create a new prevalue source and link this to our field.

So when we take a look at the contour section you’ll notice 3 trees (forms, data sources and prevalue sources)

image

So it’s obvious that we’ll be managing the prevalue sources in the prevalue sources tree.

Let’s say I have the following database, containing a table that has all countries in it

image

And now I want to use this table as a source for a countries dropdown.

First I would create a new prevalue source and call in Countries, once that has been created I’ll choose the sql database prevalue source type (since I’m getting the values from a db table)

image

As you can notice by default there are 2 SQl Database types, ‘SQL Database’ and ‘SQL Database (read only)’. If you choose the normal one we will also be able to update / delete and create new prevalues (which will update the prevalue source, so the db table in this case).

Once we select a type the type specific settings will load and we”ll need to fill these in

image

In the case of the SQL Database prevalue source type we’ll need to setup

  • Connectionstring to the db
  • Table name
  • Name of the column containing the key
  • Name of the column containing the value (that will be displayed)

Once these are filled in and the prevalue source has been saved succesfully we are ready to use the newly configured prevalue source.

When we now add or edit a field that supports prevalues (dropdownlist, radiobutton list, checkbox list)

We’ll be able to choose the type

by choosing a prevalue source ‘so anything else then standard’ we’ll link the field to the source

image

That’s it now our dropdown will list all items in the table.

In this example I used the sql database prevalue source type but there are also some other default prevalue source types and it’s of course also possible to plug in your own custom types!

A look at data sources in umbraco contour 5

To continue the series on umbraco Contour, in this post I’ll show you how you can use the data sources feature in Umbraco Contour. When you navigate to the Contour section (if you have Contour installed, otherwise you can install it from the package repository, developer section/ packages / package repo / umbraco pro) you’ll notice 3 trees in the treeview on the left. Forms, Data Sources and Prevalue Sources.

image

Data Sources allow you to create a form based on a Data source and submit your form entries to that data source. By default there are 3 types of data sources

  • Save as node (create form based on document type and create an umbraco content document on submit)
  • SQL Database (create form based on database table and submit data to that table)
  • Webservice (create form based on webservice and post to that service on submit)

In this post I’ll use the SQL Database data source type.

I have the following database that contains a subscription table with some sample fields including a title field which is a foreign key of a title table.

image

So now I want to add a form to my site that submits records (subscriptions) to this table. Using data sources in Contour you can do this without a single line of code, just by completing a simple wizard.

The first step is to create a new datasource (so simply right click the data sources tree and select create and then provide the new data source a name). Once the data source is created the next step is to select the type, in this case it will be ‘SQL Database’

image 

Once the type is selected the settings for the specific type should load. In case of the ‘SQL Database’ type there are 2 settings.

  • Connection string
  • Table name

image

Once these are setup correctly and the datasource is saved you should get a ‘create form’ button, this will start the form creation wizard.

image

In the first step of the wizard you’ll need to select which db columns you want to include on the created form (if the db columns doesn’t allow nulls this will be autu checked).

image

Depending on the presence of foreign keys the next step will be to setup the foreign keys, so you’ll have to select which column will be used to display

image

And the final step allows you to map a fieldtype to each of the fields that will be created

image

Once that is setup simply hit the ‘save form’ button and a new form will be created

Since the form is linked to a data source you’ll have limited editing possibilities

image

That’s it now we have a form that will submit data to a custom database table, without writing a single line of code but by simply completing a 3 step wizard.

And it’s of course also possible to extend these data sources by creating your own, check out the contour shared sourcecode and take a look at the default data sources code.

Creating a Quiz (including score calculation and feedback) with Umbraco Contour 11

After the Setting up a poll on your umbraco site using Contour this is another post in the Umbraco Contour series. This time I’ll create a quiz for my umbraco site using Contour.

This post uses some of the new additions to Contour 1.1.3 so you’ll need to be running that version if you want to try this yourself.

The challenge

With a quiz I mean that I’ll have a number of questions and possible answers. Each of these answers will have a weight (for example, 0 if the answer is incorrect, 1 if the answer is correct) and when a user fills in the quiz he should get feedback on how he did (so how much he scored based on the answers he gave).

Step 1: Setting up the form

Setting up the questions and answers is is a piece of cake, by simply using Contour’s ui to setup the form we have it up in minutes (so I won’t go in detail on how you create a form with Contour, if you want to try it just install Contour directly from the package repo).

image

Once I have a form with some radiobuttonlist fields I’ll also add a hidden field that will store the score

image

Step 2: Making it possible to give a weight to each answer and Calculating + storing the score

 

So once the user has completed the form/quiz I want to calculate the score. To execute some code during the record lifecycle we’ll use a workflow.

A workflow can have multiple settings each of these settings can be of a different type (for example a send email workflow will have a subject setting that is of the type textfield) to be able to give each answer a weight I’ll create a new fieldsetting type that will list all questions with their possible answers and on each answer have an input to provide a weight. Which will look like this:

image

The code for the custom fieldsetting type looks like this (download the entire sourcecode at the bottom of this post:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Umbraco.Forms.Core;
using Umbraco.Forms.Data.Storage;
using System.Web.UI.WebControls;
using System.Web.UI;
 
namespace Contour.Addons.ScoreCalclulator
{
    public class ScoreMapper : FieldSettingType
    {
        private Panel p = new Panel();
        private string _val = "";
 
        public override string Value
        {
            get
            {
                return _val;
            }
            set
            {
                _val = value;
            }
        }
 
        public override System.Web.UI.WebControls.WebControl RenderControl
            (Umbraco.Forms.Core.Attributes.Setting sender, Form form)
        {
            if (!string.IsNullOrEmpty(HttpContext.Current.Request["scoremapperValues"]))
                _val = HttpContext.Current.Request["scoremapperValues"];
 
            Dictionary<string, int> scores = new Dictionary<string, int>();
 
            foreach (string mapping in _val.Split(‘;’))
            {
                int weight = 0;
                if (!string.IsNullOrEmpty(mapping) && mapping.Split(‘,’).Length > 0)
                    scores.Add(
                        mapping.Split(‘,’)[0], 
                        int.TryParse(mapping.Split(‘,’)[1], out weight) ? weight : 0);
            }
 
            string html = "<div id=’scoreMapper’>";
 
            foreach (Field f in form.AllFields)
            {
                if (f.FieldType.SupportsPrevalues && f.PreValueSource.Type.GetPreValues(f).Count > 0)
                {
                    html += string.Format(
                        "<div class=’scoreMapperField’ style=’width:400px’><small>{0}</small>", 
                        f.Caption);
 
                    foreach (PreValue pv in f.PreValueSource.Type.GetPreValues(f))
                    {
                        html += string.Format(
                            "<div class=’scoreMapperFieldPrevalue’><span>{0}</span>", 
                            pv.Value);
                        html += string.Format(
                            "<input type=’hidden’ class=’key’ value=’{0}’/>",
                            pv.Id);
                        html += string.Format(
                            "&nbsp;<input type=’text’ class=’value’ value=’{0}’/>", 
                            scores.ContainsKey(pv.Id.ToString()) ? scores[pv.Id.ToString()].ToString() : "0");
 
                        html += "</div>";
                    }
 
                    html += "</div>";
                }
            }
 
            html += "<input type=’hidden’ id=’scoreMapperValues’ value=’" + _val + "’ name=’scoreMapperValues’/>";
            html += "</div>";
 
            System.Web.UI.Page page = (System.Web.UI.Page)HttpContext.Current.Handler;
 
            page.ClientScript.RegisterClientScriptInclude(
                "Contour.Addons.ScoreCalclulator.scoreMapper.js",
                page.ClientScript.GetWebResourceUrl(
                typeof(ScoreMapper), "Contour.Addons.ScoreCalclulator.scoreMapper.js"));
 
            p.Controls.Add(new LiteralControl(html));
 
            return p;
        }
 
 
    }
}

 

And the contents of the ScoreCalclulator.scoreMapper.js

jQuery(document).ready(function () {
    jQuery(‘#scoreMapper div.scoreMapperFieldPrevalue input.value’).change(function () {
        storeScoreMapperValues();
    });
})
 
function storeScoreMapperValues() {
    var vals = ;
 
    jQuery(‘div.scoreMapperFieldPrevalue’, jQuery(‘#scoreMapper’)).each(function () {
        var id = jQuery(‘input.key’, this).val();
        if (id != ) {
            var score = jQuery(‘input.value’, this).val();
            if (score == ) { score = 0; }
            vals += id + ‘,’ + score + ‘;’;
        }
    });
 
    jQuery(‘#scoreMapperValues’).val(vals);
}

Now I’ll use this custom field setting type on a custom workflow wich will be used to calculate the score. Besides the custom weight mapper setting the workflow also has a field picker that makes it possible to select a field on the form (I’ll use it to select the field where I want to store the score). And I’ll also add a textfield where I will prompt for a session variable key that I’ll set with the score.

So entire workflow will look like this

image

And the code (again you can download the full sourcecode at the bottom of this post):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Umbraco.Forms.Data.Storage;
using Umbraco.Forms.Core.Services;
using Umbraco.Forms.Core;
 
namespace Contour.Addons.ScoreCalclulator
{
    public class EnergyQuizScoreCalculator : Umbraco.Forms.Core.WorkflowType
    {      
        [Umbraco.Forms.Core.Attributes.Setting(
            "Score mappings", 
            description = "Setup how much each answer scores", 
            control = "Contour.Addons.ScoreCalclulator.ScoreMapper", 
            assembly = "Contour.Addons.ScoreCalclulator")]
        public string scoremappings { get; set; }
 
        [Umbraco.Forms.Core.Attributes.Setting("Field to update", 
            description = "Field that will be populated with the score", 
            control = "Umbraco.Forms.Core.FieldSetting.FieldPicker")]
        public string field { get; set; }
 
        [Umbraco.Forms.Core.Attributes.Setting("Session key to set", 
            description = "If supplied a session variable will be set", 
            control = "Umbraco.Forms.Core.FieldSetting.TextField")]
        public string sessionkey { get; set; }
 
        public EnergyQuizScoreCalculator()
        {
            this.Name = "Score Calculator";
            this.Id = new Guid("EAFAC6F1-F2D2-43A4-8940-AB7A6F7AE83F");
            this.Description = "Calculates a score based on the questions/correct answers";
        }
 
 
        public override List<Exception> ValidateSettings()
        {
            List<Exception> exceptions = new List<Exception>();
            return exceptions;
        }
 
 
        public override Umbraco.Forms.Core.Enums.WorkflowExecutionStatus Execute(Record record, RecordEventArgs e)
        {
            //calc score
            int score = 0;
 
            Dictionary<string, int> scores = new Dictionary<string, int>();
 
            foreach (string mapping in scoremappings.Split(‘;’))
            {
                if (!string.IsNullOrEmpty(mapping) && mapping.Split(‘,’).Length > 0)
                {
                    int weight = 0;
                    scores.Add(
                        mapping.Split(‘,’)[0], 
                        int.TryParse(mapping.Split(‘,’)[1], out weight) ? weight : 0);
                }
            }
 
            foreach (RecordField rf in record.RecordFields.Values)
            {
                if (rf.Values.Count > 0 && scores.ContainsKey(rf.Values[0].ToString()))
                    score += scores[rf.Values[0].ToString()];
            }
 
            //set score
            foreach (RecordField rf in record.RecordFields.Values)
            {
                if (rf.Field.Id ==  new Guid(field))
                {
                    rf.Values.Clear();
                    rf.Values.Add(score);
                    break;
                }
            }
 
            if(!string.IsNullOrEmpty(sessionkey))
                HttpContext.Current.Session[sessionkey] = score.ToString();
 
            FormStorage fs = new FormStorage();
            Form f = fs.GetForm(record.Form);
            RecordStorage rs = new RecordStorage();
            rs.UpdateRecord(record, f);
            rs.UpdateRecordXml(record, f);
 
            fs.Dispose();
            rs.Dispose();
 
            return Umbraco.Forms.Core.Enums.WorkflowExecutionStatus.Completed;
 
        }
    }
}

 

Once my custom workflow type is ready I simply need to place the assembly containing the workflow type in my bin directory and I should now be able to choose a ‘Score Calculator’ workflow type and add this to my form.

Step 3: Displaying the score

Once the workflow is setup the last step is to provide details on the score to the user filling out the quiz.

In the workflow I set a session variable with the score, so I’ll use the bracket syntax supported by Contour in the message on submit field to display this to the user. (in this case the key is moviequizscore)

image

The Result

Your content editors will be able to setup custom online quizzes, they will have full control over the number of questions/answers and how much weight they give each answer all from a nice UI directly integrated in the umbraco backend.

Want to learn more on how to extend Contour, check out the developer docs on the project page http://our.umbraco.org/projects/umbraco-pro/contour and the sourcecode for all default components in Contour (field types, workflow types, prevalues and data types) http://our.umbraco.org/projects/umbraco-contour-shared-source

Download the sourcecode described in this post here:

(simply drop the assembly Contour.Addons.ScoreCalclulator.dll your bin directory and you should also have the score calculator workflow)