Automatic replacement of site feed to Yammer feed with SharePoint sites

I’ve got lot of additional questions on Yammer and SharePoint integration after I wrote the blog post on getting started on building social intranets with SharePoint and Yammer back in March. One of the most asked questions is that how could the out of the box team site feed be replaced automatically with Yammer feed. This is actually something what we have been showing in the Office AMS samples (Solutions\Provisioning.Cloud.Sync) quite a while, with Yammer contributions for that one done by Richard diZerega.

As part of the latest July release of the Office AMS, we included slightly more simplified version of the Yammer integration demo and some reusable code to the Office AMS core component to make this process as easy as possible. You can find this sample code from the latest version of the Office AMS package at path Scenarios/Provisioning.Yammer. Main parts of this code were contributions from Sami Nieminen (Senior Consultant, Microsoft) based on the work what Richard had done previously.

Capabilities

Scenario sample in Office AMS shows how to replace the out of the box site feed with Yammer feed using few different options. In Yammer we can create groups, associate to existing ones or we can also take advantage of so called OpenGraph feeds, which can be created for any object or URL. Officially we encourage to use the OpenGraph option for site feeds, since you really don’t want to create one Yammer group for each SharePoint collaboration sites. This would simply cause massive pollution of yammer groups in your network and end users could not easily locate the needed social connections which are vital for their work.

To be able to execute the code, you’ll need to register or acquire access token for app. This means that you register app to your Yammer network which can than work on behalf of users and replace the out of the box site feed asynchronously during site provisioning. You can follow the steps defined in Yammer developer guidance.

You can control the used access token from web.config in provider hosted web by updating following key.

 <add key="YammerAccessToken" value="PutYourOwnYammerKeyHere" />

Technically you would not actually need the access token when you use the OpenGraph option as long as you assign the network dynamically, but in current code sample we use this with .NET REST operations to resolve the network dynamically. To be precise, you could actually do the whole group Yammer feed automation association without server side code, but adding the needed JavaScript to the provisioned site and when the user first time accesses the site, actual association of the feed to the network would happen. This is precisely what we demonstrated already in the previously released sample referenced earlier in this post, so with this scenario we wanted to show alternative route.

Demonstration of the functionality

This is how things looks in practice also in picture format when you execute the code (Office AMS package – Scenarios/Provisioning.Yammer). Following picture shows how the provider hosted app UI when you start creating sub sites. In the Office AMS samples we are also showing how to replace the out of the box sub site creation experience with the provider hosted app (same Solutions/Provisioning.Cloud.Sync sample), but this particular sample is just mock up so you can access this UI normally from the site content view of the site. Regardless how you access the functionality, remember that we can certainly execute the code for example during the initial site collection creation, so that each collaboration site has Yammer feeds by default in them rather than out of the box SharePoint site feed.

image

So here’s a user experience on the default settings after we have used OpenGraph option. Usage of group option will result pretty much identical rendering structure.

image

 

And here’s the UI from the Yammer side showing the discussion from the particular site in the OpenGraph structure. Provisioning is also configuring orange branding for the site to demonstrate how easily that can be changed during the site creation.

image

Show me the code

Since this code is using reusable Office AMS core component, actual provisioning, branding and yammer logic is only few lines of code. This is exactly the purpose of the Office AMS core component, so that you don’t need to write the same logic all over again. Most of the Office AMS core component code is exposed as extensions to out of the box CSOM objects like the CreateSite method for the Web object, which is create new sub site for the particular Web object as a single line of execution.

 public void CreateSubSite(Web hostWeb, string url, string template,
                             string title, string description, string feedType, string yammerGroupName)
 {
     // Create new sub site
     Web newWeb = hostWeb.CreateSite(title, url, description, template, 1033);
  
     // Set theme for the site
     newWeb.SetThemeToSubWeb(hostWeb, "Orange");
  
     //Remove the out of the box "NewsFeed" web part
     newWeb.DeleteWebPart("SitePages", "Site feed", "home.aspx");
  
     // Let's first get the details on the Yammer network using the access token
     WebPartEntity wpYammer;
     YammerUser user = YammerUtility.GetYammerUser(ConfigurationManager.AppSettings["YammerAccessToken"]);
  
     // Notice that in general we do not recommend of matching Yammer group for each site to avoid "group pollution" in Yammer
     if (feedType == "Group")
     {
         // Get Yammer Group - Creates if does not exist. Let's create these as public by default.
         YammerGroup group =
             YammerUtility.CreateYammerGroup(yammerGroupName, false, ConfigurationManager.AppSettings["YammerAccessToken"]);
         // Get Yammer web part
         wpYammer = YammerUtility.GetYammerGroupDiscussionPart(user.network_name, group.id, false, false);
     }
     else
     {
         // Get OpenGrap object for using that as the discussion feed
         wpYammer = YammerUtility.GetYammerOpenGraphDiscussionPart(user.network_name, Request["SPHostUrl"] + "/" + txtUrl.Text,
                                                                     false, false, "SharePoint Site Feed - " + title);
     }
     // Add Yammer web part to the page
     newWeb.AddWebPartToWikiPage("SitePages", wpYammer, "home.aspx", 2, 1, false);
 }

As you can see from above code, we resolve the YammerUser object regardless of the feed type. This is used to get the network name for the used access token dynamically, but you could simply also hardcode the network name for the OpenGraph option, which would remove the need to use the Yammer access token in first place.

