Nibble

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

image

Full sourcecode is available here

17 Comments so far

  1. Shannon on November 30th, 2012

    With this solution would you not then be storing all you users password in plain text?

  2. Tim Geyssens on November 30th, 2012

    @Shannon nope

  3. martin on December 4th, 2012

    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?

  4. Tim Geyssens on December 6th, 2012

    @martin yup custom fieldtypes is also possible, add reference and then set the type on the field attribute :)

  5. Ash on December 7th, 2012

    Is it possible to enable conditions or the additional settings on the fields?
    If yes how would we do it?

  6. Tim Geyssens on December 21st, 2012
  7. Maxime on February 4th, 2013

    How can we add more fields? I try to apply for iwin and have to copy code

  8. Will on February 4th, 2013

    hey,

    Is it possible to create form steps using the code first approach?

    Thanks, will

  9. Tim Geyssens on April 9th, 2013

    @Will yeah if you have different page names in the field attributes

  10. Pete Duncasnon on April 15th, 2013

    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

  11. Dan on August 2nd, 2013

    I’m using Umbraco 6.0.5 and Contour 3.0.12 and the reg form works fine except no custom properties are being saved. Help!

  12. Gavin on November 8th, 2013

    For the Login the Password and UserName is being stored in clear text in the UFRecordDataString table. Is there anyway to prevent this ?

    I’ve tried the option StoreRecordsLocally=false but that does seem to effect it.

    Thanks,
    Gavin

  13. Dan Evans on January 8th, 2014

    Hi Tim

    These forms are great, however all users login info (passwords in particular) are saved in Contour in plain text. Is there an easy way to stop Contour saving this information as it’s a bit of a security risk.

    Thanks

    Dan

  14. Tim Geyssens on January 8th, 2014

    @Dan, yeah you can specify to not store locally on the form attribute

  15. Jacob Polden on April 24th, 2014

    For some reason only one of my contour forms renders correctly at a time. If I restart the app pool they alternate consistently, even the count of field sets changes in the database. They are two different classes in the same namespace and compiled into one dll. Is that okay or should I be breaking up each form into a separate dll?

  16. Chen on July 21st, 2014

    Hi Tim,

    Does the above code examples work in Umbraco 7? Thank you.

    Chen

  17. Tony Gayter on November 18th, 2014

    When I add the StoreRecordsLocally on the form I get the following error, looks like its not set for a load balanced environment

    Unable to serialize the session state. In ‘StateServer’ and ‘SQLServer’ mode, ASP.NET will serialize the session state objects, and as a result non-serializable objects or MarshalByRef objects are not permitted. The same restriction applies if similar serialization is done by the custom session state store in ‘Custom’ mode.

Leave a Reply