Splitting Registration View into multi-page [wizard]?

Topics: Writing modules
Feb 23, 2012 at 4:34 AM

Hi guys, I’m using Extended Registration and Contrib.Profile with a Module I created which adds a custom part to the User content type for adding additional fields during registration. All works well, but I was wondering if there is a way to split up my registration view into multiple pages? I wish to utilise jquery code that I’ve already created that hide/shows divs [acting as pages]. Currently my registration page displays every field in my custom part, of which - there are many I don’t wish to be shown [currently display my backend editor]. So, what is the correct way to swap this backend view with an amended front end view when a user registers; ‘I know using a whole host of ‘ifs’ within the view itself would be a terrible idea’.

This thread’s outcome is very similar to what I wish to achieve:

http://orchard.codeplex.com/discussions/284520

so is ‘groupid’ something I should look into? Or is there an easier way to insert the appropriate view? Thanks in anticipation, cheers Dyr

Feb 23, 2012 at 10:07 PM
Edited Feb 24, 2012 at 1:50 AM

Ok guys, experimenting with my own view now, obviously I can register a new user and collect Username / Email / Password residing in @Model.UserProfile.ContentItem.UserPart, but was wondering how I would go about attaching Textbox’s to enter my other custom fields, as in the ones that reside in: @Model.UserProfile.ContentItem.MemberPart.etc and have them saved to the db as well? Cheers Dyr

PS> Would creating another specific 'register' display in my driver be a better approach? Or should I investigate Alternates perhaps?

Mar 2, 2012 at 4:15 AM

Hi guys, Let’s start again -- I’ve been struggling on/off for the last 2 months trying to get my module ‘custom registration’ up and running. First of all, I don’t wish to use ‘Extended Registration’. I have my part attached to user and all is splendid, so now I wish to enable registration creation.

  • Being that my part already has a relationship with user, is there a way to insert ‘my parts’ fields at the same time of user creation *easily*..
  • Or, do I have to create a mypart.create service that is run just after/during successful user creation?

I'm really quite desperate for some good advice, cheers Dyr

Developer
Mar 2, 2012 at 7:59 AM
Edited Mar 2, 2012 at 10:45 PM

The way I would do it is by simply creating my own controller, which takes whatever data I wanted it to. Then use IUserService to actually create the user. You can see an example here: http://skywalkersoftwaredevelopment.net/blog/writing-an-orchard-webshop-module-from-scratch-part-8

Although it requires some work on your part, it's really simple to do and provides the greatest flexibility in terms of application specific requirements.

Mar 2, 2012 at 10:26 PM

Hi Dyr.  I think I'm on the same journey as you, & skywalkers tutorials have been fantastic (thankyou :-)). 

You don't say why you don't want to use the Extended Registration module, also are you still trying for the multi-page?  (I gave up on that for the time being, didn't have much success with groupid).

My registration form's still pretty new but I've got a solution that works for me.  I wanted to use as much existing orchard functionality as possible so I didn't create my own controller (rightly or wrongly I'm not sure yet), I'm also learning MVC at the same time as Orchard so it's been very steep. 

I ended up inheriting Orchard.Users.Controllers.AccountController with my own controller so I wouldn't have to duplicate the fairly extensive private methods in there.  I have 2 actions in my controller, RegisterMember & [HttpPost]RegisterMember, which both make use of the base Register actions, and then add in the MemberPart bits. 

So this all works great with pretty minimal code, but then the next problem is what to do about all the extra non-essential fields I'm adding to MemberPart which I don't need/want to show on the registration form?  What I've ended up doing is creating url-alternate based views in my theme (not module) to basically comment them out. It took me a while to get my head around this, and as I said it's still pretty new, but I think it works.  The end result is that I've got a fairly nice looking one page registration form that asks for email address & essential MemberPart details.  

I now have one more requirement - our members are required to agree to make a minimum no. of offers as part of their membership, so I need to also include some NoticeParts on the rego form, the notices aren't part of the User, only owned by the user, so that's the next step for me. 

Good luck with your form, and if you'd like to see my code, I'll post it here, I only didn't do it this morning cos I'm working on that next step at the mo :-)

Mar 5, 2012 at 3:03 AM

I’m ever so grateful for your help guys – so please don’t assume my tardy reply to be that of indifference, but know that it’s due to comprehending the ‘fresh’ information passed on by your good selves.  It’s funny how close I was to such a solution, but clearly my lack of experience just couldn’t tie it together.  I’m currently studying ‘sfmskywalker’s tutorial *AMAZING!!!*…I really wish to start from the start and finish the entire walkthrough, but unfortunately due to my projects severe ‘time’ overrun, I will have to put this on hold for the time being.  Anyhow, so far things are progressing much better than they have been, so I’ll let you know how I get on. @planetClaire, that would be brilliant mate, though hopefully I might be able to pass on my eventual code, and between the two, there might be some decent solutions worth collaborating [more from you I’d imagine ;)].  Thanks for everything guys, I feel invigorated with renewed enthusiasm. So let's see how I go....Cheers Dyr

Mar 5, 2012 at 5:32 AM

Here we go:

AccountController

using LETS.ViewModels;
using Orchard;
using System.Web.Mvc;
using Orchard.ContentManagement;
using Orchard.Localization;
using Orchard.Mvc;
using Orchard.Security;
using Orchard.Themes;
using Orchard.Users.Services;
using Orchard.Users.Events;
using System.Collections.Generic;