What we do in the code is that we create script web part with specific Yammer embedding JavaScript code in it and we put the web part on the right column in the front page of the site using the AddWebPartToWikiPage extension method, which is also coming from Office AMS core component. Pretty simple way to inject additional web part to first column in second row (thanks for Bert Jansen on this small gem).

Here’s how the GetYammerOpenGraphDiscussionPart method looks like. In this method we just create generic web part object, which is then actually defined in xml format.

 public static WebPartEntity GetYammerOpenGraphDiscussionPart(string yammerNetworkName, 
             string url, bool showHeader, bool showFooter, string postTitle = "", string postImageUrl = "")
 {
     WebPartEntity wpYammer = new WebPartEntity();
     wpYammer.WebPartXml = CreateYammerOpenGraphDiscussionPartXml(yammerNetworkName, url, showHeader, 
                                                                 showFooter, postTitle, postImageUrl);
     wpYammer.WebPartIndex = 0;
     wpYammer.WebPartTitle = "Yammer";
     return wpYammer;
 }

Actual web part definition is parsed as followed to xml format and injected to the site welcome page. Not the most elegant way, but does work and we provide different options which can be controls for the parsing, like are we showing the header or footer of the feed.

 public static string CreateYammerOpenGraphDiscussionPartXml(string yammerNetworkName, string url, bool showHeader, bool showFooter, string postTitle="", string postImageUrl="", bool useSSO = true)
 {
     StringBuilder wp = new StringBuilder(100);
     wp.Append("<?xml version=\"1.0\" encoding=\"utf-8\" ?>");
     wp.Append("<webParts>");
     wp.Append("    <webPart xmlns='https://schemas.microsoft.com/WebPart/v3'>");
     wp.Append("        <metaData>");
     wp.Append("            <type name='Microsoft.SharePoint.WebPartPages.ScriptEditorWebPart, Microsoft.SharePoint, Version=16.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c' />");
     wp.Append("            <importErrorMessage>Cannot import this Web Part.</importErrorMessage>");
     wp.Append("        </metaData>");
     wp.Append("        <data>");
     wp.Append("            <properties>");
     wp.Append("                <property name='Title' type='string'>$Resources:core,ScriptEditorWebPartTitle;</property>");
     wp.Append("                <property name='Description' type='string'>$Resources:core,ScriptEditorWebPartDescription;</property>");
     wp.Append("                <property name='ChromeType' type='chrometype'>None</property>");
     wp.Append("                <property name='Content' type='string'>");
     wp.Append("                <![CDATA[");
     wp.Append("                    <div id='embedded-feed' style='height: 500px;'></div>");
     wp.Append("                    <script type='text/javascript' src='https://assets.yammer.com/assets/platform_embed.js'></script>");
     wp.Append("                    <script>");
     wp.Append("                        yam.connect.embedFeed({");
     wp.Append("                          container: '#embedded-feed'");
     wp.Append("                                , feedType: 'open-graph'");
     wp.Append("                                , feedId: ''");
     wp.Append("                                , config: {");
     wp.Append("                                     use_sso: " + useSSO.ToString().ToLower());
     wp.Append("                                     , header: " + showHeader.ToString().ToLower());
     wp.Append("                                     , footer: " + showFooter.ToString().ToLower());
     wp.Append("                                     , showOpenGraphPreview: false");
     wp.Append("                                     , defaultToCanonical: false");
     wp.Append("                                     , hideNetworkName: false");
     wp.Append("                                        , promptText: 'Start a conversation'");
     wp.Append("                                }");
     wp.Append("                                , objectProperties: {"); 
     wp.Append("                                  url: '" + url + "'");
     wp.Append("                                  , type: 'page'");
     wp.Append("                                  , title: '" + postTitle + "'");
     wp.Append("                                  , image: '" + postImageUrl + "'");
     wp.Append("                                }");
     wp.Append("                            });");
     wp.Append("                        </script>");
     wp.Append("                ]]>");
     wp.Append("                </property>");
     wp.Append("            </properties>");
     wp.Append("        </data>");
     wp.Append("    </webPart>");
     wp.Append("</webParts>");
  
     return wp.ToString();
 }

For the other details, you can easily download the Office AMS package and have a look on the code or debug the code while executing it against Office365 tenant.

Summary and References

As you can see from the code, this is pretty simple process. Someone might ask why isn’t Microsoft doing this by default for Office365, which is absolutely valid question and this could happen in future. Right now however there’s no confirmation on getting this natively for team sites and it might be that it’s not something what is not coming in future either specifically fro the team sites. We have however released lot of information already back in SharePoint Conference 2014 on some of the additional Yammer integration tooling which is coming sooner or later. You might want to check the Office365 roadmap for details on the concentration areas in development.

One side of the story is also the on-premises deployments which we could not automatically associate to Yammer anyway, since there would be no certainty that the customer would have Yammer available, so therefore this cannot happen by default at least in on-premises setup. This blog post is however showing how writing only few lines of code or taking advantage of the Office AMS community project, you can have this capability for your sites easily for Office365 or for on-premises deployments.

Here’s also some generic links on covered topics in this blog post.

Please do give us input on the provided examples with the Office AMS package. Your feedback is highly appreciated and if there’s something what we are missing, just let us know.