I was attempting to help a new user by answering a query in the Umbraco forum which eventually morphed into the original poster trying to create a Members' area. I'd noticed some base snippets (these are out of the box bits of Razor macro code that usually work quite well giving you good starts on creating menus or listing or child nodes etc).
There are three which look like they'd give you a fully fledged members' area;
It looks like the aim of the Umbraco team is to provide an out of the box Members' area - let's see how useable this is without having to delve into code *.
* OK - maybe a little bit of Razor !
I took the basic site template I used to write the tutorial Creating a Basic Site in Umbraco v7 or available as a PDF here and looked at how easy it is to implement these snippets into a full solution.
First create the following Document Types (allowing Umbraco to create the matching template) with a simple content tab and a bodyText Rich Text Editor Property on each.
Remember to copy the Initializr js scripts and CSS and update any references.
Allow the homepage to be created at the root and the others to be legitimate children. Then create a Master template, update all of the other templates to be children of this and add the Initialzr template code to the master with a @RenderBody and something relevant in the child templates
Next create the following content nodes;
Now we should have a fully working site - though none of the pages do anything yet.
Now go and create a Partial View Macro File (found under the Developer menu) for each of the following - remembering to select the relevant snippet and leave the Create Macro checkbox selected. A snippet is an Umbraco supplied Razor template.
Now we need to go and include these macros in the relevant templates. First job put the login status into the footer of the master template so that we have this on all pages.
Now do the same for the Login and the Register templates (using the relevant macro).
If you now visit the Register page you should see a register form.
Now users can register we need to fix a small issue in the login. To do this we need to break the "no code" rule a little bit to help us see what's happening and to help users. If we look at the Partial Macro View Login Status you'll see that this only outputs something if the member is logged in - let's add an else clause to provide a login link if they are NOT logged in - again you should style this using CSS to make it more useable.
Let's test the register by creating a TestMember - use the registration form and you'll see that this will also log you in, you can now see the Login Status macro change its output
We can now add protection to content - to do this click the right hand mouse button on the content node (e.g. the Protected content) and click the Public Access menu item.
This gives you two options - Single User Protection and Role base protection. You'll see that it warns we don't have a member group. Let's create this first in the Members section. I call mine "WebsiteRegistrations".
Create a WebsiteRegistrations Member Group.
Now we can go back and add the protection to the page.
Choose the Login page from the picker for Login Page and I just quickly created an Error page (a simple content page with help for the user to contact web admin etc.)
Let's test the solution - if you're still logged in as TestMember and you browse our Protected page you'll see the content from our Homepage... this is because the user isn't a member of the group we just created so we're being redirect to the error page.
If you edit the member and add this group you'll see this works. Obviously we could have our editor add this group manually for each registration... but ideally we'd like this to be automatic, we'll add this later.
Test Logging out and logging in. Here is where you meet your second issue - the login page redirects a successful user to the login page. This would be particularly bad from a User Experience point of view.
I've updated the default Login Razor script to use the requested URL unless it's the login page itself (to avoid a circular redirect) and to accommodate the user getting their password wrong (otherwise it would try to redirect to a page which processes macros!). Though I said they'd be no code... - copy and paste this Razor code below over your Login Partial View Macro.
IMPORTANT You need to find your login page's node ID and update line 11
To do this go to the Properties tab of your Login content node
@inherits Umbraco.Web.Macros.PartialViewMacroPage @using System.Web.Mvc.Html @using ClientDependency.Core.Mvc @using Umbraco.Web @using Umbraco.Web.Models @using Umbraco.Web.Controllers @{ // You have to set the login node ID otherwise you'll have an infinite loop if someone hits the login page first var loginNodeID = 1056; var loginModel = new LoginModel(); var loginStatusModel = Members.GetCurrentLoginStatus(); Html.EnableClientValidation(); Html.EnableUnobtrusiveJavaScript(); Html.RequiresJs("/umbraco_client/ui/jquery.js"); Html.RequiresJs("/umbraco_client/Application/JQuery/jquery.validate.min.js"); Html.RequiresJs("/umbraco_client/Application/JQuery/jquery.validate.unobtrusive.min.js"); } @* NOTE: This RenderJsHere code should be put on your main template page where the rest of your script tags are placed *@ @Html.RenderJsHere() @{ var checkUrl = HttpContext.Current.Request.Url.AbsolutePath.ToString(); // add a trailing / if there isn't one (you can access http://mydomain.com/login or http://mydomain.com/login/ if (@checkUrl[checkUrl.Length -1] != '/') { checkUrl = checkUrl + "/"; } @* if we don't have a session variable and have a request URL then store it *@ @* we have to store it because if user tries an incorrect login then Current.Request.Url will show /umbraco/RenderMvc *@ if (HttpContext.Current.Request.Url != null && HttpContext.Current.Request.Url.AbsolutePath.ToString() != "/umbraco/RenderMvc" && HttpContext.Current.Session["redirectURL"] == null) { if (checkUrl != @Umbraco.Content(loginNodeID).Url) { HttpContext.Current.Session["redirectURL"] = HttpContext.Current.Request.Url.ToString(); } } if (loginStatusModel.IsLoggedIn) { var redirectUrl = (string)HttpContext.Current.Session["redirectURL"]; var currentUrl = HttpContext.Current.Request.Url.ToString(); if (HttpContext.Current.Session["redirectURL"] != null) { // clear the session variable for future logins HttpContext.Current.Session["redirectURL"] = null; HttpContext.Current.Response.Redirect(redirectUrl); } else { // Nothing in the session so we will go home HttpContext.Current.Response.Redirect("/"); } } using (Html.BeginUmbracoForm<UmbLoginController>("HandleLogin")) { <fieldset> <legend>Login</legend> @Html.ValidationSummary("loginModel", true) @Html.LabelFor(m => loginModel.Username) @Html.TextBoxFor(m => loginModel.Username) @Html.ValidationMessageFor(m => loginModel.Username) <br /> @Html.LabelFor(m => loginModel.Password) @Html.PasswordFor(m => loginModel.Password) @Html.ValidationMessageFor(m => loginModel.Password) <br /> <button>Login</button> </fieldset> } }
There we are - we now have a working Members only area with a registration form and an enhanced login script to improve the User Experience (e.g. redirecting them to the page they requested after logging in).
Remaining Issues:
For simple sites I think this solution would work - and it's useful as a learning exercise for those new to Umbraco but I think you're still looking at packages or custom Member providers (e.g. writing code) for a member area for now.
If you spot any typos or issues with this tutorial please leave a comment below or email me steve@SiempreSolutions.co.uk
Name: Mickey Nguyen
Date: 1549hrs 05/07/2015
Website:
Excellent post. Thanks you so muck
Mickey
Name: Gerhard
Date: 0808hrs 01/10/2015
Website:
Hi, great explanation on how everything comes together without (much) coding. I only left out the part where you check for empty value in session before adding the current page. "HttpContext.Current.Session["redirectURL"] == null" - otherwise users would always go to first restricted page they visited after login, even if they then went to another.
Name: Steve
Ahh.. I see so line 36 should be: if (HttpContext.Current.Request.Url != null && HttpContext.Current.Request.Url.AbsolutePath.ToString() != "/umbraco/RenderMvc") // && HttpContext.Current.Session["redirectURL"] == null)
Name: shin
Date: 0950hrs 03/11/2015
Website:
Hi, may i know how to make sure when you log out it return to the login page?
Name: Steve
If you take a look at the Razor code for the LoginStatus partial view macro you'll see that there is a RedirectUrl value you can set on the logoutModel @Html.HiddenFor(m => logoutModel.RedirectUrl)
Hope that helps
Name: Amanda
Date: 1624hrs 03/05/2016
Website:
I actually get an infinite loop with the code you provided :( any ideas of why that would happen? I'm using Umbraco 7.4.3
Name: Steve
Hi,
Thanks for your comment - check the first line in /App_Code/UmbExtendTreeController.cs - this node ID should match your login page node ID.
Also check where you have set the error page when setting the public page permissions in Umbraco.
Hope that helps - Steve
Siempre Solutions Limited is a company registered in England and Wales with company number 09019307
Name: Steve
Thanks Gerhard, Do you mean that if they went to: