Nibble

Archive for the 'Uncategorized' Category


Single page CRUD app with Umbraco 6 1

A little experiment in using Umbraco as a datasource for a single page CRUD app. Making use of the new Umbraco APIs (ContentService and UmbracoAPIController).

 

Umbraco setup

Using Umbraco 6.1 beta (since that includes the UmbracoApiController).

Default rendering engine is set to MVC (this is setup in the /config/umbracoSettings.config).

Two doctype are in place

    • HomePage (with a property footer of type richtext editor)
    • Status (with a property message of type textbox multiple)

So this is how the content structure looks (and with the app we’ll be able to list/create/update/delete status content docs)

image

Creating the model

The data that we’ll pass between client and server, It’s a pretty simple model with only a couple of properties (the id, need that to be able to edit, and 2 other properties that will be used for the content name and the message field).

   public class Status
    {
        public int Id { get; set; }
        public string Title { get; set; }
        public string Message { get; set; }
 
    }

 

Creating the API

Since we want to create a RESTful application the obvious choice would be to use WEB API and use a API controller, you can do that in Umbraco, there is a base Web Api controller you can inherit from that will expose Umbraco related services and objects, the UmbracoApiController (available since 6.1 beta)

It’s important that the controller class name is suffixed with "ApiController"

public class StatusApiController : UmbracoApiController
{
}

With that in place we can start adding the methods that we’ll need

Fetching documents

For querying published content we’ll be working with strongly typed Content (of type IPublishedContent).

Getting all status docs underneath a certain parent and mapping them to the type of our model

 public IEnumerable<Status> GetAllStatuses(int parentId)
        {
            UmbracoHelper help = new UmbracoHelper(UmbracoContext);
            return help.TypedContent(parentId)
                .Children
                .Where(c => c.DocumentTypeAlias == StatusDocTypeAlias)
                .Select(obj => new Status()
            {
                Title = obj.Name,
                Message = obj.GetPropertyValue<string>(MessagePropertyAlias),
                Id = obj.Id
            });
        }

 

Getting a doc by it’s id and mapping it to our model type

        public Status GetStatus(int id)
        {
            UmbracoHelper help = new UmbracoHelper(UmbracoContext);
            var content  = help.TypedContent(id);
            return new Status
            {
                Id = content.Id,
                Title = content.Name,
                Message = content.GetPropertyValue<string>(MessagePropertyAlias)
            };
        }

Create/Update/Delete documents

For this we’ll make use of the new APIs available in v6, the new API consists of a number of services since we are interacting with content we’ll be using the ContentService. Because our controller inherits from the UmbracoAPIController we can easily access the ContentService with Services.ContentService

Creating a new doc (+ setting prop value + save and publish)

 public Status PostStatus(Status status, int parentId)
        {
            var cs = Services.ContentService;
            var content = cs.CreateContent(status.Title, parentId, StatusDocTypeAlias);
            content.Name = status.Title;
            content.SetValue(MessagePropertyAlias, status.Message);
            cs.SaveAndPublish(content);
            status.Id = content.Id;
            return status;
        
        }

Updating a doc

 public Status PutStatus(Status status)
        {
            var cs = Services.ContentService;
            var content = cs.GetById(status.Id);
            content.Name = status.Title;
            content.SetValue(MessagePropertyAlias, status.Message);
            cs.SaveAndPublish(content);
            return status;
 
        }

Deleting a doc

 public void DeleteStatus(int statusId)
        {
            var cs = Services.ContentService;
            var content = cs.GetById(statusId);
            cs.Delete(content);
        }

 

The App

To create the actual app that will make use of our api   I used AngularJS, that made it pretty simple.

To show a small snippet (http delete against the DeleteStatus method)

            $http({ method: ‘DELETE’, url: ‘/umbraco/api/StatusApi/DeleteStatus/’, params: { statusId: $routeParams.Id } })
                .success(function (data) {
                    
                    $rootScope.alert = true;
                    $rootScope.alertMessage = "Status removed";
                    
                    $location.path(‘/list’);
                })
                .error(function () {
                    $scope.error = "An Error has occured while deleting!";
                });

For an intro on AngularJS check http://www.egghead.io/ 

For the UI Bootstrap is used

Source code

Complete example site and source code is available on github

https://github.com/TimGeyssens/UmbracoSinglePageCrudApp

