If you open up most of the default WFFM save actions in Sitecore, you'll notice that they don't have anything filled in the Assembly and Class fields, but have some sort of xpath in field Factory Object name

This Factory is a DI container that Sitecore has - I haven't delved deep into it yet, but now that I've realized the it's there and part of how it works I certainly plan to.

My reason for looking into this is pretty simple. There is a very nice article about shoehorning values into the AdaptedResultList object that is passed into a Save Action. I wanted to use that concept while subclassing the Send Message action. The reason is that I wanted to have a contact form, with a dropdown list where there were various "departments" that the user could choose to send the contact request to.

Pretty standard stuff, you'd say. Not so much though if you want to use the standard Send Mail Message action to send the email to different recipients (or from different senders) depending on the "department" chosen, particularly if you'd prefer for security (see "email harvesting") reasons not to expose those email addresses in the HTML source code of the final contact page. Doing so, you no longer have the desired email in a value in the AdaptedResultList object, so bye-bye standard Send Mail save action, Hello Custom Code!

So you need to subclass the Sitecore.WFFM.Actions.SaveActions.SendMessage action. But here's the catch, once you do it, you get an error about two properties of the base class that are not instantiated properly. Then you go into ilSpy, and realize that this save action has a non-parameterless constructor where these two properties are passed in as parameters, and with an Interface type at that:

public SendMessage(ISettings settings, IMailSender mailSender);

That's dependency injection right there if I ever saw one.

The sitecore documentation about custom save actions says nothing about this. The example code used creates a new save action from scratch.

"But I don't want to re-invent the wheel!" I can hear you cry out in frustration. Well, at least I did. After many fruitless hours of searching for any documentation or hints about the DI container and scheme used in Sitecore, I gave up, and practically copy-pasted the decompiled code from the SendMessage action into my own.And when that didn't work, I also went on and added the decompiled code of the "SendMessage" pipeline processors into my class. And then I got stuck at a particular point where I had to get an instance of

Sitecore.Forms.Core.Dependencies.DefaultImplMailSender
to push into the
private readonly IMailSender mailSender;
property of the action.

I checked, and saw I could go parameterless, so I did:

mailSender = new Sitecore.Forms.Core.Dependencies.DefaultImplMailSender();
And got the dreaded warning:
'DefaultImplMailSender.DefaultImplMailSender()' is obsolete
The code worked, but it's definitely not future proof, and this type of contact form is popping up time and again, so I want this save action as future proof as possible.

So back to square one-ish. Or is it? In a rare moment of clarity, I searched the configuration for the actual xpath (the "Factory Object Name"), and for parts of it, and lo! and behold! In a file unsurprisingly named "Sitecore.WFFM.Dependencies.config" in App_config/Include folder, I found the following:

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <wffm>
...
      <!--Save actions constructor configuration-->
      <actions>
...
        <sendMessage type="Sitecore.WFFM.Actions.SaveActions.SendMessage, Sitecore.WFFM.Actions">
          <param name="settings" ref="/sitecore/wffm/settings" />
          <param name="mailSender" ref="/sitecore/wffm/mailSender" />
        </sendMessage>
...
	  </actions>
...
    </wffm>
  </sitecore>
</configuration>
there it was! /wffm/actions/sendMessage, just as the Factory Object name said it would be. And with the fully qualified types for the two arguments of the constructor as well!

I simply added the following within the actions

<SendEmailTest type="MyCustomNamespace.SendEmailTest, MyAssembly">
          <param name="settings" ref="/sitecore/wffm/settings" />
          <param name="mailSender" ref="/sitecore/wffm/mailSender" />
</SendEmailTest>

And just coded this as proof of concept:

    public class SendEmailTest: SendMessage
    {
        public SendEmailTest(ISettings settings, IMailSender mailSender) : base(settings, mailSender) { }
        public override void Execute(ID formId, AdaptedResultList adaptedFields, 
                          ActionCallContext actionCallContext = null, params object[] data)
        {
            Sitecore.Diagnostics.Log.Info("enter the test execute", this);
            base.Execute(formId, adaptedFields, actionCallContext, data);
        }
    }

Then went on to duplicate item /sitecore/system/Modules/Web Forms for Marketers/Settings/Actions/Save Actions/Send Email Message, renamed it into Send Mail Test (names don't really matter though) and changed the Factory Object name into the following:

/sitecore/wffm/actions/SendEmailTest

I added my new save action into a form and tested, and sure enough, this popped up in the log:

25072 17:30:14 INFO  enter the test execute

Isn't it nice when things just work? So now I was free to do whatever I wanted with the AdaptedResultList and then just call the base.Execute method with the altered one.

<imahappydev>Celebratory dance</imahappydev>

Happy coding!