Extending User details - am I on the right track?

Topics: Writing modules
Oct 11, 2013 at 1:06 AM
Hey guys, i'm learning orchard/programming and wishing to gather your input for whether i am on the right track. Basically the following is hopefully going to extend the user registration by simply adding names to a member record attached to the User record. I also wish to attach multiple addresses to this member 1 - N; simply, are my foundations on the right track - namely, will Orchard wire up this 1 - N relationship correctly? Thanks for your input, cheers Gil

MODELS:
using Orchard.ContentManagement;
using System;
using Orchard.Security;
using Orchard.Users.Models;


namespace TEST.Registration.Models{
    public class MemberPart : ContentPart<MemberPartRecord>{

        //Member Details
        public string FirstName {
            get { return Record.FirstName; }
            set { Record.FirstName = value; }}

        public string LastName {
            get { return Record.LastName; }
            set { Record.LastName = value; }}

        //User has 'Email' and 'Password'

        //System
        public DateTime? CreatedAt{
            get { return Record.CreatedAt; }
            set { Record.CreatedAt = value; }}

        public IUser User{
            get { return this.As<UserPart>(); }}
        /*
        public UserPart User
        {
            get { return this.As<UserPart>(); }
        }*/
    }
}
using Orchard.ContentManagement.Records;
using System.ComponentModel.DataAnnotations;
using System;

namespace TEST.Registration.Models{
    public class MemberPartRecord : ContentPartRecord{
        //Member Details
        [Required(ErrorMessage = "First name required.")]
        [StringLength(50, ErrorMessage = "First name must be under 50 chars.")]
        public virtual string FirstName { get; set; }

        [Required(ErrorMessage = "Last name required.")]
        [StringLength(50, ErrorMessage = "Last name must be under 50 chars.")]
        public virtual string LastName { get; set; }

        //System
        public virtual DateTime? CreatedAt { get; set; }
    }
}
using System;
using Orchard.ContentManagement.Records;
using System.ComponentModel.DataAnnotations;

namespace TEST.Registration.Models {

    public enum AddressType { Registered = 0, Collection = 1, Delivery = 2 }
    public enum State { ACT, NSW, QLD, SA, TAS, VIC, WA, NT }

    public class AddressPartRecord : ContentPartRecord
    {
        //Type: represents the type of the address (e.g. "Collection", "Delivery").
        public virtual AddressType Type { get; set; }

        //Address Details
        public virtual string Name { get; set; }

        [Required(ErrorMessage = "Street number and address required.")]
        public virtual string Street1 { get; set; }

        public virtual string Street2 { get; set; }

        [Required(ErrorMessage = "City required.")]
        public virtual string City { get; set; }

        [Required(ErrorMessage = "State required.")]
        public virtual State State { get; set; }

        [Required(ErrorMessage = "Post Code required.")]
        public virtual short PostCode { get; set; }

        [Required(ErrorMessage = "Country required.")]
        public virtual string Country { get; set; }

        //System
        public virtual DateTime? CreatedAt { get; set; }

        public virtual MemberPartRecord MemberPartRecord { get; set; }
    }
}
using System;
using Orchard.ContentManagement;

namespace TEST.Registration.Models
{
    public class AddressPart : ContentPart<AddressPartRecord>
    {
        public AddressType Type {
            get { return Record.Type; }
            set { Record.Type = value; }}

        //ADDRESS DETAILS
        public string Name {
            get { return Record.Name; }
            set { Record.Name = value; }}

        public string Street1 {
            get { return Record.Street1; }
            set { Record.Street1 = value; }}

        public string Street2 {
            get { return Record.Street2; }
            set { Record.Street2 = value; }}

        public string City {
            get { return Record.City; }
            set { Record.City = value; }}

        public State State {
            get { return Record.State; }
            set { Record.State = value; }}

        public short PostCode {
            get { return Record.PostCode; }
            set { Record.PostCode = value; }}

        public string Country {
            get { return Record.Country; }
            set { Record.Country = value; }}