Contour code first and Conditional Logic 3

Another code first example, setting up field conditions when designing your form in visual studio.

There are 3 properties on the Field attribute that need to be used (EnableCondition, ConditionActionType and ConditionLogicType) then for setting up 1 or multiple rules you’ll need to decorate the property with a FieldConditionRule attribute providing it the caption of the field, the rule operator and the value.

        [Field("Leave a comment", "Your comment",
            Mandatory = true)]
        public string Name { get; set; }
 
        [Field("Leave a comment", "Your comment",
            EnableCondition = true,
            ConditionActionType = FieldConditionActionType.Show,
            ConditionLogicType = FieldConditionLogicType.Any)]
        [FieldConditionRule("Name",FieldConditionRuleOperator.Is, "Test")]
        public string Hidden { get; set; }

In this case the field with caption hidden will only be shown if the value of the field with caption Name is test

Contour code first and custom fieldtypes 4

A question that popped up several times regarding the new Contour code first feature is if it’s possible to use custom fieldtypes.

It sure is, take a look at the following example. Let’s say we want to use a fieldtype from the Contour Contrib project.

First we’ll need to add a reference to the assembly containing the custom fieldtype we want to use

image

Once that is in place we can simply set the type of field to the type of the fieldtype you wish to use

[Field("Leave a comment", "Your comment",
           Mandatory = true,
           Type = typeof(Contour.Contrib.FieldTypes.ReCaptcha))]
        public string Captcha { get; set; }

And that’s basically it!

If the custom fieldtype has additional settings you can also provide a value for those with the following syntax

 [Field("Leave a comment", "Your comment", 
           "Language:en","Theme:clean",
           Mandatory = true,
           Type = typeof(Contour.Contrib.FieldTypes.ReCaptcha))]
        public string Captcha { get; set; }

 

Of course to deploy this we’ll need to deploy the necessary references (like contour contrib and recaptcha in this case) to your umbraco site.

Extending Contour, multilingual countries prevalue source 4

A question that got my attention this week is what would be the best way to add a multilingual list of countries to a Contour form.

ContourCountries

So this post explains how you can extend contour with a custom prevalue source type

The data

Of course we need the multilingual data, after some research I found the Unicode Common Locale Data Repository http://cldr.unicode.org/ it’s basically a collection of xml files with locale data, including a list a countries

The Code

The code is pretty simply it just uses the current culture the fetch the correct xml file (falling back to English if a culture xml isn’t found) and then it loops trough the territory nodes found in the xml.

using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Web;
using System.Xml;
using Umbraco.Forms.Core;
 
namespace Contour.Addons.Countries
{
    public class PrevalueSource : FieldPreValueSourceType 
    {
        public PrevalueSource()
        {
            this.Id = new Guid("9ED6C6D7-16A7-4087-A75D-B936D6FAE5A9");
            this.Name = "List of countries";
            this.Description = "Multilingual list of countries (http://unicode.org/ files need to be in /app_data/countrydata/)";
        }
 
        public override List<Exception> ValidateSettings()
        {
            var ex = new List<Exception>();
 
            if (!File.Exists(HttpContext.Current.Server.MapPath("/App_Data/CountryData/common/main/en.xml")))
                ex.Add(new Exception("Unicode Common Locale Data Repository not found, please download from http://cldr.unicode.org and place in /app_data/countrydata/ directory"));
            
            return ex;
        }
 
        public override List<PreValue> GetPreValues(Field field)
        {
            var pvs = new List<PreValue>();
 
            var d = new XmlDocument();
            if (File.Exists(HttpContext.Current.Server.MapPath(
                string.Format("/App_Data/CountryData/common/main/{0}.xml",
                              CultureInfo.CurrentCulture.TwoLetterISOLanguageName))))
            {
                d.Load(HttpContext.Current.Server.MapPath(
                    string.Format("/App_Data/CountryData/common/main/{0}.xml",
                                  CultureInfo.CurrentCulture.TwoLetterISOLanguageName)));
            }
            else
            {
                d.Load(HttpContext.Current.Server.MapPath(
                  "/App_Data/CountryData/common/main/en.xml"));
            }
 
            var countries = new List<KeyValuePair<string, string>>();
            
            foreach (XmlNode c in d.SelectNodes("//territory [string-length(string(@type)) < 3]"))
                countries.Add(new KeyValuePair<string, string>(c.Attributes["type"].Value, c.InnerText));
 
            countries.Sort((firstPair, nextPair) => String.Compare(firstPair.Value, nextPair.Value, StringComparison.Ordinal));
 
            int sort = 0;
            foreach (var c in countries)
            {
                PreValue pv = new PreValue();
                pv.Id = c.Key;
                pv.Value = c.Value;
                if (field != null)
                    pv.Field = field.Id;
                pv.SortOrder = sort;
                pvs.Add(pv);
 
                sort++;
            }
 
            return pvs;
        }
 
    }
}

 

