I'm trying to send an e-mail to a newly created member using the new MemberService in 7.1 but I seem to be unable to find the actual name of the user (like many others it seems) and it doesn't seem like any of the 'similar topics' provides an answer.
Here's what I do. In application startup I add a listener to the created event like so:
MemberService.Created += MemberService_Created;
And in MemberService_Created I have access to the newly created IMember
void MemberService_Created(IMemberService sender, NewEventArgs<IMember> e) {
try {
var name = e.Entity.Name; // No it's not!!
var email = e.Entity.Email;
var username = e.Entity.Username;
SomeHelper.SendEmail(name, email, username);
} catch (Exception ex) {
LogHelper.Error<ApplicationStartup>("Error sending new member email.", ex);
}
}
Unfortunately in the code sample above the name variable is just the Username. I've looked at all the properties of the IMember but there is none that contains the value I've entered as the name of the user when creating it in the Umbraco backend.
Am I completely missing something or is there just no easy way to get to the user name from the Created event?
Alas, there is no Text property on IMember. Also tried new Member(id) which does have a .Text property, but it also contains the username, not the node name.
Hmm, I thought that was the object you'd get when using the new Member(id) syntax, which I'm sure gives the username as result instead of the node name.
I thought I used that one, but it's off the top of my head, those namespaces all look alike. I'll take a look tomorrow and see. Otherwise it's a good opportunity to try those panic trousers ;)
What action is creating the member that you are seeing their username as their 'Name'? (i.e. is it the creation of the member in the back office, via a front-end script, etc... ?) In lots of cases if a member signs up, their username will by default be their name.
Here's a side bit of information that you need to know:
Ok i can replicate this logic, here's what is happening
When the member is first created, it uses the membership provider to first create the member - this is required to support non-umbraco membership providers of course
Because a the Umbraco membership is active by default, this uses the UmbracoMembershipProvider which wraps the IMemberService. A membership provider has no notion of a 'real name', so it uses the login name when calling into the IMemberService.CreateMemberWithIdentity (which is the method that executes the 'Created' event)
After the initial creation of the member with the membership provider, we then check if the membership provider is an Umbraco membership provider, if so, then we lookup the raw IMember, set it's name value and save it using the IMemberService.Save method - which will also trigger an event.
So the first thing you want to do is stop using the Created event as noted previously since you'll only want to send an email to members that have actually been persisted to the database.
You'll then want to use the Saved event, but since the member has already been saved with an identity, you won't simply just be able to check if the member is new based on it's ID or the extension method mentioned in the doc link I sent above. Instead you should track who you've sent the email to, maybe even a custom property on the member itself. In that case, you'd want to subscribe to the 'Saving', ensure that the entity is not new (already has an identity), ensure you custom flag has not been set, then set your custom flag to true, send the email (recommended to do that async) and the member will then be persisted with your sent flag set.
Moving forward, in the core we can look at calling a custom overloaded method on the Umbraco membership provider that also accepts a Name value, then check if an Umbraco membership provider is active and call that method, which would in turn call the CreateWithIdentity with the real name value. The reason this is not done now was because this is how the legacy membership stuff worked and I couldn't break compatibility but I think there might be a way to get this working without breaking anything, we'll see.
Unfortunately I cannot make this change without introducing a breaking change in the API. I'd have to modify the abstract method PerformCreateUser in the class Umbraco.Core.Security.UmbracoMembershipProviderBase
This might be an 'acceptable' breaking change since I highly doubt anyone is inheriting from this class apart from us, but that's just an assumption.
In the meantime you'll probably have to use the work around as described above.
Sorry to drag this up again but the problem still exists and I want to confirm my understanding of the workaround is correct as I've implemented what I understood of this thread and the results are still the same.
Should it actually be as follows?
Add a new boolean property to the member type e.g. SendWelcomeEmail.
Subscribe to the Member.Saving event & check the entity has no identity whilst setting the new SendWelcomeEmail property value to true.
Subscribe to the Member.Saved event, check the new property and if true send the email and set the new property value to false thus allowing us access to the new member properties and disabling the flag for future so emails are not set again.
For anyone else that might be fighting with this, I finally have a solution with thanks to Shannon. The solution is on the issue on the tracker but for the benefit of those reading on here, the solution was as follows…
foreach (var member in e.SavedEntities)
{
// We can skip this iteration if the member has not yet been persisted or they have already received the email
if (member.GetValue<MemberStatus>("MemberCreationStatus") .Equals(MemberStatus.PasswordGenerated)) continue;
if (member.GetValue<MemberStatus>("MemberCreationStatus").Equals(MemberStatus.InitialServiceSave))
{
member.SetValue("MemberCreationStatus", MemberStatus.PasswordGenerated.ToString());
memberSvc.Save(member, false);
// Generate a new password that meets minimum requirements
var password = Membership.GeneratePassword(
Membership.MinRequiredPasswordLength,
Membership.MinRequiredNonAlphanumericCharacters);
// Save the password to persist it before we send the email
memberSvc.SavePassword(member, password);
// Send the member a welcome email with the initial password
Members.SendWelcomeEmail(member, password);
}
}
My Enum is quite simply:
public enum MemberStatus
{
InitialCreate,
InitialProviderSave,
InitialServiceSave,
PasswordGenerated
}
I've had to return to this due to reports of the welcome emails not being sent still. The Member.Saving event handler has been modified as follows:
if (!member.HasIdentity) // This will mean that the member isn't created yet
{
member.SetValue("MemberCreationStatus", MemberStatus.InitialCreate.ToString());
sender.Save(member, false); // Without this the welcome emails are still not sent!
}
else if (member.GetValue<MemberStatus>("MemberCreationStatus").Equals(MemberStatus.InitialCreate))
{
// The member has been created, if the username and name are the same it has most likely been set by the provider
if (member.Username.Equals(member.Name))
{
member.SetValue("MemberCreationStatus", MemberStatus.InitialProviderSave.ToString());
}
else
{
// We reach this stage when the user is manually created in the back office as editors don't use the email as the username
member.SetValue("MemberCreationStatus", MemberStatus.InitialServiceSave.ToString());
}
}
else if(member.GetValue<MemberStatus>("MemberCreationStatus").Equals(MemberStatus.InitialProviderSave))
{
// Finally, udpate the status to indicate the service has now saved the member
member.SetValue("MemberCreationStatus", MemberStatus.InitialServiceSave.ToString());
}
It really is far more complicated than it should be and I hope this gets some attention soon. Hopefully, this helps someone else out and save them tearing their hair out trying to get it to work as I did.
Get actual name of member
I'm trying to send an e-mail to a newly created member using the new MemberService in 7.1 but I seem to be unable to find the actual name of the user (like many others it seems) and it doesn't seem like any of the 'similar topics' provides an answer.
Here's what I do. In application startup I add a listener to the created event like so:
And in MemberService_Created I have access to the newly created IMember
Unfortunately in the code sample above the name variable is just the Username. I've looked at all the properties of the IMember but there is none that contains the value I've entered as the name of the user when creating it in the Umbraco backend.
Am I completely missing something or is there just no easy way to get to the user name from the Created event?
Try
e.Entity.Text
.Alas, there is no Text property on IMember. Also tried new Member(id) which does have a .Text property, but it also contains the username, not the node name.
Interestingly, you're right, it seems like we don't expose that information currently.. ouch!
Looks like you can only get it from the obsoleted API.. :(
Not sure yet how we're going to fix this one, hope Shannon comes up with a good one: http://issues.umbraco.org/issue/U4-5287
Hmm, I thought that was the object you'd get when using the new Member(id) syntax, which I'm sure gives the username as result instead of the node name.
Depends on your usings.. :) That's why I included the complete old namespace.
I thought I used that one, but it's off the top of my head, those namespaces all look alike. I'll take a look tomorrow and see. Otherwise it's a good opportunity to try those panic trousers ;)
Hi,
I've added these two tests here that work:
https://github.com/umbraco/Umbraco-CMS/commit/b5f900e789fb2e40cc166e5eee56e84f717b2319
So here's what we need to know:
Here's a side bit of information that you need to know:
I will update the documentation to reflect all of this accurately
Ok i can replicate this logic, here's what is happening
So the first thing you want to do is stop using the Created event as noted previously since you'll only want to send an email to members that have actually been persisted to the database.
You'll then want to use the Saved event, but since the member has already been saved with an identity, you won't simply just be able to check if the member is new based on it's ID or the extension method mentioned in the doc link I sent above. Instead you should track who you've sent the email to, maybe even a custom property on the member itself. In that case, you'd want to subscribe to the 'Saving', ensure that the entity is not new (already has an identity), ensure you custom flag has not been set, then set your custom flag to true, send the email (recommended to do that async) and the member will then be persisted with your sent flag set.
Moving forward, in the core we can look at calling a custom overloaded method on the Umbraco membership provider that also accepts a Name value, then check if an Umbraco membership provider is active and call that method, which would in turn call the CreateWithIdentity with the real name value. The reason this is not done now was because this is how the legacy membership stuff worked and I couldn't break compatibility but I think there might be a way to get this working without breaking anything, we'll see.
Unfortunately I cannot make this change without introducing a breaking change in the API. I'd have to modify the abstract method
PerformCreateUser
in the classUmbraco.Core.Security.UmbracoMembershipProviderBase
This might be an 'acceptable' breaking change since I highly doubt anyone is inheriting from this class apart from us, but that's just an assumption.
In the meantime you'll probably have to use the work around as described above.
Sorry to drag this up again but the problem still exists and I want to confirm my understanding of the workaround is correct as I've implemented what I understood of this thread and the results are still the same.
Should it actually be as follows?
SendWelcomeEmail
.Member.Saving
event & check the entity has no identity whilst setting the newSendWelcomeEmail
property value totrue
.Member.Saved
event, check the new property and iftrue
send the email and set the new property value tofalse
thus allowing us access to the new member properties and disabling the flag for future so emails are not set again.Cheers, Simon
For anyone else that might be fighting with this, I finally have a solution with thanks to Shannon. The solution is on the issue on the tracker but for the benefit of those reading on here, the solution was as follows…
In the MemberService.Saving event handler I am doing the following:
And in the MemberService.Saved event I am doing the following:
My Enum is quite simply:
I've had to return to this due to reports of the welcome emails not being sent still. The Member.Saving event handler has been modified as follows:
It really is far more complicated than it should be and I hope this gets some attention soon. Hopefully, this helps someone else out and save them tearing their hair out trying to get it to work as I did.
is working on a reply...