namespace LETS.Controllers
{
    [Themed]
    public class AccountController : Orchard.Users.Controllers.AccountController, IUpdateModel 
    {
        private readonly IMembershipService _membershipService;
        private readonly IOrchardServices _orchardServices;
        private readonly IContentManager _contentManager;

        public AccountController(IAuthenticationService authenticationService, IMembershipService membershipService, IUserService userService, IOrchardServices orchardServices, IEnumerable<IUserEventHandler> userEventHandlers, IContentManager contentManager) 
            : base(authenticationService, membershipService, userService, orchardServices, userEventHandlers)
        {
            _membershipService = membershipService;
            _orchardServices = orchardServices;
            _contentManager = contentManager;
        }

        public ActionResult RegisterMember()
        {
            // start with the base & add a user profile
            dynamic shapeResult = Register();
            var user = _contentManager.New("User");
            shapeResult.Model.UserProfile = _contentManager.BuildEditor(user);
            return new ShapeResult(this, shapeResult.Model);
        }

        [HttpPost]
        public ActionResult RegisterMember(RegisterMemberViewModel memberVM)
        {
            ActionResult actionResult = null;
            // validate user with attached parts
            var dummyUser = _contentManager.New("User");
            var registerShape = _orchardServices.New.Register();
            registerShape.UserProfile = _contentManager.UpdateEditor(dummyUser, this);
            if (!ModelState.IsValid)
            {
                _orchardServices.TransactionManager.Cancel();
                return new ShapeResult(this, registerShape);
            }
            // validated, proceed to register member
            dynamic usersRegisterResult = Register(memberVM.Email, memberVM.Email, memberVM.Password, memberVM.ConfirmPassword);
            if (usersRegisterResult is RedirectResult || usersRegisterResult is RedirectToRouteResult)
            {
                // successful result, now update the new user with the parts
                _contentManager.Flush();
                var newUser = _membershipService.GetUser(memberVM.Email);
                if (newUser != null)
                {
                    _orchardServices.ContentManager.UpdateEditor(newUser, this);
                    actionResult = usersRegisterResult;
                }
            }
            else
            {
                actionResult = new ShapeResult(this, registerShape);
            }
            // we're using email as username
            ModelState.Remove("username");
            return actionResult;
        }

        bool IUpdateModel.TryUpdateModel<TModel>(TModel model, string prefix, string[] includeProperties, string[] excludeProperties)
        {
            return TryUpdateModel(model, prefix, includeProperties, excludeProperties);
        }

        void IUpdateModel.AddModelError(string key, LocalizedString errorMessage)
        {
            ModelState.AddModelError(key, errorMessage.ToString());
        }

    }
}

Views/Register.cshtml

<h1>@Html.TitleForPage(T("Create a New Account").ToString())</h1>
<p>@T("Use the form below to create a new account.")</p>
<p>@T("Passwords are required to be a minimum of {0} characters in length.", ViewData["PasswordLength"])</p>
@Html.ValidationSummary(T("Account creation was unsuccessful. Please correct the errors and try again.").ToString()) 
@using (Html.BeginFormAntiForgeryPost()) { 
    <fieldset>
        <legend>@T("Account Information")</legend>
    
    <label for="email" class="required">@T("Email")</label>
    @Html.TextBox("email") 
    @Html.ValidationMessage("email") 
    
    <label for="password" class="required">@T("Password")</label>
    @Html.Password("password") 
    @Html.ValidationMessage("password") 
    
    <label for="confirmPassword" class="required">@T("Confirm password")</label>
    @Html.Password("confirmPassword") 
    @Html.ValidationMessage("confirmPassword") 

    @if (Model.UserProfile != null)
    {
        <fieldset>
            <legend>@WorkContext.CurrentSite.SiteName</legend>
            @Display(Model.UserProfile)
        </fieldset>
    }
    
    <button class="primaryAction" type="submit">@T("Register")</button>
    
    </fieldset>
 } 

Routes.cs

using System.Collections.Generic;
using System.Web.Mvc;
using System.Web.Routing;
using JetBrains.Annotations;
using Orchard.Mvc.Routes;

namespace LETS
{
    [UsedImplicitly]
    public class Routes : IRouteProvider
    {
        public IEnumerable<RouteDescriptor> GetRoutes()
        {
            return new[] {
                new RouteDescriptor {
                    Priority = 19,
                    Route = new Route(
                        "Users/Account/Register",
                        new RouteValueDictionary {
                            {"area", "LETS"},
                            {"controller", "Account"},
                            {"action", "RegisterMember"}
                        },
                        new RouteValueDictionary(),
                        new RouteValueDictionary {
                            {"area", "LETS"}
                        },
                        new MvcRouteHandler())
                    }
            };
        }

        public void GetRoutes(ICollection<RouteDescriptor> routes)
        {
            foreach (var routeDescriptor in GetRoutes())
            {
                routes.Add(routeDescriptor);
            }
        }
    }
}

Theme: Views\EditorTemplate-Telephone-url-users-account-register.cshtml

@* hide this non essential field on registration form *@

Hope this helps :-) 

Mar 8, 2012 at 12:12 AM

Sorry PlanetClaire for the late reply [been exceptionally busy, and even this will be a short reply as I run out the door]…yeah, very similar to my solution, but I have done a couple of things differently that may help amend what you are trying to sort. I will be OS for a week, so upon my return, if you require some ideas/source code re: split screen wizard client-side val / custom form hassles with placement file/extended reg module […why I didn’t want to use it], let us know and I’ll post whatever you’re after through. Good luck with it all, cheers Dyr