Code is available on bitbucket https://bitbucket.org/starfighter83/contour.addons.country

The package

The countries prevalue source is available as a package and can be used on your current Contour projects http://our.umbraco.org/projects/backoffice-extensions/contour-countries

Contour 3.0 Code First, Member registration, profile, login, change password 10

The upcoming Contour 3.0 release features a new code first framework that is outlined in this post on umbraco.com

To add some more examples I’ve updated the example with some additional member forms:

Profile form

using System;
using System.Collections.Generic;
using Umbraco.Forms.CodeFirst;
using Umbraco.Forms.Core.Providers.FieldTypes;
using umbraco.cms.businesslogic.member;
 
namespace Contour.CodeFirstExample
{
    [Form("Member/Profile", ShowValidationSummary = true, MessageOnSubmit = "Profile updated!")]
    public class Profile: FormBase
    {
        [Field("Profile", FormFieldsets.Details,
            Mandatory = true,
            DefaultValue = "{member.name}")]
        public string Name { get; set; }
 
        [Field("Profile", FormFieldsets.Details,
            Mandatory = true,
            Regex = @"(\w[-._\w]*\w@\w[-._\w]*\w\.\w{2,3})",
            DefaultValue = "{member.email}")]
        public string Email { get; set; }
 
        [Field("Profile", FormFieldsets.Details,
            Type = typeof(FileUpload),
            DefaultValue = "{member.avatar}")]
        public string Avatar { get; set; }
 
        public override IEnumerable<Exception> Validate()
        {
            var e = new List<Exception>();
 
            var m = Member.GetCurrentMember();
 
            if (m != null)
            {
                if (m.Email != Email)
                {
                    if (Member.GetMemberFromLoginName(Email) != null)
                        e.Add(new Exception("Email already in use"));
                }
            }
 
            return e;
 
        }
 
        public override void Submit()
        {
            var m = Member.GetCurrentMember();
 
            if (m != null)
            {
                m.Email = Email;
                m.LoginName = Email;
                m.Text = Name;
                //asign custom properties
                if (!string.IsNullOrEmpty(Avatar))
                    m.getProperty("avatar").Value = Avatar;
            }
        }
    }
}

 

Change password form

using System;
using System.Collections.Generic;
using Umbraco.Forms.CodeFirst;
using Umbraco.Forms.Core.Providers.FieldTypes;
using umbraco.cms.businesslogic.member;
 
namespace Contour.CodeFirstExample
{
    [Form("Member/Change password", ShowValidationSummary = true, MessageOnSubmit = "Password updated!")]
    public class ChangePassword: FormBase
    {
        [Field("Change password", "",
            Type = typeof(Password),
            Mandatory = true)]
        public string Password { get; set; }
 
        [Field("Change password", "",
            Type = typeof(Password),
            Mandatory = true)]
        public string RepeatPassword { get; set; }
 
        public override IEnumerable<Exception> Validate()
        {
            var e = new List<Exception>();
         
            //makes sure the passwords are identical
            if (Password != RepeatPassword)
                e.Add(new Exception("Passwords must match"));
 
            return e;
        }
 
        public override void Submit()
        {
            var m = Member.GetCurrentMember();
 
            if(m != null)
                m.Password = Password;
        }
    }
}

 

Login form

using System;
using System.Collections.Generic;
using Umbraco.Forms.CodeFirst;
using Umbraco.Forms.Core.Providers.FieldTypes;
using umbraco.cms.businesslogic.member;
 
namespace Contour.CodeFirstExample
{
    [Form("Member/Login", ShowValidationSummary = true, MessageOnSubmit ="You are now logged in")]
    public class Login: FormBase
    {
        [Field("Login", "",
           Mandatory = true,
           Regex = @"(\w[-._\w]*\w@\w[-._\w]*\w\.\w{2,3})")]
        public string Email { get; set; }
 
