12

We have a client that is on 8.1u1 and is using WFFM. They want to integrate the data collected on the form into Dynamics CRM, however the OOTB save actions for creating entities in the CRM aren't flexible enough. They have some complex business rules on when to create Contacts, when to create Leads, connecting them to Organizations, etc...

To date, we have accomplished this with a custom form, but now they want the ability to use that same logic, but create more forms on the fly. We already have the business logic nicely abstracted and injected into the controller, so I figured I could just wrap that in a Save Action.

So, my question is how can I "inject" the implementation of our business rules into my WFFM save action and reuse that?

13

The WFFM module was significantly re-written in version 8.1+ and allows configuration driven dependency injection out of the box.

For example, if you take a look at the /sitecore/system/Modules/Web Forms for Marketers/Settings/Actions/Save Actions/Update Contact Details Save Action then you will see the following item definition:

Factory Object Name: /sitecore/wffm/actions/updateContactDetails
Assembly: [Empty]
Class: [Empty]
MVC Type: [Empty]

Note that the Factory Object Name field is new and in previous versions the other 3 fields would be filled in with the assembly details of the Save Action. Instead, in the latest versions in Sitecore.WFFM.Dependencies.config you will find the following definition:

<wffm>    
  <actions>
    ...
    <updateContactDetails type="Sitecore.WFFM.Actions.SaveActions.UpdateContactDetails, Sitecore.WFFM.Actions">
      <param name="analyticsTracker" ref="/sitecore/wffm/analytics/analyticsTracker" />
      <param name="authentificationManager" ref="/sitecore/wffm/authentificationManager" />
      <param name="logger" ref="/sitecore/wffm/logger" />
      <param name="facetFactory" ref="/sitecore/wffm/analytics/facetFactory" />
      <param name="contactManager" ref="/sitecore/wffm/analytics/contactManager" />
    </updateContactDetails>
    ...
  </actions>

  <logger type="Sitecore.Forms.Core.Dependencies.DefaultImplLogger, Sitecore.Forms.Core"/>
  <authentificationManager type="Sitecore.Forms.Core.Dependencies.DefaultImplAuthentificationManager, Sitecore.Forms.Core"/>
  <analytics>
    <analyticsTracker type="Sitecore.WFFM.Analytics.Dependencies.DefaultImplAnalyticsTracker, Sitecore.WFFM.Analytics"/>
    <facetFactory type="Sitecore.WFFM.Analytics.Dependencies.DefaultImplFacetFactory, Sitecore.WFFM.Analytics"/>
    <contactManager type="Sitecore.WFFM.Analytics.Dependencies.ContactManagerWrapper, Sitecore.WFFM.Analytics">
      <param name="contactManager" ref="/sitecore/tracking/contactManager" />
    </contactManager>
    ...
  </analytics>
</wffm>

The Save Action accepts these settings as Constructor parameters, the params defined in config must match those in your code. These will all be wired up automatically by the module.

public class UpdateContactDetails : WffmSaveAction
{
    private readonly IAnalyticsTracker analyticsTracker;
    private readonly IAuthentificationManager authentificationManager;
    private readonly ILogger logger;
    private readonly IFacetFactory facetFactory;
    private readonly IContactManager contactManager;

    public string Mapping { get; set; }

    public UpdateContactDetails(IAnalyticsTracker analyticsTracker, IAuthentificationManager authentificationManager, ILogger logger, IFacetFactory facetFactory, IContactManager contactManager)
    {
      Assert.IsNotNull((object) analyticsTracker, "analyticsTracker");
      ...
      this.analyticsTracker = analyticsTracker;
      this.authentificationManager = authentificationManager;
      this.logger = logger;
      this.facetFactory = facetFactory;
      this.contactManager = contactManager;
    }

    public override void Execute(ID formId, AdaptedResultList adaptedFields, ActionCallContext actionCallContext = null, params object[] data)
    {
      this.UpdateContact(adaptedFields);
    }

    ...
}
  • Perfect - this is exactly what I was looking for! – LonghornTaco Oct 21 '16 at 18:32
1

Had a very similar situation a while ago (just another CRM) and did not have much time (as usual) to get it working. I ended up using a service locator pattern, probably not the most elegant solution but it works.

In the constructor of the save action I resolve the dependencies, in our case using Autofac:

// Constructor
public SaveToSomewhere()
{
    XFactory = ApplicationContainer.Resolve<IXFactory>();
    XRepository = ApplicationContainer.Resolve<IXRepository>();
}

public IXFactory XFactory { get; set; }
public IXRepository XRepository { get; set; }

It is a solution, but if there are more elegant ones - really injecting instead of resolving - I'd also love to learn those.

  • Totally understand using service locator in a pinch, but prefer not to unless it's the absolute last option. – LonghornTaco Oct 21 '16 at 18:33
  • I understand your preference - as I mentioned this is just an option (maybe still for older projects) but when upgrading this project I might refactor this now that I know it is possible. And lets re-post this question when the new forms module arrives ;) – Gatogordo Oct 22 '16 at 8:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.