Updating Sitecore Social Profile into an external

2019-05-29 08:43发布

问题:

We are using the Sitecore Social Connected Module version 3.0 with Sitecore 7.5. Everything is working fine, in the sense that we log the user in and see the profile information stored into MongoDb. However, there is a requirement to also store the profile information into a separate system.

I've successfully been able to do this by tapping into the following social module events:

<event name="social:connector:user:loggedin">
</event>
<event name="social:connector:user:socialprofileattached">
</event>

In those events, I utilize the following code to read MongoDb and attempt to update the profile in my external system:

INetworkManager networkManager = ExecutingContext.Current.IoC.Get<INetworkManager>(new IParameter[0]);
ISocialProfileManager socialProfileManager = ExecutingContext.Current.IoC.Get<ISocialProfileManager>(new IParameter[0]);
string name = networkManager.GetNetwork(new IDIdentifier(ID.Parse(MembershipParameters.LinkedIn_NetworkId))).Name;
SocialProfile socialProfile = socialProfileManager.GetSocialProfile(Tracker.Current.Contact.ContactId.GetIdentifier(), name);

if (!socialProfile.IsEmpty && socialProfile.Fields.Any())
{
    MembershipUtil.UpdateLinkedInPofile(socialProfile);
}

The problem is that this only works on second+ log in attempts. On the first attempt, the socialProfile.Fields count is always 0. It seems that the code is getting called too early.

What I really need is a event or pipeline AFTER mongo has been updated so that I can retrieve it and update the external system.

Any other suggestions on how to accomplish this would obviously be welcome as well. Thanks.

回答1:

In my experience of using MongoDB with Sitecore the full details about a user's visit are not available until the session ends and some data is stored in session. At session end the data is flushed into MongoDB.

A more reliable way of getting the social data might be to use an AggregationProcessor. You could put some code in this processor to push your social data into your external system at this point.

namespace MyNamespace.Aggregation
{
    public class MyProcessor: AggregationProcessor
    {
      protected override void OnProcess(AggregationPipelineArgs args)
      {
          //your code
      }
   }
}

And then in a config file:

 <configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
 <sitecore>
    <pipelines>
        <group groupName="analytics.aggregation">
            <pipelines>
                <interactions>
                    <processor type="Mynamespace.aggregation.MyProcessor, MyDll" />
                </interactions>
            </pipelines>
        </group>
    </pipelines>
</sitecore>



回答2:

Turns out I was overthinking the whole process. With the help of a decompiler, I realized that the login methods called accepts a Callback URL sublayout parameter. This is where the user gets redirected to after the login, so I overwrote the login button and added a return url to the Callback url. Then in the callback url, I called my database update method.

The login code was decompiled from Sitecore.Social.Client.Connector.Controls.LoginButtonBase.Login

protected void NewLogin(string networkName)
{
    var pageUrl = Request["pageUrl"];
    ILoginHelper loginHelper = ExecutingContext.Current.IoC.Get<ILoginHelper>(new IParameter[0]);
    Sublayout parent = this.Parent as Sublayout;
    NameValueCollection nameValueCollection = (parent != null ? WebUtil.ParseUrlParameters(parent.Parameters) : new NameValueCollection());
    Dictionary<string, object> dictionary = nameValueCollection.AllKeys.ToDictionary<string, string, object>((string key) => key, (string key) => nameValueCollection[key]);
    string callbackUrl = string.Empty;
    if (dictionary.ContainsKey("Callback URL") && !string.IsNullOrEmpty((string)dictionary["Callback URL"]))
    {
        try
        {
            try
            {
                callbackUrl = ExecutingContext.Current.IoC.Get<ILinkManager>(new IParameter[0]).GenerateLink(dictionary["Callback URL"].ToString(), string.Empty);
                if (string.IsNullOrEmpty(callbackUrl))
                {
                    ExecutingContext.Current.IoC.Get<ILogManager>(new IParameter[0]).LogMessage(string.Format("Could not parse the '{0}' link value. Context item ID: {1}", dictionary["Callback URL"], (Sitecore.Context.Item != null ? Sitecore.Context.Item.ID.ToString() : "null")), LogLevel.Error, this);
                }
                else if (!string.IsNullOrEmpty(callbackUrl) && !string.IsNullOrEmpty(pageUrl))
                {
                    callbackUrl += string.Format("?pageurl={0}", pageUrl);
                }
            }
            catch (Exception exception1)
            {
                Exception exception = exception1;
                ExecutingContext.Current.IoC.Get<ILogManager>(new IParameter[0]).LogMessage(string.Format("Could not parse the '{0}' link value. Context item ID: {1}", dictionary["Callback URL"], (Sitecore.Context.Item != null ? Sitecore.Context.Item.ID.ToString() : "null")), LogLevel.Error, this, exception);
            }
        }
        finally
        {
            dictionary.Remove("Callback URL");
        }
    }
    if (string.IsNullOrEmpty(callbackUrl))
    {
        callbackUrl = base.Request.Url.ToString();
    }
    loginHelper.Login(networkName, false, dictionary, callbackUrl);
}

An important note here. The second parameter in the following line indicates whether the profile should be updated asynchronously. If this is set to true, then the profile may still not be available to you when you hit the callback url. Setting that to false ensured that the UserProfile object in the MongoDB record was updated and available to me on the callback page. There is naturally a cost involved with that.

loginHelper.Login(networkName, false, dictionary, callbackUrl);

Call this in the callback url landing page:

INetworkManager networkManager = ExecutingContext.Current.IoC.Get<INetworkManager>(new IParameter[0]);
ISocialProfileManager socialProfileManager = ExecutingContext.Current.IoC.Get<ISocialProfileManager>(new IParameter[0]);
string name = networkManager.GetNetwork(new IDIdentifier(Sitecore.Data.ID.Parse(MembershipParameters.LinkedIn_NetworkId))).Name;
if (Tracker.Current != null)
{
    SocialProfile socialProfile =
        socialProfileManager.GetSocialProfile(Tracker.Current.Contact.ContactId.GetIdentifier(),
            name);

    if (!socialProfile.IsEmpty && socialProfile.Fields.Any())
    {
        MembershipUtil.UpdateLinkedInPofile(socialProfile);
    }
}