        [Field("Login", "",
            Type = typeof(Password),
            Mandatory = true)]
        public string Password { get; set; }
 
 
        public override IEnumerable<Exception> Validate()
        {
            var e = new List<Exception>();
 
            if(Member.GetMemberFromLoginName(Email) == null)
                e.Add(new Exception("No member found with that email address"));
            else if (Member.GetMemberFromLoginNameAndPassword(Email, Password) == null)
                e.Add(new Exception("Incorrect password"));
 
            return e;
        }
 
        public override void Submit()
        {
           var m = Member.GetMemberFromLoginNameAndPassword(Email, Password);
           if (m != null)
               Member.AddMemberToCache(m);
        }
    }
}

 

Registration form

using System;
using System.Collections.Generic;
using Umbraco.Forms.CodeFirst;
using umbraco.cms.businesslogic.member;
using umbraco.BusinessLogic;
using Umbraco.Forms.Core.Providers.FieldTypes;
 
namespace Contour.CodeFirstExample
{
    
    public enum FormPages
    {
        Registration
    }
 
    public enum FormFieldsets
    {
        Details
    }
 
    [Form("Member/Registration", ShowValidationSummary = true, MessageOnSubmit="You are now registered!")]
    public class Registration: FormBase
    {
        public const string MemberTypeAlias = "Member";
        public const string MemberGroupName = "Authenticated";
 
        [Field(FormPages.Registration,FormFieldsets.Details,
            Mandatory= true)]
        public string Name { get; set; }
 
        [Field(FormPages.Registration, FormFieldsets.Details,
            Mandatory = true,
            Regex = @"(\w[-._\w]*\w@\w[-._\w]*\w\.\w{2,3})")]
        public string Email { get; set; }
 
        [Field(FormPages.Registration, FormFieldsets.Details, 
            Type = typeof(Password),
            Mandatory = true)]
        public string Password { get; set; }
 
        [Field(FormPages.Registration, FormFieldsets.Details, 
            Type = typeof(Password),
            Mandatory = true)]
        public string RepeatPassword { get; set; }
 
        [Field(FormPages.Registration, FormFieldsets.Details, 
            Type = typeof(FileUpload))]
        public string Avatar { get; set; }
 
        public override IEnumerable<Exception> Validate()
        {
            var e = new List<Exception>();
            //checks if email isn’t in use
            if(Member.GetMemberFromLoginName(Email) != null)
                e.Add(new Exception("Email already in use"));
            //makes sure the passwords are identical
            if (Password != RepeatPassword)
                e.Add(new Exception("Passwords must match"));
 
            return e;
        }
        public override void Submit()
        {
            //get a membertype by its alias
            var mt = MemberType.GetByAlias(MemberTypeAlias); //needs to be an existing membertype
            //get the user(0)
            var user = new User(0);
            //create a new member with Member.MakeNew
            var member = Member.MakeNew(Name, mt, user);
            //assign email, password and loginname
            member.Email = Email;
            member.Password = Password;
            member.LoginName = Email;
            //asign custom properties
            if(!string.IsNullOrEmpty(Avatar))
                member.getProperty("avatar").Value = Avatar;
            //asssign a group, get the group by name, and assign its Id
            var group = MemberGroup.GetByName(MemberGroupName); //needs to be an existing MemberGroup
            member.AddGroup(group.Id);
            //generate the member xml with .XmlGenerate
            member.XmlGenerate(new System.Xml.XmlDocument());
            //add the member to the website cache to log the member in
            Member.AddMemberToCache(member);
            
        }
    }
}

The code assumes there is a membertype called Member with a additional property with the alias avatar and a membergroup called Authenticated

For more member properties the code needs to be updated…

After deploying you should end up with the following forms

image

Full sourcecode is available here

Restricted Upload datatype for Umbraco 4

Another friday another project Smile , this time an improved upload datatype that will allow you to restrict the upload for your content editor.

The settings will allow you to provide a maximum file size (in KB) and the allowed file extension(s)

image

When the file isn’t valid the editor will get a notification

image

That’s it Smile

The package is available on our.umbraco.org

Embedding third party media in Umbraco 1

