Nibble

Contour member tools 20

After the previous post on contour and members I got several requests asking how you could use contour for a registration form so here are the details. I make use of some additional fieldtypes and workflow types so to make it easy to get started I also packaged those up (download details at the bottom). The package will add some extensions to contour that make it possible to create registration and profile forms.

Overview of what it will add:

  • Member Login fieldtype
  • Compare textbox fieldtype
  • Add member to membergroup workflow type
  • Save as umbraco member workflow type
  • Update umbraco member workflow type

Setting up a registration form

The registration form will be creating umbraco members so first make sure you have a member type. In this example I’ll be using a member type with 2 custom properties, first name and last name but it’s of course possible to add more …

image

Once the member type is setup we’ll create a registration form in Contour. Making use of the 2 additional field types.

image

The email address will be used as a unique identifier for our members (and also as the loginname). So the type of the email address field is set to ‘Member Login’.

image

The type of the password field is set to ‘Compare textbox’

image

Both fields are also set to mandatory.

Once the registration form is designed we’ll need to attach the workflow that will perform the actual registration. The type is called ‘Save as umbraco member’

image

The workflow has 3 settings, the member type of the member that will be created, an optional membergroup to add the member to and an option to immediately login the member after registation.

Once a membertype has been chosen it’s possible to map the properties of the type to the fields on the form. Name, login and email are all set the the email field since the email will be used as the unique identifier.

image

If you also want to add the newly created member to a group it’s possible by setting the membergroup setting.

image

It’s also possible to directly login the member after registration

image

You can then add other workflows that sends an email to the site administator, a welcome email to the new member….

image

Once the registration form is submitted a new member is created with the supplied details

image

image

The Member Login field type also makes sure that the login (email) is unique. It uses a little bit if jQuery for it so make sure jQuery is available where you place the registration form. If you enter an email address that is already in use you’ll get notified. This check also occurs when the form is submitted so it’s not possible to end up with duplicate emails.

image

Setting up an edit profile form

To create the profile form we’ll make use of the bracket syntax to fill the fields with member values and again use a workflow to update the member. This is already outlined in a previous post: Contour forms and umbraco members

Going further

To get a login form, logout button you can simply make use of the standard .net login controls. And with the public access dialog you can setup role based protection. Details on how to do that can be found in the members umbraco.tv tutorial 

Download

The Contour member tools Package is available on the project page on our.umbraco.org . Make sure to be running at least version 1.1.9 of Umbraco Contour.

Contour forms and Umbraco members 14

Contour 1.1.9 has just been shipped. It’s a minor release, but it adds a nifty little feature making it super easy to work with member data. Default values and workflow settings bracket syntax has now been updated to support member values.

Member values as default field values

Say that you have a comment form on your website with the standard fields (name, email, website and comment).

clip_image002

In the case when an Umbraco member is currently logged in and you want to pre-populate the name and email fields with the values from the logged in member, it’s now just a matter of setting the default value using the bracket syntax.

image

You can access: member.id, member.loginname, member.email and for custom properties simply use the alias member.customalias.

Full details on the syntax can be found in the Contour developer documentation.

Member values in workflow settings

Of course it’s also possible to use this syntax in workflow settings. For example, sending an email to the current member when a form has been submitted, just set the email setting to {member.email}

clip_image006

Member profile editor

Combine the default values with a workflow that will update the member details and you have a member profile editor.

Let’s start with a simple member type with just a single property with the alias custom.
clip_image008

I want the members to be able to edit their email and the value for the custom property so I’ll create a form with 2 fields (email address and custom property).

clip_image010

The default value of the email field has been set to {member.email}

clip_image012

And the one for custom property has been set to {member.custom}

clip_image014

So now if I request the form when I’m logged in as an Umbraco member, the fields should be populated with the current member values.

clip_image016

The next step is to make sure the member values get updated when the form is submitted. This can be done with a workflow (workflow is part of the contour strikes again project).

clip_image018

The workflow simply maps the member type properties to the contour form fields. When the form is submitted the member properties will be updated. Once this is in place you have a working profile editor.

Make sure you have at least Contour version 1.1.9 in order to use these features. Install and upgrade instructions can be found on the contour project page.

Branch/shop/… locator for Umbraco Razor style 0

A while ago I created a locator package for umbraco that made it pretty easy to add find nearest branch/shop/dealer/… functionality to your umbraco site.

To sharpen my razor skills I thought it would be great to try to port this from xslt to razor and turns out this was pretty easy (feel the power of the dark side).

Another update is that it now uses the google maps api v3 so no more need for api keys

