We had the requirement to embed a Yammer poll in SharePoint. This poll would be shown on the homepage of an Office 365 portal. With the Yammer WebPart deprecated, there are two options: Use the REST API, or use the Yammer Embed. We initially tried it with the Yammer Embed, but that is too limited. It only allows to display a certain feed, and has no filter options. The only way to show only one item is to use a Topic feed, and only tag one poll with that Topic. Another showstopper was that we could not apply our styling to the Yammer embed, as it’s hosted in an iframe with no options to style it. It was also annoying that the user had to log in / authenticate every time the browser was closed & reopened.

On the other hand, the REST API has no official documentation around polls. It is possible to retrieve the poll(s), but officially it’s not possible to submit votes. However, I found a solution with the REST API and impersonation with a server-side stored token. This would mean that a user would not have to log in, it would always work.

There are three steps:

  1. Create a Yammer App
  2. Retrieve a test access token as a Yammer Network Admin
  3. Create a SharePoint Provider Hosted add-in, in my case I’ve chosen for MVC.

Create a Yammer App

Go to https://www.yammer.com/client_applications and register a new app. Copy & paste the client ID and client secret to e.g. notepad.

Retrieve a test token

Go to https://developer.yammer.com/v1.0/docs/test-token and follow the steps to create a test token. Copy it to notepad as well.

Create a provider hosted add-in

The next step is to create a SharePoint Provider Hosted add-in. You will need an Office 365 Developer site, and a place to host your website. In my case I’m using Azure as my provider. Create a new SharePoint add-in, select MVC, and let’s directly add an app part that will display our Yammer Poll. So I created an app part with a new MVC controller. I assume for now you know how to do that and will focus on the Yammer part.

First, we need to store the token and client ID. As we use Azure, the Web App settings in Azure are perfect to do this. I’ve stored the token as a custom connection string, the client ID as a custom setting. Then I can retrieve them by calling:

private const string ENV_VAR_AUTHENTICATION_TOKEN = "CUSTOMCONNSTR_authentication_token";
private const string ENV_VAR_YAMMER_APP_CLIENT_ID = "APPSETTING_yammer_app_client_id";

var authenticationToken = Environment.GetEnvironmentVariable(ENV_VAR_AUTHENTICATION_TOKEN);
var clientId = Environment.GetEnvironmentVariable(ENV_VAR_YAMMER_APP_CLIENT_ID);

With this token you are going to retrieve a list of users, to retrieve the Yammer User ID of the current user. Of course you could craft all requests yourself, but I’ve chosen to incorporate the unofficial Yammer REST API wrapper. I found the official .NET SDK too complicated, and it seemed to focus only on Windows apps / Windows Phone.

The REST API wrapper makes it very easy to call any official REST API method. I’ll still need to craft custom HttpRequests for the unofficial ones, more about that later.

The following code retrieves the current SharePoint user, calls Yammer for the user with that email address, and then retrieves the access token for that user. Next, I will retrieve the messages in a given topic, grab the first one, and put the HTML of the poll in the ViewBag:

using (var clientContext = spContext.CreateUserClientContextForSPHost())
{
  if (clientContext != null)
  {
    spUser = clientContext.Web.CurrentUser;
    clientContext.Load(spUser, user => user.Email);
    clientContext.ExecuteQuery();
    using (var yammerClient = new Client(authenticationToken))
    {
        var email = spUser.Email;
        // this code hangs if the email address does not exist 🙁
        UserFullInfo users = await yammerClient.Users.GetByEmail(email).ConfigureAwait(false);
        string impToken = authenticationToken;
        if (users != null)
        {
          var url = string.Format(
 "https://www.yammer.com/api/v1/oauth/tokens.json?user_id={0}&consumer_key={1}",
 users.Id, clientId);
          string tokenInfo = MakeGetRequest(url, authenticationToken);
          var tokens = new JavaScriptSerializer().Deserialize<List<YammerToken>>(tokenInfo);
          impToken = tokens[0].token;
        }
        using (var impersonatedClient = new Client(impToken))
        {
          var messagesTask = impersonatedClient.Messages.GetByTopicId(yammerTopicId);
          var messages = await messagesTask.ConfigureAwait(false);
          var html = messages.Meta.YModules[0].InlineHtml;
          viewBag.Message = html;
          ViewBag.ImpToken = impToken;
        }
      }
    }
 }