        //SYSTEM
        public DateTime? CreatedAt {
            get { return Record.CreatedAt; }
            set { Record.CreatedAt = value; }}
    }
}
MIGRATION
using System;
using Orchard;
using Orchard.ContentManagement;
using Orchard.ContentManagement.MetaData;
using Orchard.Core.Contents.Extensions;
using Orchard.Data.Migration;
using Orchard.Users.Models;


namespace TEST.Registration
{
    public class RegistrationMigration : DataMigrationImpl
    {
        private readonly IOrchardServices _orchardServices;

        public RegistrationMigration(IOrchardServices orchardServices)
        {
            _orchardServices = orchardServices;
        }

        public int Create() {
            SchemaBuilder.CreateTable("MemberPartRecord", table => table
                .ContentPartRecord()
                .Column<string>("FirstName", c => c.WithLength(50))
                .Column<string>("LastName", c => c.WithLength(50))
                .Column<DateTime>("CreatedAt")
            );

            SchemaBuilder.CreateTable("AddressPartRecord", table => table
                .ContentPartRecord()
                .Column<int>("MemberPartRecord_id")
                .Column<string>("Type", c => c.WithLength(50))

                .Column<string>("Name")
                .Column<string>("Street1")
                .Column<string>("Street2")
                .Column<string>("City")
                .Column<string>("State")
                .Column<short>("PostCode")
                .Column<string>("Country")

                .Column<DateTime>("CreatedAt")
            );

            ContentDefinitionManager.AlterPartDefinition("MemberPart",
                builder => builder.Attachable()
                //.Attachable(false)
            );

            ContentDefinitionManager.AlterTypeDefinition("Member", type => type
                .WithPart("MemberPart")
                .WithPart("UserPart")
            );

            ContentDefinitionManager.AlterTypeDefinition("Address", type => type
                .WithPart("CommonPart")
                .WithPart("AddressPart")
            );

            //ORCHARD SETTINGS:
            var registrationSettings = _orchardServices.WorkContext.CurrentSite.As<RegistrationSettingsPart>();
            registrationSettings.UsersCanRegister = true;
            registrationSettings.UsersAreModerated = true;
            
            return 1;
        }
    }
}
Oct 14, 2013 at 2:41 AM
Ok, perhaps I should attack this from another angle. Can anyone recommend a module that allows front-end users to add/edit/delete etc., records of a custom content type so I can inspect the code to try and work out how orchard goes about things? I don't want a solution like Custom Forms, I want to learn/be able to create the module myself. My current aim is to create a simple module that allows members/users to login to a ‘their-account-section’ and check/uncheck newsletter parameters, address details etc.; hopefully simple, but should then equip me with the knowledge to create more complex situations. Any help or guidance will be greatly appreciated. Cheers Gil
Developer
Oct 14, 2013 at 11:05 AM
Just to clear the terminology up a bit the sum of the "records of a custom content type" are called content items. Handling content part records independently of their content item doesn't make sense in most of the cases.

Frontend content item editing is the same as on the backend and basically includes the following tasks:
  1. Fetch the content item or create a new one (IContentManager.New(), no persistence yet).
  2. Build its editor shape (IContentManager.BuildEditor()).
  3. Display the editor shape inside a form.
  4. In the action where the editor is posted to fetch of create the item (as in 1.). Here also persist the item if it's new.
  5. Update the editor of the content item (IContentManager.UpdateEditor()).
  6. Check if model state is valid, if not, cancel the transaction.
See: https://orchardtrainingdemo.codeplex.com/SourceControl/latest#Controllers/ContentsAdminController.cs
Oct 14, 2013 at 10:38 PM
Wow! Thank you very much for that demo link and succinct reply Piedone - I'm sure that unbelievably well documented module will be perfect for learning Orchard [especially correct terminology ;) ]. Can't wait to get home and tear it apart, many thanks Gil. :)
Developer
Oct 14, 2013 at 11:36 PM
Glad, have fun :-).