From 8994a44430c97f75204265b74de403f2ffe215df Mon Sep 17 00:00:00 2001
From: Milad Karbasizadeh
Date: Sun, 27 Apr 2014 19:00:58 +0430
Subject: [PATCH] Change Password + Forget Password done
---
Sevomin.Models/ChangePasswordViewModel.cs | 23 +++++
Sevomin.Models/ForgotPasswordViewModel.cs | 11 +++
Sevomin.Models/Helpers/SevominEmailer.cs | 68 +++++++++++--
Sevomin.Models/Repositories/UserRepository.cs | 10 +-
Sevomin.Models/ResetPasswordViewModel.cs | 18 ++++
Sevomin.Models/Sevomin.Models.csproj | 3 +
.../AccountController.cs | 96 ++++++++++++++++++-
Sevomin.WebFrontend/App_Start/RouteConfig.cs | 15 +++
Sevomin.WebFrontend/Content/intro.css | 9 +-
.../Sevomin.WebFrontend.csproj | 6 ++
.../Views/Account/ChangePassword.cshtml | 51 ++++++++++
.../Views/Account/Forgot.cshtml | 34 +++++++
.../Views/Account/Login.cshtml | 20 ++--
.../Views/Account/ResetPassword.cshtml | 42 ++++++++
Sevomin.WebFrontend/Views/Shared/Intro.cshtml | 14 +--
.../Views/Shared/IntroSignup.cshtml | 2 +
.../Views/Shared/Navbar.cshtml | 7 +-
.../Views/Shared/PostResult.cshtml | 2 +-
Sevomin.WebFrontend/Web.config | 7 ++
19 files changed, 403 insertions(+), 35 deletions(-)
create mode 100644 Sevomin.Models/ChangePasswordViewModel.cs
create mode 100644 Sevomin.Models/ForgotPasswordViewModel.cs
create mode 100644 Sevomin.Models/ResetPasswordViewModel.cs
create mode 100644 Sevomin.WebFrontend/Views/Account/ChangePassword.cshtml
create mode 100644 Sevomin.WebFrontend/Views/Account/Forgot.cshtml
create mode 100644 Sevomin.WebFrontend/Views/Account/ResetPassword.cshtml
diff --git a/Sevomin.Models/ChangePasswordViewModel.cs b/Sevomin.Models/ChangePasswordViewModel.cs
new file mode 100644
index 0000000..0340c2f
--- /dev/null
+++ b/Sevomin.Models/ChangePasswordViewModel.cs
@@ -0,0 +1,23 @@
+using System.ComponentModel;
+using System.ComponentModel.DataAnnotations;
+namespace Sevomin.Models
+{
+ public class ChangePasswordViewModel
+ {
+ [DisplayName("رمز عبور فعلی")]
+ [Required(ErrorMessage = "ورود {0} الزامی است.")]
+ [DataType(DataType.Password)]
+ public string Password { get; set; }
+
+ [DisplayName("رمز عبور جدید")]
+ [Required(ErrorMessage = "ورود {0} الزامی است.")]
+ [DataType(DataType.Password)]
+ public string NewPassword { get; set; }
+
+ [DisplayName("تایید رمز عبور")]
+ [Required(ErrorMessage = "ورود {0} الزامی است.")]
+ [DataType(DataType.Password)]
+ [Compare("NewPassword", ErrorMessage = "تایید رمز عبور با رمز عبور جدید مطابقت ندارد.")]
+ public string ConfirmPassword { get; set; }
+ }
+}
diff --git a/Sevomin.Models/ForgotPasswordViewModel.cs b/Sevomin.Models/ForgotPasswordViewModel.cs
new file mode 100644
index 0000000..e0860e6
--- /dev/null
+++ b/Sevomin.Models/ForgotPasswordViewModel.cs
@@ -0,0 +1,11 @@
+using System.ComponentModel;
+using System.ComponentModel.DataAnnotations;
+namespace Sevomin.Models
+{
+ public class ForgotPasswordViewModel
+ {
+ [DisplayName("ایمیل")]
+ [Required(ErrorMessage = "ورود {0} الزامی است.")]
+ public string Email { get; set; }
+ }
+}
diff --git a/Sevomin.Models/Helpers/SevominEmailer.cs b/Sevomin.Models/Helpers/SevominEmailer.cs
index ac042ae..7e75273 100644
--- a/Sevomin.Models/Helpers/SevominEmailer.cs
+++ b/Sevomin.Models/Helpers/SevominEmailer.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.IO;
using System.Linq;
using System.Net.Mail;
using System.Text;
@@ -7,18 +8,67 @@ using System.Threading.Tasks;
namespace Sevomin.Models.Helpers
{
+ public enum EmailType
+ {
+ EmailConfirmation,
+ PasswordReset,
+ NewPassword
+ }
+
public class SevominEmailer
{
public Dictionary Parameters { get; set; }
+ public EmailType EmailType { get; set; }
+ private string EmailFolderPath;
+ private string EmailConfirmationFilePath;
+ private string NewPasswordFilePath;
+ private string PasswordResetFilePath;
public SevominEmailer()
{
-
+ EmailFolderPath = Path.Combine(System.Web.HttpContext.Current.Server.MapPath("~/app_data"), "emails");
+ if (!Directory.Exists(EmailFolderPath))
+ throw new ApplicationException("Emails folder does not exist in the right address.");
+ EmailConfirmationFilePath = Path.Combine(EmailFolderPath, "email-confirmation.html");
+ NewPasswordFilePath = Path.Combine(EmailFolderPath, "new-password.html");
+ PasswordResetFilePath = Path.Combine(EmailFolderPath, "password-reset.html");
+ if(!File.Exists(EmailConfirmationFilePath))
+ throw new ApplicationException("Email confirmation template does not exist in the right address.");
+ if (!File.Exists(NewPasswordFilePath))
+ throw new ApplicationException("New password template does not exist in the right address.");
+ if (!File.Exists(PasswordResetFilePath))
+ throw new ApplicationException("Password reset template does not exist in the right address.");
+ Parameters = new Dictionary();
+ }
+ public SevominEmailer(Dictionary parameters, EmailType emailType) : this()
+ {
+ Parameters = parameters;
+ EmailType = emailType;
}
- public async void Send(string to, string subject, string template, bool isHtml)
+ public async Task SendAsync(string to, string subject, bool isHtml)
{
SmtpClient client = new SmtpClient();
MailMessage msg = new MailMessage();
+ string template;
+ switch (EmailType)
+ {
+ case EmailType.EmailConfirmation:
+ template =
+ File.ReadAllText(EmailConfirmationFilePath, Encoding.UTF8);
+ break;
+ case EmailType.PasswordReset:
+ template =
+ File.ReadAllText(PasswordResetFilePath, Encoding.UTF8);
+ break;
+ case EmailType.NewPassword:
+ template =
+ File.ReadAllText(NewPasswordFilePath, Encoding.UTF8);
+ break;
+ default:
+ template = string.Empty;
+ break;
+ }
+
foreach (var address in to.Split(','))
msg.To.Add(address);
msg.From = new MailAddress("no-reply@sevom.in", "سومین - مرکز کاریابی کنترل پروژه");
@@ -28,14 +78,16 @@ namespace Sevomin.Models.Helpers
msg.IsBodyHtml = isHtml;
Func getBody = () =>
{
- foreach (var param in Parameters)
- template = template.Replace(param.Key, param.Value);
+ foreach (var param in Parameters)
+ template = template.Replace(string.Format("[{0}]", param.Key), param.Value);
return template;
};
- msg.Body = getBody();
-
- client.SendAsync(msg, null);
+ msg.Body = getBody();
+ await Task.Run(() =>
+ {
+ client.Send(msg);
+ });
}
}
-}
+}
\ No newline at end of file
diff --git a/Sevomin.Models/Repositories/UserRepository.cs b/Sevomin.Models/Repositories/UserRepository.cs
index 1f71637..72e40a8 100644
--- a/Sevomin.Models/Repositories/UserRepository.cs
+++ b/Sevomin.Models/Repositories/UserRepository.cs
@@ -45,7 +45,7 @@ namespace Sevomin.Models.Repositories
public User Find(string identifier)
{
- throw new NotImplementedException();
+ return SevominDbContext.Current.Users.FirstOrDefault(u => u.UserName.ToLower() == identifier.ToLower());
}
public void Delete(User entity)
@@ -55,13 +55,7 @@ namespace Sevomin.Models.Repositories
public void Save()
{
- throw new NotImplementedException();
- }
-
-
- public User Find(long identifier)
- {
- throw new NotImplementedException();
+ SevominDbContext.Current.SaveChanges();
}
}
}
diff --git a/Sevomin.Models/ResetPasswordViewModel.cs b/Sevomin.Models/ResetPasswordViewModel.cs
new file mode 100644
index 0000000..8fa16c3
--- /dev/null
+++ b/Sevomin.Models/ResetPasswordViewModel.cs
@@ -0,0 +1,18 @@
+using System.ComponentModel;
+using System.ComponentModel.DataAnnotations;
+namespace Sevomin.Models
+{
+ public class ResetPasswordViewModel
+ {
+ [DisplayName("رمز عبور")]
+ [Required(ErrorMessage = "ورود {0} الزامی است.")]
+ [DataType(DataType.Password)]
+ public string Password { get; set; }
+
+ [DisplayName("تایید رمز عبور")]
+ [Required(ErrorMessage = "ورود {0} الزامی است.")]
+ [DataType(DataType.Password)]
+ [Compare("Password", ErrorMessage = "تایید رمز عبور با رمز عبور اصلی مطابقت ندارد.")]
+ public string ConfirmPassword { get; set; }
+ }
+}
diff --git a/Sevomin.Models/Sevomin.Models.csproj b/Sevomin.Models/Sevomin.Models.csproj
index ae6fd8b..58a3510 100644
--- a/Sevomin.Models/Sevomin.Models.csproj
+++ b/Sevomin.Models/Sevomin.Models.csproj
@@ -72,6 +72,9 @@
+
+
+
diff --git a/Sevomin.WebFrontend.Controllers/AccountController.cs b/Sevomin.WebFrontend.Controllers/AccountController.cs
index 7b3578d..9f158ec 100644
--- a/Sevomin.WebFrontend.Controllers/AccountController.cs
+++ b/Sevomin.WebFrontend.Controllers/AccountController.cs
@@ -70,6 +70,14 @@ namespace Sevomin.WebFrontend.Controllers
else if(user is Dovomin)
await UserManager.AddToRoleAsync(user.Id, "Dovomin");
+#if !DEBUG
+ SevominEmailer emailer = new SevominEmailer();
+ emailer.EmailType = EmailType.EmailConfirmation;
+ emailer.Parameters.Add("display-name", user.DisplayName);
+ emailer.Parameters.Add("confirmation-code", user.ConfirmationCode);
+ await emailer.SendAsync(user.Email, "تایید عضویت در سومین", true);
+#endif
+
await SignInAsync(user, isPersistent: false);
return RedirectToAction("MyProfile", "Account");
}
@@ -96,7 +104,7 @@ namespace Sevomin.WebFrontend.Controllers
if (user == null)
return HttpNotFound();
- if (Request.IsAuthenticated)
+ if (Request.IsAuthenticated && User.Identity.Name.ToLower() != user.UserName.ToLower())
{
ViewBag.Result = new PostResultViewModel(false,
string.Format("شما با نام کاربری {0} در سایت وارد شده اید. نمی توانید حساب کاربری {1} را تایید نمایید.",
@@ -146,12 +154,96 @@ namespace Sevomin.WebFrontend.Controllers
return View(model);
}
-
public ActionResult Logout()
{
AuthenticationManager.SignOut();
return RedirectToAction("Index", "Home");
}
+
+ public ActionResult Forgot()
+ {
+ return View();
+ }
+
+ [HttpPost]
+ [ValidateAntiForgeryToken]
+ public ActionResult Forgot(ForgotPasswordViewModel model)
+ {
+ var user = UserRepository.Current.Find(model.Email);
+ if (user == null)
+ {
+ ViewBag.Result = new PostResultViewModel(false, "کاربری با این آدرس ایمیل یافت نشد. لطفا دوباره تلاش کنید.");
+ return View();
+ }
+ user.ConfirmationCode = Sevomin.Models.User.GetConfirmationCode();
+ UserRepository.Current.Save();
+
+#if !DEBUG
+ SevominEmailer emailer = new SevominEmailer();
+ emailer.EmailType = EmailType.PasswordReset;
+ emailer.Parameters.Add("display-name", user.DisplayName);
+ emailer.Parameters.Add("reset-code", user.ConfirmationCode);
+ await emailer.SendAsync(user.Email, "بازنشانی رمز عبور در سومین", true);
+#endif
+
+ ViewBag.Result = new PostResultViewModel(true, "آدرس بازنشانی رمز عبور برای شما ارسال شد.");
+ return View();
+ }
+
+ public ActionResult ResetPassword(string code)
+ {
+ var user = UserRepository.Current.FindWithConfirmationCode(code);
+ if (user == null)
+ return HttpNotFound();
+
+ return View();
+ }
+
+ [HttpPost]
+ [ValidateAntiForgeryToken]
+ public async Task ResetPassword(string code, ResetPasswordViewModel model)
+ {
+ var user = UserRepository.Current.FindWithConfirmationCode(code);
+ if (user == null)
+ return HttpNotFound();
+
+ await UserManager.RemovePasswordAsync(user.Id);
+ await UserManager.AddPasswordAsync(user.Id, model.Password);
+ user.ConfirmationCode = string.Empty;
+ UserRepository.Current.Save();
+
+ ViewBag.Result = new PostResultViewModel(true, "رمز عبور شما با موفقیت بازنشانی شد.");
+
+ return View();
+ }
+
+ [Authorize]
+ public ActionResult ChangePassword()
+ {
+ return View();
+ }
+
+ [Authorize]
+ [HttpPost]
+ public async Task ChangePassword(ChangePasswordViewModel model)
+ {
+ var user = await UserManager.FindAsync(User.Identity.Name, model.Password);
+ if (user != null)
+ {
+ await UserManager.RemovePasswordAsync(user.Id);
+ await UserManager.AddPasswordAsync(user.Id, model.NewPassword);
+ UserRepository.Current.Save();
+
+ ViewBag.Result = new PostResultViewModel(true, "رمز عبور شما با موفقیت به روز شد.");
+ return View();
+ }
+ else
+ {
+ ViewBag.Result = new PostResultViewModel(false, "رمز عبور فعلی وارد شده با اطلاعات ما مطابقت ندارد. لطفا دوباره تلاش کنید.");
+ return View();
+ }
+ }
+
[Authorize]
public async Task MyProfile(bool? success)
diff --git a/Sevomin.WebFrontend/App_Start/RouteConfig.cs b/Sevomin.WebFrontend/App_Start/RouteConfig.cs
index ec4e7d3..ecc33fe 100644
--- a/Sevomin.WebFrontend/App_Start/RouteConfig.cs
+++ b/Sevomin.WebFrontend/App_Start/RouteConfig.cs
@@ -64,6 +64,21 @@ namespace Sevomin.WebFrontend
url: "dovomin/id-{userId}",
defaults: new { controller = "Account", action = "Dovomin" }
);
+ routes.MapRoute(
+ name: "ForgotPassword",
+ url: "forgot-password",
+ defaults: new { controller = "Account", action = "Forgot" }
+ );
+ routes.MapRoute(
+ name: "ResetPassword",
+ url: "reset-password/{code}",
+ defaults: new { controller = "Account", action = "ResetPassword" }
+ );
+ routes.MapRoute(
+ name: "ChangePassword",
+ url: "change-password",
+ defaults: new { controller = "Account", action = "ChangePassword" }
+ );
#endregion
#region For Jobs
diff --git a/Sevomin.WebFrontend/Content/intro.css b/Sevomin.WebFrontend/Content/intro.css
index 97fb4ce..ad61802 100644
--- a/Sevomin.WebFrontend/Content/intro.css
+++ b/Sevomin.WebFrontend/Content/intro.css
@@ -1,5 +1,5 @@
.introduction{
- height: 270px;
+ min-height: 270px;
}
.intro-icon, #sevomin-intro-logo{
text-align: center;
@@ -20,6 +20,13 @@
#sevomin-intro-logo img{
width: 150px;
}
+ul#intro-parts{
+ margin: 0;
+ padding: 0;
+}
+ul#intro-parts li{
+ list-style: none;
+}
#s1, #s2, #s3 {
font-size: 0.95em;
color: #444444;
diff --git a/Sevomin.WebFrontend/Sevomin.WebFrontend.csproj b/Sevomin.WebFrontend/Sevomin.WebFrontend.csproj
index c4e3c5f..2c5667d 100644
--- a/Sevomin.WebFrontend/Sevomin.WebFrontend.csproj
+++ b/Sevomin.WebFrontend/Sevomin.WebFrontend.csproj
@@ -122,6 +122,9 @@
+
+
+
@@ -318,6 +321,9 @@
+
+
+
Web.config
diff --git a/Sevomin.WebFrontend/Views/Account/ChangePassword.cshtml b/Sevomin.WebFrontend/Views/Account/ChangePassword.cshtml
new file mode 100644
index 0000000..862ca4d
--- /dev/null
+++ b/Sevomin.WebFrontend/Views/Account/ChangePassword.cshtml
@@ -0,0 +1,51 @@
+@model Sevomin.Models.ChangePasswordViewModel
+
+@{
+ ViewBag.Title = "تغییر کلمه عبور";
+}
+@Html.Partial("PostResult", ViewBag.Result as Sevomin.Models.PostResultViewModel)
+
+
+
+ @using (Html.BeginForm("ChangePassword", "Account", FormMethod.Post, new { role = "form" }))
+ {
+ @Html.AntiForgeryToken()
+
+ }
+
+
\ No newline at end of file
diff --git a/Sevomin.WebFrontend/Views/Account/Forgot.cshtml b/Sevomin.WebFrontend/Views/Account/Forgot.cshtml
new file mode 100644
index 0000000..9f669e1
--- /dev/null
+++ b/Sevomin.WebFrontend/Views/Account/Forgot.cshtml
@@ -0,0 +1,34 @@
+@model Sevomin.Models.ForgotPasswordViewModel
+
+@{
+ ViewBag.Title = "بازنشانی رمز عبور";
+}
+@Html.Partial("PostResult", ViewBag.Result as Sevomin.Models.PostResultViewModel)
+
+
+
+ @using (Html.BeginForm("Forgot", "Account", FormMethod.Post, new { role = "form" }))
+ {
+ @Html.AntiForgeryToken()
+
+ }
+
+
\ No newline at end of file
diff --git a/Sevomin.WebFrontend/Views/Account/Login.cshtml b/Sevomin.WebFrontend/Views/Account/Login.cshtml
index a025df8..27998a0 100644
--- a/Sevomin.WebFrontend/Views/Account/Login.cshtml
+++ b/Sevomin.WebFrontend/Views/Account/Login.cshtml
@@ -11,7 +11,7 @@
اگر قبلا در سایت ثبتنام کردهاید میتوانید با وارد کردن کد کاربری و کلمه عبور وارد سایت شوید.
اگر قبلا در سایت ثبتنام نکردهاید میتوانید هماکنون به هر بخشی که مایل هستید بروید و اطلاعات موجود در سایت را مرور کنید (به جز مواردی که از سوی اشخاص محرمانه معرفی شدهاند). میتوانید با مراجعه به صفحه اول سایت ثبتنام نیز بکنید تا بتوانید برای آگهیهای استخدام اعلام آمادگی کنید، آگهی استخدام ثبت کنید و ...
-
+
@@ -24,24 +24,28 @@
@Html.Partial("IntroSignup")
diff --git a/Sevomin.WebFrontend/Views/Shared/IntroSignup.cshtml b/Sevomin.WebFrontend/Views/Shared/IntroSignup.cshtml
index c24f244..5654d45 100644
--- a/Sevomin.WebFrontend/Views/Shared/IntroSignup.cshtml
+++ b/Sevomin.WebFrontend/Views/Shared/IntroSignup.cshtml
@@ -17,6 +17,7 @@
ما به شما کمک میکنیم که نیرویی مطابق با نیازهای خود بیابید.
تمام این خدمات رایگان ارائه میشوند.
+
@using (Html.BeginForm("Signup", "Account", FormMethod.Post, new { role = "form" }))
{
@Html.AntiForgeryToken()
@@ -48,6 +49,7 @@
حتی میتوانید رزومه و مشخصات خود را در اختیار ما قرار دهید تا خودمان کارهای متناسب را برایتان بیابیم و از طرف شما اعلام آمادگی کنیم.
تمام این خدمات رایگان ارائه میشوند.
+
@using (Html.BeginForm("Signup", "Account", FormMethod.Post, new { role = "form" }))
{
@Html.AntiForgeryToken()
diff --git a/Sevomin.WebFrontend/Views/Shared/Navbar.cshtml b/Sevomin.WebFrontend/Views/Shared/Navbar.cshtml
index 3ddbbc5..098f594 100644
--- a/Sevomin.WebFrontend/Views/Shared/Navbar.cshtml
+++ b/Sevomin.WebFrontend/Views/Shared/Navbar.cshtml
@@ -22,7 +22,12 @@
}
else
{
- ویرایش پروفایل
+ پروفایل
+
+
خروج از سایت
}
diff --git a/Sevomin.WebFrontend/Views/Shared/PostResult.cshtml b/Sevomin.WebFrontend/Views/Shared/PostResult.cshtml
index 6805512..973f709 100644
--- a/Sevomin.WebFrontend/Views/Shared/PostResult.cshtml
+++ b/Sevomin.WebFrontend/Views/Shared/PostResult.cshtml
@@ -1,3 +1,3 @@
@if (Model != null && Model is Sevomin.Models.PostResultViewModel) {
- @Model.Message
+ @MvcHtmlString.Create(Model.Message)
}
\ No newline at end of file
diff --git a/Sevomin.WebFrontend/Web.config b/Sevomin.WebFrontend/Web.config
index 5da6bf9..4d05837 100644
--- a/Sevomin.WebFrontend/Web.config
+++ b/Sevomin.WebFrontend/Web.config
@@ -32,6 +32,13 @@
+
+
+
+
+
+
+