Design Recommendations Needed for Custom Login control

Topics: Customizing Orchard, Writing modules
Feb 27, 2013 at 6:18 PM
Edited Feb 27, 2013 at 6:24 PM
Hi Everyone,

I am learning Orchard in order to determine if it will meet our needs. I have taken the fundamentals and advanced PluralSight training for Orchard, and I am pretty impressed. However, I am having difficulty wrapping my head around some of the concepts.

I have read a lot of different articles about creating a custom module, but I haven't seen much about content types that do not need to be persisted in the database. I am struggling to understand how I can create a logon widget to be used in my layout.

I have a lot of development experience so I can usually put the pieces together on my own, so I am not looking for someone to give me the code. I am trying to understand the best way to implement this functionality, and I'm sure the community has done this.

Could someone please provide a list of the steps that should be taken in order to create a module that will serve up a login view and allow me to capture the login event and handle validation?

I have done this:
1) Used the codegen feature to create a new module

2) Used the codegen feature to create a new controller, added a Login action method and referenced the view model I want to use.
using System.Web.Mvc;
using Orchard.Localization;
using Orchard;

namespace MyCompany.Authentication.Controllers
{
    public class AuthenticationController : Controller
    {
        public IOrchardServices Services { get; set; }
        public LoginViewModel viewModel { get; set; }

        public AuthenticationController(IOrchardServices services)
        {
            Services = services;
            T = NullLocalizer.Instance;
        }

        public ActionResult Login()
        {
            return View();
        }

        public Localizer T { get; set; }
    }
}
3) Created a ViewModels folder and added a single view model class with UserName and Password fields.
using Orchard.ContentManagement;
using System.ComponentModel.DataAnnotations;

namespace MyCompany.Authentication.ViewModels
{
    public class LoginViewModel
    {
        public LoginViewModel()
        {
        }
        [Required]
        public string UserName { get; set; }
        [Required]
        public string Password { get; set; }

        public string LoginResult { get; set; }
    }
}
4) Created a view.
@model MyCompany.Authentication.ViewModels.LoginViewModel
@{
    Script.Require("jQuery");
}

@using (Html.BeginFormAntiForgeryPost("Login", FormMethod.Post, new { id = "loginForm" }))
{ 
    <fieldset>
        <legend>@T("Account Information")</legend>
        <div>
            <label for="UserName">@T("UserName:")</label>
            @Html.TextBoxFor(m => m.UserName, new { @class = "deltaText", @id = "txtUserName", @name = "txtUserName" })
            @Html.ValidationMessageFor(m => m.UserName)
        </div>
        <div>
            <label for="Password">@T("Password:")</label>
            @Html.TextBox("Password")
            @Html.ValidationMessage("Password")
        </div>
        <div>
            <button class="primaryAction" type="submit">@T("Login")</button>
        </div>
    </fieldset>
}
However, I have not created a content type or a content part, or datamigrations because I don't think its required if I don't need to persist anything to a database, and most all of the tutorials I've found use migrations, parts and part drivers.

Again, I'm just looking for broad strokes that will reflect best practices in this scenario. I figure if I get this piece working, then the light bulbs will begin to shine brightly!

Thanks to anyone who can help me get my head around this!
--Jason
Developer
Feb 28, 2013 at 8:12 AM
Good approach actually! If you want to completely override authentication (this is a question: if you don't want to really swap out everything there are multiple better ways) this is the way to go. You know have to reroute requests to the built-in action to yours by implementing IRouteProvider (see any Routes class for example).
Alternatively if you only want to extend the existing user management you can look at implementing IUserEventHandler and IAuthorizationServiceEventHandler.
Mar 4, 2013 at 5:48 PM
Edited Mar 4, 2013 at 9:19 PM
Thanks for your reply, Piedone.
I am unsure if I want to replace or extend the user management pieces of Orchard, I'm just not comfortable enough with Orchard yet.
However, as far as a proof of concept goes, I thought this was a good place to start. I simply want my piece of html, in this case a custom form with a login action to be displayed on the AsideSecond zone of the Theme Machine.
I got stuck, however when I realized that I don't need my view model to be persisted to a database and the example articles all seem to do exactly that.
On the Orchard site, I read this article: http://docs.orchardproject.net/Documentation/Writing-a-content-part. In the article, a new module is generated, the module.txt file is modified, and then the very next step is to create the content part and and a content part record. My first thought is that I'm barking up the wrong tree and that this must not be the correct way since I don't need any database persistence.

Since I am new to Orchard, I may not even be asking my question properly. I know that we first create a theme, and then we can add widgets to the zones. The code that I show in my intial post is clearly not enough to display anything in Orchard, but I am not sure what pieces are missing.

I am just looking for a high level set of steps that I can work with that will allow me to define a block of html in a razor view, display it in a zone on my form, and allow me to capture events from the control without dealing with records.

1) Create your module
2) Create your controller
3) Create your view model/content parts/content records.... etc

One thing I haven't done yet is to configure route values. I'm still working on trying to understand how that works. Do I need to specify route values for my login control? I don't understand why because I want the control to be a part of the homepage and it has its own route values, and I figure the login button on my widget is where I'll point to a controller action. I did add the AutoRoute part to my login part, but for some reason when I look at my part in the dashboard, the AutoRoute section isn't shown as expandable and I can't see the textboxes to enter slugs.

Anyway, I'm rambling, so I apologize. I've been trying for three days and I've created a login content type, a login part, a login widget, modified my placement file and still have no clue how to properly add my widget to my layout.

Thanks
--Jason
Developer
Mar 4, 2013 at 9:41 PM
Oh, so you'd just like a login widget? I see. Fort this you'll just need to render some html to be displayed. As a practice and probably for added flexibility (there are other, more advanced ways to do this too) you well could do this with a custom part (that will just deal with the login form) that you'll attach to a new content type with the stereotype widget (i.e. your widget content type) and what you'll use to create a new widget content item from (the widget content item is what you'll add from the UI to the zone).
Now parts don't necessarily need to have a corresponding record. Actually if you want to just display a login form your part can be empty too. You just need the following, in your own module:
  • A part: this will represent the functionality you'll add to your login widget.
  • A driver, with only a Display() method: this will produce a single shape that will display the login form.
  • A Placement.info file having an entry corresponding to the previously mentioned shape.
  • A template, also corresponding to that shape, that will actually contain the markup of the form (remember to use Hml.AntiforgeryForm or how is it named).