Since Umbraco v4.9 embedding third party media in the RTE has gotten a lot easier, it’ just a matter of supplying the Url to the video/image/sound/poll/… you want to add setting the size and hitting insert!

image

This is done by making use of the oembed format so every site that support the format can be supported by the dialog in a minute  (if it isn’t already) Smile

All this is setup in the new config file /config/EmbeddedMedia.config, for each supported third party site there is an entry in the config file

  <!-- Youtube Settings -->
  <provider name="Youtube" type="Umbraco.Web.Media.EmbedProviders.OEmbedVideo, umbraco">
    <urlShemeRegex><![CDATA[youtu(?:\.be|be\.com)/(?:(.*)v(/|=)|(.*/)?)([a-zA-Z0-9-_]+)]]></urlShemeRegex>
    <apiEndpoint><![CDATA[http://www.youtube.com/oembed]]></apiEndpoint>
    <requestParams type="Umbraco.Web.Media.EmbedProviders.Settings.Dictionary, umbraco">
      <param name="iframe">1</param>
      <param name="format">xml</param>
    </requestParams>
  </provider>

The provider that needs to be used (most of the time it’s oembed), a regex that will match the supported urls, the api endpount for the oembed stuff and some optional request params if needed.

So if a third party site supports oembed and isn’t supported by the insert third party media dialog yet it’s just a case if adding an entry to the config file

But the implementation isn’t fixed to oembed it’s also possible to plug in custom providers that don’t make use of the oembed format

Look at the following entry for twitgoo

  <!-- Twitgoo Settings , not an OEmbed one -->
  <provider name="Twitgoo" type="Umbraco.Web.Media.EmbedProviders.Twitgoo, umbraco">
    <urlShemeRegex><![CDATA[twitgoo\.com/]]></urlShemeRegex>
  </provider>

That makes use of a custom provider that looks like

using HtmlAgilityPack;
 
namespace Umbraco.Web.Media.EmbedProviders
{
    public class Twitgoo : AbstractProvider
    {
        public override bool SupportsDimensions
        {
            get
            {
                return false;
            }
        }
 
        public override string GetMarkup(string url, int maxWidth, int maxHeight)
        {
            var web = new HtmlWeb();
            var doc = web.Load(url);
 
            var img = doc.DocumentNode.SelectSingleNode("//img [@id = ‘fullsize’]").Attributes["src"];
 
            return string.Format("<img src=\"{0}\"/>",
               img.Value);
        }
    }
}

Which will basically search for the element with id full-size and return an image using the src attribute of the full-size element found.

So you can also write your own providers for sites you wish to support Smile

Create/Update COntour forms code first 1

Another result of freedom Fridays at the Umbraco HQ, an addon for Umbraco Contour the official form builder that will allow you to create/update forms from code.

It’s heavily inspired by uSiteBuilder (they did all the hard work).

With this add-on you will be able to design your Contour forms from visual studio, here is a simple example:

   public enum FormPages
    {
        Contact
    }
 
    public enum FormFieldsets
    {
        Details
    }
 
    [Form("Another Contact Form", MessageOnSubmit = "Thank you")]
    public class AnotherContactForm : FormBase
    {
        [Field(FormPages.Contact, FormFieldsets.Details,
           Mandatory = true)]
        public string Name { get; set; }
 
        [Field(FormPages.Contact, FormFieldsets.Details,
            Mandatory = true)]
        public string Email { get; set; }
 
        [Field(FormPages.Contact, FormFieldsets.Details,
           Mandatory = true,
           Type = typeof(Umbraco.Forms.Core.Providers.FieldTypes.Textarea))]
        public string Message { get; set; }
 
    }

This code will result in the following form

image

It’s also possible to attach third party functionality either by attaching workflows from code

        public override List<WorkflowBase> Workflows
        {
            get
            {
                return new List<WorkflowBase> {
                        new Email()
                    };
            }
        }

With the email class being as follows

    [Workflow("Send Email",FormState.Submitted)]
    public class Email: WorkflowBase
    {
 
        public override WorkflowType Type
        {
            get
            {
                return new Umbraco.Forms.Core.Providers.WorkflowTypes.SendEmail
                    {
                        Email = "test@test.com",
                        Subject = "the form Contact Form was submitted",
                        Message = "the form Contact Form was submitted, " +
                                    "this is the list of values it contained, " +
                                    "you can turn this email off under workflows in Umbraco Contour"
                    };
            }
        }
    }

Or by overriding the submit method (form class properties will be populated with the record values Smile )

        public override void Submit()
        {
            
            umbraco.library.SendMail(
                Email,
                "me@company.com",
                "New Contact",
                string.Format("New message from {0} : {1}",Name,Message),
                false);
        }

The main advantages of this code first approach is that forms and workflows can be source controlled, support for unit testing, support for automated deployments, easy form maintenance through code…

The add-ons and some example forms are available on the project page at our.umbraco.org

Full sourcecode is available here

For a quick demo watch:

Moving from Umbraco V4 to v5 : Installation 1

First post in a hopefully large series on the differences and similarities between Umbraco v4 (webforms) and Umbraco v5 (MVC). So this assumes you’ve already used Umbraco v4 and are starting with Umbraco v5 (although it should also be valuable if you don’t have previous experience and just want to get started with Umbraco v5).

As an obvious first post we’ll take a look at the installation of Umbraco v5.

There are different ways to install Umbraco but here I’ll outline my preferred option, downloading the latest stable release from codeplex and using webmatrix to get up and running.

To get details on different installation methods( iis and web platform installer, nuget, visual studio template, manually, …) check out the getting started page on the v5 documentation.

Downloading

You can always find the latest stable release on http://umbraco.codeplex.com/releases, at the time of this blogpost that’s Umbraco 5.0.1. So simply download the recommended download (the web application).

image

There is also an option to get a nightly build (so a work in progress version) you can find those at http://nightly.umbraco.org/ (of course there is no guaranty that the version you’ll download will work 100% since it’s a current development version).

Once the download is successful you should end up with an archive if you unzip that you should have a file structure similar to the following screenshot.

image

Firing up

Now to get Umbraco running I’ll be using WebMatrix, if you don’t have it installed you can get it at the following site http://www.microsoft.com/web/webmatrix/ (basically WebMatrix will make it easy to get up and running without having to worry about all the dependencies, you might have to install Umbraco once trough the web gallery to make sure the dependencies get installed).

WIth webmatrix installed I can simply fire it up and have it ready to run my site if I open the context menu on the Umbraco web application archive folder.

image

After hitting the ‘”Open as a web site with Microsoft WebMatrix” option you should end up with an instance of WebMatrix that has the Umbraco site open (tree at the middle left should show you the files).

image

So all that’s left is to hit the run button in the webmatrix toolbar.

image

Webmatrix will open a new browser window or will add a new tab to an existing window and you should see the following

image

So now we’ll have to launch the Umbraco installation wizard by clicking 1 of the 2 links.

Installation wizard

The installation wizard looks pretty much the same as it did in Umbraco v4 with the big difference being that there are less steps in the installation process (no license and no skins step).

image

After hitting “Lets get started!” We need to specify the db that we want to use. Currently you can choose for SQL Server 2008 (and higher versions), SQL CE 4, or a custom connection string.

Still pretty similar to the v4 installer just that SQL Server 2005 isn’t supported and that there isn’t support for MySQL at this point. If you choose the SQL CE 4 option you won’t need to add additional files like you needed when installing v4 on SQL CE. So to get up an running without having to worry about the db simply choose SQL CE 4 and hit install.

image

After hitting install you should see the installation progress and once it’s finished you can continue to the next step.

image

In the create user step you’ll have to specify the admin user details (this is exactly the same as on v4).

image

After the user step you’ll be able to install a starter kit, this will install a sample starter site. Currently there is only 1 you can choose and there is no option to choose a skin for it (so different to v4 at the moment).

image

Once that step is completed the installation is done

image

Accessing Umbraco

After a successful installation it’s possible to go to the Umbraco backend and that’s still located at /Umbraco

image

So after providing the user credentials (that where setup in the create user section) you should have access to the Umbraco backend (again looking very familiar).

image

Conclusion

If you have experience with setting up an Umbraco v4 it al feels very familiar and you should be up and running in a couple of minutes.

Extending Umbraco Contour 2.0 for Umbraco 5, creating a custom field type 3

Like mentioned in the previous post there are some changes in Contour 2.0 when creating custom field types for Contour running on Umbraco v5. So in this post I’ll show a first complete example.

A custom field type consists of 2 main things:

  • A class containing the definition
  • The view that will be used to render the fields of the custom field types

Fieldtype class

So similar to how it is when extending Contour 1.x you’ll need a class that inherits from Umbraco.Forms.Core.FieldType (need to reference Umbraco.Forms.Core).

public class CustomFieldType: FieldType


In the constructor you’ll need to set the id of the provider.

this.Id = new Guid("4EF14104-2334-42F4-85D2-C426CA2800C8");


And some stuff that will be used by the form designer, the name (will appear in the field type selector of the form designer), the icon (also shown in the form designer).

this.Name = "Custom fieldtype";
this.Icon = "../../../../ExampleFieldType/Content/Images/custom.png";


Will result in the following (assuming that the icon is located at \App_Plugins\Packages\ExampleFieldType\Content\Images~\custom.png

image

You’ll also have to specify the html code that will be shown as a preview in the form designer

 public override string RenderPreview()
{
    return "<input type=\"text\" class=\"textfield\" value=\"custom preview\" />";
 }

Will result in the following preview shown on the form designer:

image

Here is the full code snippet (you can download the source at the bottom of this post)

using System;
using System.Collections.Generic;
using Umbraco.Forms.Core;
 
namespace ContourV5.ExampleFieldType
{
    public class CustomFieldType: FieldType
    {
        public CustomFieldType()
        {
            this.Id = new Guid("4EF14104-2334-42F4-85D2-C426CA2800C8");
            this.Name = "Custom fieldtype";
            this.Description = "Renders an example fieldtype";
 
            this.Icon = "../../../../ExampleFieldType/Content/Images/custom.png";
            this.DataType = FieldDataType.LongString;
        }
 
        public override string RenderPreview()
        {
            return "<input type=\"text\" class=\"textfield\" value=\"custom preview\" />";
        }
 
        public override string RenderPreviewWithPrevalues(List<object> prevalues)
        {
            return RenderPreview();
        }
 
 
    }
}

 

Fieldtype view

Another part that is needed is a view that will be used to output the fields of your custom fieldtype.

The minimum, so important here is to use the FieldViewModel (need to reference Umbraco.Forms.MVC) and that the name of the input is set to @Model.Name since this will be used to fetch and save the value.

@model Umbraco.Forms.MVC.Models.FieldViewModel
 
@{
    Layout = null;
}
 
<input type="text" name="@Model.Name" id="@Model.Id" class="text" value="" />

 

If you also want to support record editing and showing the stored value when moving back and forth in multi step forms you’ll need to check if there are already record values and then set the value of your control

@model Umbraco.Forms.MVC.Models.FieldViewModel
 
@{
    Layout = null;
 
    string val = string.Empty;
   
    if(Model.RecordValues != null)
    {
        val = Model.RecordValues.First().ToString();
    }
}
 
<input type="text" name="@Model.Name" id="@Model.Id" class="text" value="@val" />

 

And in order to support client side validation there are also some additions to be made (extra attributes)

@model Umbraco.Forms.MVC.Models.FieldViewModel
 
@{
    Layout = null;
 
    string val = string.Empty;
   
    if(Model.RecordValues != null)
    {
        val = Model.RecordValues.First().ToString();
    }
}
 
<input type="text" name="@Model.Name" id="@Model.Id" class="text" value="@val"
@{if(Model.Field.Mandatory) 
{
      <text>data-val="true" data-val-required="@Model.RequiredErrorMessage"</text>
}
}
/>

 

Adding the custom fieldtype

You’ll of course need to have Contour installed first, once that is installed you can add your custom fieldtype. To keep a clean overview of the extensions installed on your Umbraco v5 site simply create a new folder in the /App_Plugins/Packages directory

image

Once that is created drop your assembly in the lib subfolder (create it first)

image

You can then drop other resources like the field type icon in your custom directory (make sure this matches the path of the icon property on your fieldtype).

image

The view however needs to go in the

/App_Plugins/Packages/Contour/Views/Partial/ directory and needs to be named following the convention FieldType.FieldTypeClassName.cshtml

image

Porting existing fieldtypes to Contour 2.0 for Umbraco v5

Basically you’ll just have to create the view and if you want to do some additional processing of the value(s) also override the ProcessValue method on your field type class.

Download the example project

Next Page »