While not officially documented, I found out that the HTML for the poll is in the YModules list as a property called InlineHtml.

Important too is that above code will hang at retrieving the user by email address, if it cannot find that email address. Not sure yet why. I already solved the async deadlock issue by using ConfigureAwait(false).

For custom HttpRequest, I’ve used code from this blog post.

If you run this, you should now see a poll in your app part. However, the links (to vote / show results / etc.) won’t do anything. We will need to attach our custom event handlers to the links to make the voting work.

Attaching event handlers to the links in the HTML

First, we need to attach handlers to the “Change Vote” and “Go to Results” links. That’s quite easy:

var questionsContainer = $("div[class*='questions_container']");
var resultsContainer = $("div[class*='-results_container']");
$("a:contains('Change Vote')").click(function() {
  questionsContainer.show();
  resultsContainer.hide();
});
$("a:contains('Go to Results')").click(function () { questionsContainer.hide(); resultsContainer.show(); });

The next thing is to attach an event handler to the Vote button. I’ve done this by creating a server side action, and I’m calling that using AJAX. As I’ve said before, it’s not officially documented to vote on a poll using the REST API. Luckily we have Fiddler. When a user submits a poll vote, a POST request is submitted to:

https://www.yammer.com/api/v1/ymodules/{ymoduleid}/inline

with as data:

rpc=poll.setViewerAnswerFromForm&form%5Bchoice%5D={val}&access_token={token};

  • ymoduleid is the yModuleId of the poll, retrieved earlier
  • val is the value of the radio button that was selected
  • token is the access token. Not sure why it’s also here, because it still needs to be present in the Bearer token.

So, the server side method is quite easy:

[HttpPost] public ActionResult VotePoll(string val, string yModuleId, string token) {
  var url = string.Format("https://www.yammer.com/api/v1/ymodules/{0}/inline", yModuleId);
  var data = "rpc=poll.setViewerAnswerFromForm&form%5Bchoice%5D=" + val + "&access_token=" + token;
  var request = MakePostRequest(data, url, token, "application/x-www-form-urlencoded; charset=UTF-8");
  return Json(request); }

Last thing to do is to attach the Vote button to this server side method. In the View, add this JavaScript:

var token = '@ViewBag.ImpToken';
var cssClass = questionsContainer.attr('class');
var yModuleId = cssClass.replace("ymodule-instance-", "").replace("-questions_container", "");
$("div[class*='questions_container'] button[type='submit']").click(function () {
  var val = $("div[class*='questions_container'] input[name=choice]:checked").val();
  $.ajax({
    url: '@Url.Action("VotePoll")',
    type: "POST",
    dataType: "json",
    data: { val: val, yModuleId: yModuleId, token: token },
    complete: function () {
      window.location.reload();
    },
    success: function (data) { },
    error: function (e) {
  }});
});
  • Retrieve the ymodule by extracting it from the CSS class of the container
  • Attach a click event to the submit button that posts back to the server
  • On completion, reload such that the vote is visible.

Create a poll to test

Everything in Yammer is based on feeds: Group Feeds, User Feeds, and Topic Feeds. For above code to work, you will either need to put the poll in a certain group, or attach a certain topic. As you’ve seen, I have chosen to use topics. In our case, the poll that must be shown in SharePoint, will be attached a topic called “Home page Poll”. The ID of that topic is a configurable ID in the AppPart, and passed to the Provider as a property in the query string.

You can find the topic ID is to attach a topic to the poll, and then click on the topic link. The URL will be something like: https://yammer.com/network/topics/12345 . 12345 is the Topic ID.

That’s it! You now offically have built your Yammer Vote Embed App Part!