Contour 3.0 Code First, Member registration, profile, login, change password
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

With this solution would you not then be storing all you users password in plain text?
@Shannon nope
Is it possible to use this pattern with custom fieldtypes like the re-captcha in contour contrib? Or is it limited only to core fieldtypes?
@martin yup custom fieldtypes is also possible, add reference and then set the type on the field attribute
Is it possible to enable conditions or the additional settings on the fields?
If yes how would we do it?
@Ash check http://www.nibble.be/?p=221
How can we add more fields? I try to apply for iwin and have to copy code
hey,
Is it possible to create form steps using the code first approach?
Thanks, will
@Will yeah if you have different page names in the field attributes
Hey Tim,
We’ve had a nasty time getting this working on a recent project trying to get login, signup etc working. Feels like we are going against the grain. Where best to send feedback, quirks and issues for this sort of stuff so others don’t get as stuck?
Cheers
Pete