It works in combination with the google maps datatype so you’ll need to make sure that your documents have coordinates stored before you are able to search (so a property of the type google maps using the google maps datatype).

image

Once the documents you want to search through have a long/lat stored you can use the locator macro.

image

The macro has 4 parameters

  • Document type alias of the documents you want to search through
  • Property alias of the property containing the coordinates
  • If you want the units in miles instead of km
  • Number of results to display

If you have the macro on a page/template you’ll need to provide it with a search location. So posting to that page with a querystring variable s will do the trick like ?s=gentbrugge

The default output is pretty simple,

  • Search location
  • Map with markers
  • Unordered list of results ordered by distance from search location

image

But that can be updated to your needs. There is some stuff going on in the locator assembly but the output can be completely customized by updating the razor script.

@inherits umbraco.MacroEngines.DynamicNodeContext
@using Locator
@using Locator.GeoCoding
@using Locator.GeoCoding.Services.Google
@using umbraco.MacroEngines;
 
@{
 
   @* Select the nodes you want to search through, defaults to children *@
   var nodesToSearchThrough = string.IsNullOrEmpty(Parameter.DocTypeAlias) ?
        Model.Children.Where("Visible"): Model.AncestorOrSelf(1).Descendants(Parameter.DocTypeAlias).Where("Visible");
   
   @* Alias of the property containing the long/lat, defaults to location *@
   string locationPropAlias = string.IsNullOrEmpty(Parameter.LocationPropAlias) ? 
        "location" : Parameter.locationPropAlias;
   
   @* Number of results, defaults to 5 *@
   int numberOfSearchResults = string.IsNullOrEmpty(Parameter.NumberOfResults)|| Parameter.NumberOfResults == "0" ? 
        5 : int.Parse(Parameter.NumberOfResults);
  
   @* Distance unit, defaults to km *@
   DistanceUnits distanceunit = Parameter.UnitInMiles == "1" ? 
        DistanceUnits.Miles : DistanceUnits.Kilometers;
  
   List<GeoItem> items = new List<GeoItem>();
   Location searchLocation = new Location();
  
   if(string.IsNullOrEmpty(Request["s"]))
   {
       @* Search term not provided *@ 
       <p>Please provide a search term</p>
   }
   else 
   {
      @* Lookup search location coordinates *@ 
      GeoResponse r = GoogleGeoCoder.CallGeoWS(Request["s"]);
    
      if(r.Results.Length == 0)
      {
        @* Location not found *@ 
        <p>Location not found:  @Request["s"]</p>
      }
      else
      {
        @* build up list of results *@       
        searchLocation = new Location(r.Results[0].Geometry.Location.Lat, r.Results[0].Geometry.Location.Lng);
       
        foreach (var node in nodesToSearchThrough) {
                         
              Location itemLocation = new Location(
                    Convert.ToDouble(node.GetProperty(locationPropAlias).ToString().Split(‘,’)[0], Utility.NumberFormatInfo),
                    Convert.ToDouble(node.GetProperty(locationPropAlias).ToString().Split(‘,’)[1], Utility.NumberFormatInfo));  
              
              items.Add(new GeoItem(node,itemLocation,searchLocation.DistanceBetween(itemLocation,distanceunit)));
                                                                                       
        }
        @* sort based on distance *@
        items.Sort(new GeoItemComparer());
       
      }
   }
}
@if(items.Count > 0)
{
@* Output Results *@ 
 
<p>Result for: @Request["s"]</p>
  
@* Output Map *@
 
<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false">
</script>
 
<div id="map_canvas" style="width:500px; height:500px"></div>
 
<script type="text/javascript">
  function initialize() {
    var latlng = new google.maps.LatLng(
                    @searchLocation.Latitude.ToString(Utility.NumberFormatInfo), 
                    @searchLocation.Longitude.ToString(Utility.NumberFormatInfo));
    var myOptions = {
      zoom: 8,
      center: latlng,
      mapTypeId: google.maps.MapTypeId.ROADMAP
    };
    var map = new google.maps.Map(document.getElementById("map_canvas"),
        myOptions);
    @{int c = 1;}                      
    @foreach(GeoItem geo in items.Take(numberOfSearchResults)){
        dynamic node = ((DynamicNode)geo.Node);    
                                                          
        <text>
          var mLatlng@(c) = new google.maps.LatLng(
                                      @geo.Location.Latitude.ToString(Utility.NumberFormatInfo),
                                      @geo.Location.Longitude.ToString(Utility.NumberFormatInfo));
          var marker@(c) = new google.maps.Marker({
              position: mLatlng@(c) , 
              map: map, 
              title:"@node.Name"
          });                                                              
         </text>
        c++;                                                  
   }
  }
  initialize();
</script>
 
 
@* Output list *@
<ul>
@foreach(GeoItem geo in items.Take(numberOfSearchResults)){ 
  dynamic node = ((DynamicNode)geo.Node);                                                
  <li><a href="@node.Url">@node.Name - @geo.Distance.ToString("0.00") km</a></li>                               
}
</ul>
}

