Nibble

Archive for November, 2012

Extending Contour, multilingual countries prevalue source 5

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 16

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 5

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