Updates Login and ForgotPassword to be MVC and have a Captcha
This commit is contained in:
@@ -1761,6 +1761,8 @@
|
||||
<Content Include="umbraco\lib\bootstrap-tabdrop\README.md" />
|
||||
<Content Include="Views\MacroPartials\Releases\ReleasesDropdown.cshtml" />
|
||||
<Content Include="Views\Partials\Community\TermsAndConditions.cshtml" />
|
||||
<Content Include="Views\MacroPartials\Members\ForgotPassword.cshtml" />
|
||||
<Content Include="Views\MacroPartials\Members\Login.cshtml" />
|
||||
<None Include="Views\Partials\Forum\MemberBadge.cshtml" />
|
||||
<Content Include="Views\Search\Search.aspx" />
|
||||
<Content Include="Views\Web.config" />
|
||||
@@ -4414,6 +4416,8 @@
|
||||
<Content Include="Views\Partials\Grid\Bootstrap3-Fluid.cshtml" />
|
||||
<Content Include="Views\Partials\Grid\Bootstrap2.cshtml" />
|
||||
<Content Include="Views\Partials\Grid\Bootstrap2-Fluid.cshtml" />
|
||||
<Content Include="Views\Partials\Members\Login.cshtml" />
|
||||
<Content Include="Views\Partials\Members\ForgotPassword.cshtml" />
|
||||
<None Include="web.Debug.config">
|
||||
<DependentUpon>web.config</DependentUpon>
|
||||
</None>
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
@inherits Umbraco.Web.Macros.PartialViewMacroPage
|
||||
|
||||
@{
|
||||
if (Members.IsLoggedIn())
|
||||
{
|
||||
Response.Redirect("/member/profile");
|
||||
}
|
||||
}
|
||||
|
||||
@Html.Action("RenderForgotPassword", "Login")
|
||||
@@ -0,0 +1,17 @@
|
||||
@inherits Umbraco.Web.Macros.PartialViewMacroPage
|
||||
|
||||
@{
|
||||
if (Members.IsLoggedIn())
|
||||
{
|
||||
if (Model.MacroParameters["NextPage"] != null && string.IsNullOrWhiteSpace(Model.MacroParameters["NextPage"].ToString()) == false)
|
||||
{
|
||||
// It's being called from the old package repository, don't redirect
|
||||
}
|
||||
else
|
||||
{
|
||||
Response.Redirect("/member/profile");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Html.Action("RenderLogin", "Login")
|
||||
@@ -0,0 +1,49 @@
|
||||
@using OurUmbraco.Our.Controllers
|
||||
@using reCAPTCHA.MVC
|
||||
@inherits UmbracoViewPage<Umbraco.Web.Models.LoginModel>
|
||||
@{
|
||||
Html.EnableClientValidation(true);
|
||||
Html.EnableUnobtrusiveJavaScript(true);
|
||||
}
|
||||
|
||||
@if (Request.QueryString["success"] == null)
|
||||
{
|
||||
<div class="form simpleForm" id="registrationForm">
|
||||
@using (Html.BeginUmbracoForm<LoginController>("ForgotPassword"))
|
||||
{
|
||||
@Html.ValidationSummary(true)
|
||||
@Html.AntiForgeryToken()
|
||||
|
||||
<fieldset>
|
||||
<p>
|
||||
@Html.LabelFor(m => m.Username, new { @cless = "inputLabel" })<br />
|
||||
@Html.ValidationMessageFor(m => m.Username)
|
||||
|
||||
@Html.TextBoxFor(m => m.Username, new { @class = "email title" })
|
||||
</p>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<p>
|
||||
@Html.Recaptcha()
|
||||
@Html.ValidationMessage("ReCaptcha")
|
||||
</p>
|
||||
</fieldset>
|
||||
<input class="button green" type="submit" value="Retrieve password">
|
||||
}
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<p>If an account with that email exists you have just been sent instructions to reset your password.</p>
|
||||
}
|
||||
|
||||
<style type="text/css">
|
||||
.field-validation-error, .validation-summary-errors {
|
||||
color: red;
|
||||
}
|
||||
|
||||
.input-validation-error {
|
||||
border-color: red !important;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,50 @@
|
||||
@using OurUmbraco.Our.Controllers
|
||||
@using reCAPTCHA.MVC
|
||||
@inherits UmbracoViewPage<Umbraco.Web.Models.LoginModel>
|
||||
@{
|
||||
Html.EnableClientValidation(true);
|
||||
Html.EnableUnobtrusiveJavaScript(true);
|
||||
}
|
||||
|
||||
<div class="form simpleForm" id="registrationForm">
|
||||
@using (Html.BeginUmbracoForm<LoginController>("Login"))
|
||||
{
|
||||
@Html.ValidationSummary(true)
|
||||
@Html.AntiForgeryToken()
|
||||
|
||||
<fieldset>
|
||||
<p>
|
||||
@Html.LabelFor(m => m.Username, new { @class = "inputLabel" })<br />
|
||||
@Html.ValidationMessageFor(m => m.Username)
|
||||
|
||||
@Html.TextBoxFor(m => m.Username, new { @class = "email title" })
|
||||
</p>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<p>
|
||||
@Html.LabelFor(m => m.Password, new { @class = "inputLabel" })<br />
|
||||
@Html.ValidationMessageFor(m => m.Password)
|
||||
@Html.PasswordFor(m => m.Password, new { @class = "password title" })
|
||||
</p>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<p>
|
||||
@Html.Recaptcha()
|
||||
@Html.ValidationMessage("ReCaptcha")
|
||||
</p>
|
||||
</fieldset>
|
||||
|
||||
<input class="button green" type="submit" value="Log in">
|
||||
}
|
||||
</div>
|
||||
|
||||
<style type="text/css">
|
||||
.field-validation-error, .validation-summary-errors {
|
||||
color: red;
|
||||
}
|
||||
|
||||
.input-validation-error {
|
||||
border-color: red !important;
|
||||
}
|
||||
</style>
|
||||
@@ -1,11 +1,176 @@
|
||||
using System.Web.Mvc;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Mail;
|
||||
using System.Text;
|
||||
using System.Web.Mvc;
|
||||
using System.Web.Security;
|
||||
using OurUmbraco.Our.usercontrols;
|
||||
using reCAPTCHA.MVC;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Web.Models;
|
||||
using Umbraco.Web.Mvc;
|
||||
|
||||
namespace OurUmbraco.Our.Controllers
|
||||
{
|
||||
public class LoginController: SurfaceController
|
||||
public class LoginController : SurfaceController
|
||||
{
|
||||
[ChildActionOnly]
|
||||
public ActionResult RenderLogin()
|
||||
{
|
||||
var loginModel = new LoginModel();
|
||||
return PartialView("~/Views/Partials/Members/Login.cshtml", loginModel);
|
||||
}
|
||||
|
||||
[ChildActionOnly]
|
||||
public ActionResult RenderForgotPassword()
|
||||
{
|
||||
var loginModel = new LoginModel();
|
||||
return PartialView("~/Views/Partials/Members/ForgotPassword.cshtml", loginModel);
|
||||
}
|
||||
|
||||
[CaptchaValidator]
|
||||
public ActionResult Login(LoginModel model)
|
||||
{
|
||||
if (ModelState.IsValid == false)
|
||||
return CurrentUmbracoPage();
|
||||
|
||||
var memberService = Services.MemberService;
|
||||
int totalMembers;
|
||||
var members = memberService.FindByEmail(model.Username, 0, 100, out totalMembers).ToList();
|
||||
if (totalMembers > 1)
|
||||
{
|
||||
var duplicateMembers = new List<DuplicateMember>();
|
||||
foreach (var member in members)
|
||||
{
|
||||
var totalKarma = member.GetValue<int>("reputationTotal");
|
||||
var duplicateMember = new DuplicateMember { MemberId = member.Id, TotalKarma = totalKarma };
|
||||
duplicateMembers.Add(duplicateMember);
|
||||
}
|
||||
|
||||
// rename username/email for each duplicate member
|
||||
// EXCEPT for the one with the highest karma (Skip(1))
|
||||
foreach (var duplicateMember in duplicateMembers.OrderByDescending(x => x.TotalKarma).ThenByDescending(x => x.MemberId).Skip(1))
|
||||
{
|
||||
var member = memberService.GetById(duplicateMember.MemberId);
|
||||
var newUserName = member.Username.Replace("@", "@__" + member.Id);
|
||||
member.Username = newUserName;
|
||||
member.Email = newUserName;
|
||||
memberService.Save(member, false);
|
||||
}
|
||||
}
|
||||
|
||||
var memberToLogin = memberService.FindByEmail(model.Username, 0, 1, out totalMembers).SingleOrDefault();
|
||||
if (memberToLogin != null)
|
||||
{
|
||||
// Note: After July 23rd 2015 we need people to activate their accounts! Don't approve automatically
|
||||
// otherwise:
|
||||
// Automatically approve all members, as we don't have an approval process now
|
||||
// This is needed as we added new membership after upgrading so IsApproved is
|
||||
// currently empty. First time a member gets saved now (login also saves the member)
|
||||
// IsApproved would turn false (default value of bool) so we want to prevent that
|
||||
if (memberToLogin.CreateDate < DateTime.Parse("2015-07-23") && memberToLogin.Properties.Contains(Constants.Conventions.Member.IsApproved) && memberToLogin.IsApproved == false)
|
||||
{
|
||||
memberToLogin.IsApproved = true;
|
||||
memberService.Save(memberToLogin, false);
|
||||
}
|
||||
}
|
||||
|
||||
if (Members.Login(model.Username, model.Password))
|
||||
{
|
||||
return Redirect("/");
|
||||
}
|
||||
|
||||
ModelState.AddModelError("", "That username and password combination isn't valid here");
|
||||
return CurrentUmbracoPage();
|
||||
}
|
||||
|
||||
[CaptchaValidator]
|
||||
public ActionResult ForgotPassword(LoginModel model)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(model.Username))
|
||||
return CurrentUmbracoPage();
|
||||
|
||||
var memberService = Services.MemberService;
|
||||
int totalMembers;
|
||||
var members = memberService.FindByEmail(model.Username, 0, 100, out totalMembers);
|
||||
if (totalMembers > 1)
|
||||
{
|
||||
var duplicateMembers = new List<DuplicateMember>();
|
||||
foreach (var member in members)
|
||||
{
|
||||
var totalKarma = member.GetValue<int>("reputationTotal");
|
||||
var duplicateMember = new DuplicateMember { MemberId = member.Id, TotalKarma = totalKarma };
|
||||
duplicateMembers.Add(duplicateMember);
|
||||
}
|
||||
|
||||
// rename username/email for each duplicate member
|
||||
// EXCEPT for the one with the highest karma (Skip(1))
|
||||
foreach (
|
||||
var duplicateMember in
|
||||
duplicateMembers.OrderByDescending(x => x.TotalKarma).ThenByDescending(x => x.MemberId).Skip(1))
|
||||
{
|
||||
var member = memberService.GetById(duplicateMember.MemberId);
|
||||
var newUserName = member.Username.Replace("@", "@__" + member.Id);
|
||||
member.Username = newUserName;
|
||||
member.Email = newUserName;
|
||||
memberService.Save(member);
|
||||
}
|
||||
}
|
||||
|
||||
var m = memberService.GetByEmail(model.Username);
|
||||
if (m == null)
|
||||
{
|
||||
// Don't add an error and reveal that someone with this email address exists on this site
|
||||
return Redirect(CurrentPage.Url + "?success=true");
|
||||
}
|
||||
|
||||
// Automatically approve all members, as we don't have an approval process now
|
||||
// This is needed as we added new membership after upgrading so IsApproved is
|
||||
// currently empty. First time a member gets saved now (login also saves the member)
|
||||
// IsApproved would turn false (default value of bool) so we want to prevent that
|
||||
if (m.Properties.Contains(Constants.Conventions.Member.IsApproved) && m.IsApproved == false)
|
||||
{
|
||||
m.IsApproved = true;
|
||||
memberService.Save(m, false);
|
||||
}
|
||||
|
||||
var pass = RandomString(16, true);
|
||||
memberService.SavePassword(m, pass);
|
||||
|
||||
var mail = "<p>Hi " + m.Name + "</p>";
|
||||
mail = mail + "<p>This is your new password for your account on https://our.umbraco.org:</p>";
|
||||
mail = mail + "<p><strong>" + pass + "</strong></p>";
|
||||
mail = mail + "<br/><br/><p>All the best<br/> <em>The email robot</em></p>";
|
||||
|
||||
using (var mailMessage = new MailMessage())
|
||||
{
|
||||
mailMessage.Subject = "Your password to our.umbraco.org";
|
||||
mailMessage.Body = mail;
|
||||
mailMessage.IsBodyHtml = true;
|
||||
mailMessage.To.Add(new MailAddress(m.Email));
|
||||
mailMessage.From = new MailAddress("robot@umbraco.org");
|
||||
|
||||
using (var smtpClient = new SmtpClient())
|
||||
smtpClient.Send(mailMessage);
|
||||
}
|
||||
|
||||
return Redirect(CurrentPage.Url + "?success=true");
|
||||
}
|
||||
|
||||
private string RandomString(int size, bool lowerCase)
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
var random = new Random();
|
||||
for (var i = 0; i < size; i++)
|
||||
{
|
||||
var ch = Convert.ToChar(Convert.ToInt32(Math.Floor(26 * random.NextDouble() + 65)));
|
||||
builder.Append(ch);
|
||||
}
|
||||
|
||||
return lowerCase ? builder.ToString().ToLower() : builder.ToString();
|
||||
}
|
||||
|
||||
public ActionResult Logout()
|
||||
{
|
||||
FormsAuthentication.SignOut();
|
||||
|
||||
@@ -35,6 +35,8 @@ namespace OurUmbraco.Our
|
||||
AddStrictMinimumVersionForPackages();
|
||||
RenameUaaStoUCloud();
|
||||
AddMarkAsSolutionReminderSent();
|
||||
UseNewLoginForm();
|
||||
UseNewForgotPasswordForm();
|
||||
}
|
||||
|
||||
private void EnsureMigrationsMarkerPathExists()
|
||||
@@ -379,7 +381,7 @@ namespace OurUmbraco.Our
|
||||
compareContentType = contentTypeService.GetContentType(releaseCompareAlias);
|
||||
|
||||
var releaseLandingContentType = contentTypeService.GetContentType("ReleaseLanding");
|
||||
|
||||
|
||||
var allowedContentTypes = new List<ContentTypeSort> { new ContentTypeSort(compareContentType.Id, 0) };
|
||||
releaseLandingContentType.AllowedContentTypes = allowedContentTypes;
|
||||
contentTypeService.Save(releaseLandingContentType);
|
||||
@@ -489,13 +491,13 @@ namespace OurUmbraco.Our
|
||||
var path = HostingEnvironment.MapPath(MigrationMarkersPath + migrationName + ".txt");
|
||||
if (File.Exists(path))
|
||||
return;
|
||||
|
||||
|
||||
var macroService = ApplicationContext.Current.Services.MacroService;
|
||||
var macro = macroService.GetByAlias("MemberSignup");
|
||||
macro.ControlType = "";
|
||||
macro.ScriptPath = "~/Views/MacroPartials/Members/Register.cshtml";
|
||||
macroService.Save(macro);
|
||||
|
||||
|
||||
string[] lines = { "" };
|
||||
File.WriteAllLines(path, lines);
|
||||
}
|
||||
@@ -526,7 +528,7 @@ namespace OurUmbraco.Our
|
||||
LogHelper.Error<MigrationsHandler>(string.Format("Migration: '{0}' failed", migrationName), ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void RenameUaaStoUCloud()
|
||||
{
|
||||
var migrationName = MethodBase.GetCurrentMethod().Name;
|
||||
@@ -585,5 +587,55 @@ namespace OurUmbraco.Our
|
||||
LogHelper.Error<MigrationsHandler>(string.Format("Migration: '{0}' failed", migrationName), ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void UseNewLoginForm()
|
||||
{
|
||||
var migrationName = MethodBase.GetCurrentMethod().Name;
|
||||
|
||||
try
|
||||
{
|
||||
var path = HostingEnvironment.MapPath(MigrationMarkersPath + migrationName + ".txt");
|
||||
if (File.Exists(path))
|
||||
return;
|
||||
|
||||
var macroService = ApplicationContext.Current.Services.MacroService;
|
||||
var macro = macroService.GetByAlias("MemberLogin");
|
||||
macro.ControlType = "";
|
||||
macro.ScriptPath = "~/Views/MacroPartials/Members/Login.cshtml";
|
||||
macroService.Save(macro);
|
||||
|
||||
string[] lines = { "" };
|
||||
File.WriteAllLines(path, lines);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.Error<MigrationsHandler>(string.Format("Migration: '{0}' failed", migrationName), ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void UseNewForgotPasswordForm()
|
||||
{
|
||||
var migrationName = MethodBase.GetCurrentMethod().Name;
|
||||
|
||||
try
|
||||
{
|
||||
var path = HostingEnvironment.MapPath(MigrationMarkersPath + migrationName + ".txt");
|
||||
if (File.Exists(path))
|
||||
return;
|
||||
|
||||
var macroService = ApplicationContext.Current.Services.MacroService;
|
||||
var macro = macroService.GetByAlias("MemberPasswordReminder");
|
||||
macro.ControlType = "";
|
||||
macro.ScriptPath = "~/Views/MacroPartials/Members/ForgotPassword.cshtml";
|
||||
macroService.Save(macro);
|
||||
|
||||
string[] lines = { "" };
|
||||
File.WriteAllLines(path, lines);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.Error<MigrationsHandler>(string.Format("Migration: '{0}' failed", migrationName), ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user