Package can be downloaded on the project page: http://our.umbraco.org/projects/developer-tools/locator-razor-style

A First look at Property Editors in Umbraco v5 0

Umbraco v5 introduces a new definition: Property Editor

From the wiki: A Property Editor is the editor that a Data Type references. In Umbraco versions previous to version 5, there was no official name given to the editor that a Data Type referenced and it was also known as a Data Type. In version 5 we differentiate between the 2 aspects of Umbraco. A Data Type is defined by the Administrator of Umbraco which references a Property Editor. A Property Editor on the other hand is defined by a developer and compiled into a DLL.

So where in previous umbraco versions we talked about creating custom datatypes this will be know as property editors in v5.

On this blog I always used the same example when creating custom datatypes, a textarea where you can set a limit on the amount of characters (like in this post).

image

So as a first attempt at creating a property editor for umbraco v5 I created the same control. Of course this is build against the CTP release so there might be changes by the time v5 is released (if so I’ll do some follow up posts)

To get started with creating property editors, take a look at the v5 Wiki: Developing Property Editors

We’ll need to implement 3 classes, PropertyEditor, EditorModel and PrevalueModel (and in this case I’ll also have a razor view for my content editor control).

image

 

Property Editor

Core class which defines the Property Editor

using Umbraco.Cms.Model.BackOffice.PropertyEditors;
 
namespace Umbraco.Addons.PropertyEditors.CharLimit
{
    [PropertyEditor("E3FAF03B-C3A5-4630-B917-9B6D9F645833", 
        "CharLimit", "Text Area with character limit")]
    public class CharLimitEditor : PropertyEditor<CharLimitEditorModel, CharLimitPreValueModel>
    {
        public CharLimitEditor()
        {       
        }
 
        public override CharLimitEditorModel CreateEditorModel(CharLimitPreValueModel preValues)
        {
            return new CharLimitEditorModel(preValues);
        }
 
        public override CharLimitPreValueModel CreatePreValueEditorModel(string preValues)
        {
            return new CharLimitPreValueModel(preValues);
        }
    }
}

 

PreValueModel

Used to provide configuration settings for the Property Editor

using Umbraco.Cms.Model.BackOffice.PropertyEditors;
using Umbraco.Cms.Model.BackOffice.Editors;
 
namespace Umbraco.Addons.PropertyEditors.CharLimit
{
    public class CharLimitPreValueModel : PreValueModel
    {
        public CharLimitPreValueModel(string preValues)
            : base(preValues)
        { }
 
        public int CharacterLimit { get; set; }
 
        [AllowDocumentTypePropertyOverride]
        public bool IsRequired { get; set; }
 
        [AllowDocumentTypePropertyOverride]
        public string RegexValidationStatement { get; set; }
 
    }
}

image

An awesome improvement here is that you can mark settings with the AllowDocumentTypePropertyOverride, doing so will make them available on the add / edit property form of the document type

image

EditorModel

The EditorModel class is the class with the properties that a CMS editor will see

using System.Collections.Generic;
using Umbraco.Cms.Model.BackOffice.PropertyEditors;
using System.ComponentModel.DataAnnotations;
using Umbraco.Cms.Model.BackOffice;
using System.Text.RegularExpressions;
using Umbraco.Cms.Web.EmbeddedViewEngine;
 
namespace Umbraco.Addons.PropertyEditors.CharLimit
{
    [EmbeddedView("Umbraco.Addons.PropertyEditors.CharLimit.Views.CharLimitEditor.cshtml", 
        "Umbraco.Addons.PropertyEditors")]
    public class CharLimitEditorModel : EditorModel<CharLimitPreValueModel>, IValidatableObject
    {
 
        public CharLimitEditorModel(CharLimitPreValueModel preValueModel)
            : base(preValueModel)
        {
            
        }
 
        [DisplayFormat(ConvertEmptyStringToNull = false)]
        [ShowLabel(false)]
        public string Value { get; set; }
 
