MediaWizards Grimoire

Apr 13, 2023 - Huw Reddick

Implementing a Forgot password for members part 2

In part 1 of Implementing a Forgot password, we discussed how to create views and controllers for the process.

The following example will show how to create a custom mailsender service used in part 1.

To create the mail sender we need three things:
  1. An IMemberMailService interface that defines the methods
  2. A MemberMailService that implements the IMemberMailService interface
  3. A composer that adds the service to the runtime.

IMemberMailService

    public interface IMemberMailService
    {
        Task SendResetPassword(string email, string token);
    }

MemberMailService

    public class MemberMailService : IMemberMailService
    {
        private readonly string _fromEmail;
        private readonly HostingSettings _hostingSettings;
        private readonly ILogger _logger;
        private readonly IEmailSender _emailSender;
        private readonly IHostingEnvrionment  _hostingEnvironment;
        private readonly ILocalizationService _localisation;
        private readonly IMemberService _memberService;


        public MemberMailService(IHostingEnvrionment  hostingEnvironment, IOptions<GlobalSettings> globalSettings,IEmailSender emailSender, 
            ILocalizationService localisation,ILogger<MailMessage> logger,IMemberService memberService,IOptions<ContentSettings> contentSettings,
            IOptions<HostingSettings> hostingSettings)
        {

            _logger = logger;
            _emailSender = emailSender;
            _hostingEnvironment = hostingEnvironment;
            _localisation = localisation;
            _memberService = memberService;
            _hostingSettings = hostingSettings.Value;

            _fromEmail = globalSettings.Value.Smtp?.From != null ? globalSettings.Value.Smtp.From : contentSettings.Value.Notifications.Email;
        }


        public async Task SendResetPassword(string email, string token)
        {
            try
            {
                var member = _memberService.GetByEmail(email);
                if (member != null)
                {
                    string baseURL = hostingEnvironment.ApplicationMainUrl.AbsoluteUri;
                    var resetUrl = baseURL + "/forgotpassword/?id=" + member.Id + "&token=" + token;

                    var messageBody = _localisation.GetOrCreateDictionaryValue("Forums.ResetBody",$@"<p>Hi {member.Name},</p>
                    <p>Someone requested a password reset for your account on {_hostingSettings.SiteName}.</p>
                    <p>If this wasn't you then you can ignore this email, otherwise, please click the following password reset link to continue:</p>
                    <p>Please go to <a href='{resetUrl}'>here</a> to reset your password</p>
                    <p>&nnbsp;</p>
                    <p>Kind regards,<br/>The {_hostingSettings.SiteName} Team</p>");

                    EmailMessage message = new EmailMessage(_fromEmail,email,_localisation.GetOrCreateDictionaryValue("Forums.ResetSubject", $@"Password reset requested for {_hostingSettings.SiteName}"),messageBody,true);

                    try
                    {
                        // smtp (assuming you've set all this up)
                        await _emailSender.SendAsync(message, emailType: "Contact");
                    }
                    catch (Exception ex)
                    {
                        _logger.LogInformation("Failed to send the email - probably because email isn't configured for this site\n {0}", ex.ToString());
                    }
                }

            }
            catch (Exception ex)
            {
                _logger.LogError(ex,"Error {0}",ex.Message);
            }
        }
    }

MailServiceComposer

    public class MailServiceComposer : IComposer
    {
        public void Compose(IUmbracoBuilder builder)
        {
            builder.ManifestFilters().Append<EmailValidationManifestFilter>();

            builder.Services.AddScoped<IMemberMailService, MemberMailService>();

        }
    }

 

In this blog post I explain how to implement an email validation flow for Member registration.

In part 2 of my Implementing a Forgot password for members I explain how to implement the IMemberMailService to send the reset password email.

How to implement a ForgotPassword process for Umbraco members in Umbraco 9+

Custom views give you complete control over how a Block is rendered in the backoffice and this enables you to give a better representation of the content. In this article I will explain how I created a custom Block view based on the fullcalendar.io javascript library to display events in the backoffice.

These are my experiences of creating an Umbraco package for the MediaWiz Forums, using package targets, razor class libraries, static web assets and template views.

Many thanks go to Kevin Jump and Luuk Peters without whose help I would probably have given up.