Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • P.J. Melies 18 posts 108 karma points
    Jun 02, 2021 @ 22:07
    P.J. Melies
    0

    Validate Email uniqueness on custom edit profile form

    I'm trying to do something that seems relatively simple on the surface but I'm finding myself going down a rabbit hole. I'm developing a custom Edit Profile form for members and I'm trying to validate that their email address (if they choose to change it) is not already in use before I update the membership record.

    Apologies for my obtuse code. It's rapidly deteriorating as I'm working on finding a solution that works.

    I have a view that inherits from UmbracoViewPage. In this view I call Html.Action() invoking a ChildActionOnly method on my surface controller.

    @inherits Umbraco.Web.Mvc.UmbracoViewPage<BlockListItem>
    @using System.Web.Mvc.Html;
    @using Umbraco.Core.Models.Blocks;
    @using Umbraco.Web;
    ...
    @Html.Action("MemberEditProfileRenderForm", "MemberProfileSurface")
    

    In the ChildActionOnly method I'm retrieving the current member's profile via Members.GetCurrentMemberProfileModel() and using that ProfileModel to initialize the properties on my custom view model. Then I return a PartialView to display the custom form using the custom view model.

    [ChildActionOnly]
    public ActionResult MemberEditProfileRenderForm()
    {
        // code that builds model ommitted
        return PartialView("Profile/MemberDetailsForm", model);
    }
    

    Originally I had the validation logic in the surface controller method that handles the form post but I found that the ModelState error that I was adding manually wasn't persisted for some reason.

    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult HandleEditMemberDetails([Bind(Prefix = "memberProfileViewModel")] MemberProfileViewModel model)
    {            
        if (!ModelState.IsValid)
            return CurrentUmbracoPage();
    
        var profileModel = Members.GetCurrentMemberProfileModel();
    
        // If the Email has changed verify that there isn't another member with that Email in the system.
        if (!profileModel.Email.Equals(model.Email, StringComparison.OrdinalIgnoreCase))
        {
            var duplicateEmailMember = Members.GetByEmail(model.Email);
            if (duplicateEmailMember != null)
            {
                //The following doesn't work for some reason
                ModelState.AddModelError("memberProfileViewModel.Email", Umbraco.GetDictionaryValue("A member with this e-mail address already exists", "A member with this e-mail address already exists"));
                return CurrentUmbracoPage();
            }
        }
    

    So now I'm trying to code a custom validation attribute and apply it to my view model but the problem I'm running into is I don't know how to DI the MembershipHelper in the attribute constructor properly. Here's my current code for the validator.

    using System;
    using System.Collections.Generic;
    using System.ComponentModel.DataAnnotations;
    using Umbraco.Web.Security;
    
    namespace ICSA.Core.Models.Profile
    {
        public class UniqueEmail : ValidationAttribute
        {
            private MembershipHelper membershipHelper;
    
            public UniqueEmail(MembershipHelper memHelper)
            {
                membershipHelper = memHelper;
            }
    
            protected override ValidationResult IsValid(object value, ValidationContext validationContext)
            {
                var model = (MemberProfileViewModel)validationContext.ObjectInstance;
    
                var profileModel = membershipHelper.GetCurrentMemberProfileModel();
    
                // If the Email has changed verify that there isn't another member with that Email in the system.
                if (!profileModel.Email.Equals(model.Email, StringComparison.OrdinalIgnoreCase))
                {
                    var duplicateEmailMember = membershipHelper.GetByEmail(model.Email);
                    if (duplicateEmailMember != null)
                    {
                        return new ValidationResult("A member with this e-mail address already exists", new List<string>() { "memberProfileViewModel.Email" });
                    }
                }
    
                return ValidationResult.Success;
            }
        }
    }
    

    But now I've realized I don't believe it's possible for me to pass in a reference to the Membership helper in a validation attribute constructor. The following isn't valid syntax.

    [Required(AllowEmptyStrings = false, ErrorMessage = "Please provide your email address")]
    [EmailAddress]
    // membershipHelper isn't a valid attribute parameter type
    [UniqueEmail(membershipHelper)]
    public string Email { get; set; }
    

    At this point I'm pretty sure that I'm missing something relatively simple in my original code where I'm calling ModelState.AddModelError() in the surface controller method. I'm just not sure what's preventing that validation error from appearing in the ValidationMessageFor() in my view.

  • Huw Reddick 1929 posts 6717 karma points MVP 2x c-trib
    Jun 03, 2021 @ 08:55
    Huw Reddick
    100

    I decorate my ViewModel property with

    [System.Web.Mvc.Remote("CheckForEmailAddress", "MemberSurface", ErrorMessage = "The email address is already in use")]
    public string Email { get; set; }
    

    which calls a surface action that returns Json to the UmbracoForm

    public JsonResult CheckForEmailAddress(string email)
    {
        var emailcheck = Members.GetByEmail(email);
    
        if (emailcheck != null)
        {
            return Json("This Email Address is already registered", JsonRequestBehavior.AllowGet);
        }
        else
        {
            return Json(true, JsonRequestBehavior.AllowGet);
        }
    }
    
  • P.J. Melies 18 posts 108 karma points
    Jun 03, 2021 @ 16:52
    P.J. Melies
    0

    Thank you very much. I did not know about the Remote attribute. That is exactly what I was looking for and works like a charm.

  • Huw Reddick 1929 posts 6717 karma points MVP 2x c-trib
    Jun 03, 2021 @ 17:55
    Huw Reddick
    0

    Yes it's a handy decorator if you need to do any custom validation

Please Sign in or register to post replies

Write your reply to:

Draft