        public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
        {
            //ensure the string is empty and never ‘null’
            if (string.IsNullOrEmpty(Value)) Value = string.Empty;
 
            if (PreValueModel.IsRequired && string.IsNullOrEmpty(Value))
            {
                yield return new ValidationResult(
                    "Value is required", new[] { "Value" });
            }
            else if (!string.IsNullOrEmpty(PreValueModel.RegexValidationStatement) && 
                !Regex.IsMatch(Value, PreValueModel.RegexValidationStatement))
            {
                yield return new ValidationResult(
                    "Value does not match the required pattern", new[] { "Value" });
            }
        }
    }
}

 

note here that you have much more control over the validation

View

@inherits System.Web.Mvc.WebViewPage<Umbraco.Addons.PropertyEditors.CharLimit.CharLimitEditorModel>
@using System.Web.Helpers;
@using System.Web.Mvc;
@using System.Web.Mvc.Ajax;
@using System.Web.Mvc.Html;
@using System.Web.Routing;
@using System.Web.WebPages;
@using Microsoft.Web.Mvc;
@using ClientDependency.Core;
@using ClientDependency.Core.Mvc;
@using Umbraco.Cms.Web;
@using Umbraco.Cms.Web.Editors;
@using Umbraco.Addons.PropertyEditors.CharLimit;
 
@Html.ValidationMessageFor(x => Model.Value)
 
@Html.TextAreaFor(x => Model.Value)
 
@if (Model.PreValueModel.CharacterLimit > 0)
{     
    <br/><span class=’limitstatus’></span>
 
    <script type="text/javascript">
    (function ($) {
        $(document).ready(function () {  
            $(‘#@Html.IdFor(x => Model.Value)’).keyup(function () {
               
                var limit = @Model.PreValueModel.CharacterLimit.ToString();
                if ($(this).val().length > limit) {
                        $(‘.limitstatus’, $(this).parent()).html(‘You cannot write more then ‘ + limit + ‘ characters!’);
                    $(this).val($(this).val().substr(0, limit));
                }
                else {
                        $(‘.limitstatus’, $(this).parent()).html(‘You have ‘ + (limit - $(this).val().length) + ‘ characters left.’);
                }
            });
        });
    })(jQuery);
    </script>
}
 

So for the content editor control I created a razor view, since I’ll need to output a textarea but also some javascript

Want to see more examples of v5 property editors, make sure to take a look at the v5 sourcecode:

http://umbraco.codeplex.com/SourceControl/list/changesets

Transfering Umbraco Contour forms with Courier 2 5

Last week Courier 2 was released, Courier 2 makes it possible to compare and transfer content, doc types, templates, media, … between umbraco instances in a couple of clicks (saving you loads of time when working with multiple environments since it will detect all dependencies, and you won’t have to care about ID conflicts since that is simply handled by courier). You can read more details on it here: http://umbraco.com/products/more-add-ons/courier-2 

But it doesn’t stop with the standard umbraco parts like doc types and templates,… Courier comes with an API that enables you to also support custom data.

Details on how to get started with a custom provider can be found in the sample item provider docs: http://nightly.umbraco.org/UmbracoCourier/Sample%20Itemprovider.pdf
and sample sourcecode
http://nightly.umbraco.org/UmbracoCourier/Sample%20ItemProvider%20Source.zip

So wouldn’t it be great to be able to transfer Contour forms with Courier

image

Based on the sample item provider documentation I wrote a provider for Contour, you can download the assembly here: http://contourprovcourier.codeplex.com/releases/view/67524

Make sure you have Courier 2 and Contour installed on both source and target installation, then simply drop the assembly in the bin directory of both installations.

And you should now be able to transfer forms and changes to forms between instances. Courier will also be able to pick up any dependencies you might have to Contour form, if you are transferring a content node that has a form inside the rte or a template with a form macro on it Courier will know and also transfer the form.

Check it out:

Umbraco Contour and Razor 7

Want to output Contour records with razor instead of xslt well I’ve created some dynamic objects you can use to do so (please note that this is just a primary version of the dynamic objects, this will be improved in a next Contour release)

Here is an example:

@using Contour.Addons.DynamicObjects
 
<ul id="comments">
 @foreach (dynamic record in Library
           .GetApprovedRecordsFromPage(@Model.Id).OrderBy("Created"))
 {
     <li>
          @record.Created.ToString("dd MMMM yyy")
          @if(string.IsNullOrEmpty(record.Website)){
             <strong>@record.Name</strong>
          }
          else{
             <strong>
               <a href="@record.Website" target="_blank">@record.Name</a>
             </strong>
          }
         <span>said</span>
        <p>@record.Comment</p>
     </li>
  }
</ul>

 

This example uses the comment form created from the comment form template and then simply outputs all comments submitted on the current page. To access the field values you can simply use the dot notation (.FieldCaption like @record.Comment)

Download the assembly here: (simply unzip and drop in your bin directory)

Contour.Addons.DynamicObjects.dll.zip

Make sure you are running at least version 4.7 of umbraco since it won’t work on older version and of course have Contour installed.

MASTER OF DATATYPES, the video 4

The video wasn’t available when I posted the slidedeck and sourcecode so now you can also watch the “Master of Datatypes” session. See how  easy it is to create your own custom umbraco datatypes (I’m creating 3 new datatypes in 30 minutes in the video).

Make sure to check out the buug.be site since you can watch all the other sessions from the Umbraco Be Festival to!

http://www.buug.be/en/events/umbraco-festival

Usercontrolwrapper and storing parseable xml data 4

More detailed post related to the ‘master of datatypes’ session on the fact that it is possible to store parseable xml data on your custom datatypes using the usercontrolwrapper.

A feature that was added to the usercontrolwrapper since the umbraco Juno release (4.6) was that if you returned some xml (as a string) it will recognize this and store it as parseable xml data (like the related links datatype or the image cropper datatype, basicly datatypes that store rich data, not just a single value). By storing this as xml it’s easy to work with in both xslt and razor.

As a final piece to the puzzle we also added some helper methods that you can use to serialize an object to xml, found in the umbraco.editorControls.userControlGrapper.SerializationHelper namespace (so no need to write custom to xml methods but simply use the helper methods).

Here is an example:

    public partial class MultipleValuesDatatype : System.Web.UI.UserControl
        , umbraco.editorControls.userControlGrapper.IUsercontrolDataEditor
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            if (!Page.IsPostBack)
                customControl.Values = _umbval;
        }
 
        private List<string> _umbval;
        public object value
        {
            get
            {
                return SerializationHelper.ValueToXmlString(customControl.Values);
            }
            set
            {
                if (value != null && string.IsNullOrEmpty(value.ToString()))
                    _umbval = new List<string>();
                else
                {
                    _umbval = 
                        (List<string>)SerializationHelper.ValueFromXmlString(
                            value, 
                            typeof(List<string>));
                }
            }
        }
    }

The object get’s serialized (it’s of the type list<string> in this case) and the xml representation of the object gets stored.

If you take a look at the xml cache (open the /app_data/umbraco.config file) you can see how the xml representation looks (where openingHours is the alias of the property)

 <openingHours>
      <ArrayOfString 
              xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
        <string>Friday: 20</string>
        <string>Saterday: 21</string>
      </ArrayOfString>
</openingHours>

So in this case I get an ArrayOfString element with a string element for each of the values.

To output this on our frontend we can use some xslt or since the razor implementation in Umbraco 4.7 has awesome support for xml properties we can also use some razor:

<umbraco:Macro runat="server" language="cshtml">
  <ul>
  @foreach(var value in Model.openingHours)
  {
    <li>@value.InnerText</li>
  }
  </ul>
</umbraco:Macro>


So as you see the latest updates to the usercontrol wrapper (configuration with data editor settings and the parseable xml support) make it a lot more useful and there should hardly be a need for using anything else when you want to create a custom Umbraco datatype

Download the entire sourcecode here

MAster of Datatypes 1

If you weren’t at the Belgium Umbraco Festival last friday you missed out on an awesome event! Massive kudos to the buug team for making it happen. As mentioned during my session I would share the slidedeck and sourcecode…

The session was about how easy it can be to create custom umbraco datatypes (that are configurable and store rich data) if you take advantage of the tools that umbraco provides you, the tools being

259790952

Slidedeck:

 

Sourcecode:

Umbraco events in Belgium 1

If you haven’t noticed it yet there are some awesome umbraco events happening in Belgium in the next couple of weeks. The Belgian Umbraco User Group (http://www.buug.be/) has been quite busy!

Here’s a quick overview:

10 March - Umbraco and Azure workshop

Learn how to setup your Umbraco website in Azure during this full day free workshop.

Full details and registration on the buug.be site. (act quick since this is limited to 20 seats)

18 March – Umbraco Be Festival

After the successful UK and DK editions, the Umbraco festival will land in Belgium on Friday 18th March. Confirmed speakers include Umbraco founder Niels Hartvig and v5 lead architect Alex Norcliff.

Full details and registration on the buug.be site.

5 – 8 April – Official level 1 and level 2 training

Want to master Umbraco, join one of the official courses. The next round of official umbraco training in Belgium will be on:

Further details on the umbraco.com site.

Hope to see you there!

Next Page »