Partager via


Exemple de code : connecteur SharePoint-LinkedIn

Dernière modification : lundi 22 août 2011

S’applique à : SharePoint Server 2010

Dans cet article
Présentation de l’intégration avec LinkedIn
Structure de la solution
Authentification OAuth et LinkedIn
Utilisation de l’API LinkedIn
Création du travail de minuteur
Stockage des profils utilisateur SharePoint
Gestion des jetons
Création de la page d’adhésion au service
Mise à jour du travail de minuteur LinkedIn
Mise à jour du statut de l’utilisateur
Génération et exécution de l’exemple

Cet exemple montre comment ajouter à votre site MonSite des données sociales issues d’autres réseaux sociaux, comment créer et utiliser de nouvelles propriétés de profil utilisateur, et comment mettre en œuvre de nouveaux travaux de minuteur gérés sur votre site Administration centrale de SharePoint. Dans la première partie de la solution, l’utilisateur doit accorder à l’application SharePoint l’autorisation d’agir au nom de l’utilisateur. Ce processus d’adhésion au service est géré par la norme d’authentification OAuth, utilisée par LinkedIn et de nombreux autres réseaux sociaux informatisés. La page d’inscription gère la récupération et le stockage du jeton nécessaire pour autoriser l’application à mettre à jour les données LinkedIn. La deuxième partie de la solution est mise en œuvre sous la forme d’un travail de minuteur qui détecte les modifications de profil utilisateur. Le travail du minuteur nécessite une page de gestion permettant d’activer et de désactiver le travail du minuteur.

Exemple de code fourni par :Collaborateur MVP Mathew McDermott, Catapult Systems | Collaborateur MVP Andrew Connell, Critical Path Training, LLC

Installez cet exemple de code sur votre ordinateur en téléchargeant le Kit de développement logiciel (SDK) Microsoft SharePoint 2010 ou l’exemple disponible sur le site Code Gallery (éventuellement en anglais). Si vous téléchargez le SDK SharePoint 2010, l’exemple est installé dans votre système de fichiers à l’emplacement suivant : C:\Program Files\Microsoft SDKs\SharePoint 2010\Samples\Social Data and User Profiles.

Présentation de l’intégration avec LinkedIn

À l’heure où bon nombre d’entreprises adoptent l’informatique sociale à tous les niveaux, il est parfois nécessaire de publier depuis l’intérieur de l’entreprise différentes informations auprès de fournisseurs externes de plateformes sociales informatisées. Ces plateformes touchant un public de plus en plus large, bon nombre d’entre elles implémentent des API ouvertes offrant la possibilité de créer des applications accédant aux informations de l’utilisateur.

Cette solution met à jour le statut LinkedIn d’un utilisateur de Microsoft SharePoint Server 2010 lorsqu’il insère le suffixe #li à la fin d’une notification de statut. L’authentification sécurisée entre l’application personnalisée et le fournisseur de services fait partie des principaux problèmes auxquels sont confrontés les développeurs d’applications, quelle que soit la plateforme. LinkedIn utilise la norme OAuth pour l’authentification et l’accès aux API. Le développeur doit ainsi s’inscrire pour obtenir un accès aux API et recevoir une paire de clés d’API à utiliser dans l’application. Une fois cette procédure terminée, la solution peut être déployée et les utilisateurs peuvent à leur tour s’inscrire pour utiliser l’application.

Structure de la solution

Dans la première partie de la solution, l’utilisateur doit accorder à l’application SharePoint l’autorisation d’agir au nom de l’utilisateur. Ce processus d’adhésion au service est géré par la norme d’authentification OAuth. Pour plus d’informations sur le processus d’authentification de LinkedIn, voir la documentation LinkedIn Developer API. Cette solution implémente une page d’inscription permettant à chaque utilisateur d’accepter l’application. La page d’inscription (illustrée dans la figure 1) gère la collecte et le stockage du jeton nécessaire pour autoriser l’application à mettre à jour les données LinkedIn.

Figure 1. Page d’adhésion au service LinkedIn

Page d'abonnement à LinkedIn

La deuxième partie de la solution est un travail de minuteur détectant les modifications de profil utilisateur. Si le champ LinkedIn Status Note est modifié et qu’il contient la chaîne de caractères #li, la solution envoie cette mise à jour vers le compte LinkedIn de l’utilisateur. Le travail du minuteur nécessite une page de gestion (illustrée dans la figure 2) permettant d’activer et de désactiver le travail du minuteur.

Figure 2. Page de gestion du travail de minuteur LinkedIn

Page de Gérer la page du travail de minuteur LinkedIn

Authentification OAuth et LinkedIn

Les caractéristiques de la norme OAuth sont disponibles sur le site OAuth Standards (éventuellement en anglais). Une bibliothèque .NET open source, DotNetOpenAuth, gère les interactions requises pour implémenter les transactions Oauth. LinkedIn Toolkit (éventuellement en anglais), un projet CodePlex utilisant la bibliothèque DotNetOpenAuth, gère les détails de l’API LinkedIn pour cette solution.

Utilisation de l’API LinkedIn

Pour commencer à développer des solutions à l’aide de l’API LinkedIn, vous devez vous inscrire afin d’obtenir une clé d’API et suivre la procédure ci-dessous.

Pour commencer à utiliser l’API LinkedIn

  1. Demandez une clé d’API sur le site LinkedIn Developer Portal (éventuellement en anglais).

  2. Utilisez la clé dans votre application pour permettre aux utilisateurs de s’inscrire afin d’utiliser votre application.

  3. Une fois que les utilisateurs sont inscrits, stockez le jeton utilisateur afin de procéder aux mises à jour de statut au nom des utilisateurs.

Création du travail de minuteur

Pour plus d’informations sur la procédure permettant de créer un travail de minuteur, voir Creating Custom Timer Jobs in Windows SharePoint Services 3.0. Pour procéder aux mises à jour de statut LinkedIn, la présente solution modifie certains détails d’implémentation mentionnés dans cet article.

Stockage des profils utilisateur SharePoint

La première difficulté posée par cette solution concerne l’authentification de SharePoint auprès du système externe et le stockage des jetons d’authentification, en l’absence de toute saisie du mot de passe de l’utilisateur. Les profils utilisateur facilitent le stockage du jeton d’authentification de chaque utilisateur. Les champs signalés dans le tableau 1 doivent être configurés par l’administrateur de la batterie de serveurs ou par le récepteur d’événements FeatureActivated. Le champ de jeton ne doit pas être visible pour l’utilisateur.

Tableau 1. Champs devant être configurés par l’administrateur de la batterie de serveurs ou par le récepteur d’événements FeatureActivated

Nom

Nom complet

Objectif

Type

LI-Token

Jeton sécurisé LinkedIn

Stocker les jetons d’authentification requis par l’application.

Chaîne

LI-Status

Mise à jour du statut LinkedIn

Stocker les préférences de l’utilisateur concernant la mise à jour du statut LinkedIn.

Booléen

Gestion des jetons

Pour pouvoir utiliser le kit de ressources LinkedIn (éventuellement en anglais) et la bibliothèque DotNetOpenAuth, le développeur doit créer un gestionnaire de jetons gérant le stockage et la récupération des jetons OAuth. Le traitement des jetons est assuré par la classe personnalisée implémentant l’interface IConsumerTokenManager. L’interface IConsumerTokenManager est appelée par la bibliothèque DotNetOpenAuth pour gérer le stockage et la collecte des jetons requis pour l’authentification des utilisateurs dans le cadre de la norme OAuth.

La classe SPTokenManager de cette solution stocke les jetons dans les propriétés du profil utilisateur.

Le constructeur utilise l’objet UserProfile comme paramètre.

public SPTokenManager(UserProfile userProfile, string consumerKey, string consumerSecret)
{      
  if (String.IsNullOrEmpty(consumerKey))
  {
    throw new ArgumentNullException("consumerKey");
  }
    this.userProfile = userProfile;
    this.ConsumerKey = consumerKey;
    this.ConsumerSecret = consumerSecret;
}

La méthode ExpireRequestTokenAndStoreNewAccessToken efface le jeton de demande stocké précédemment et stocke un nouveau jeton d’accès.

public void ExpireRequestTokenAndStoreNewAccessToken(string consumerKey, string requestToken, string accessToken, string accessTokenSecret)
{
//Clear the previous request token and its secret, and store the new access token and its secret.  
Debug.WriteLine(String.Format("[OAuth] ExpireRequestTokenAndStoreNewRequestToken : {0} {1}", accessToken, accessTokenSecret));
SetUserProfileValue(requestToken, "", TokenType.RequestToken);
SetUserProfileValue(accessToken, accessTokenSecret, TokenType.AccessToken);
}

La méthode StoreNewRequestToken accepte la demande et la réponse d’authentification, puis récupère et stocke les jetons nécessaires.

public void StoreNewRequestToken(UnauthorizedTokenRequest request, ITokenSecretContainingMessage response)
{
//Store the "request token" and "token secret" for later use
Debug.WriteLine(String.Format("[OAuth] StoreNewRequestToken : {0} {1}", response.Token, response.TokenSecret));
SetUserProfileValue(response.Token, response.TokenSecret, TokenType.RequestToken);
}

La méthode GetTokenSecret renvoie le secret de jeton correspondant au jeton demandé (jeton de demande ou jeton d’accès).

public string GetTokenSecret(string token)
{
  //Return the token secret for the request token OR access token that 
  //you are given.   
  return GetUserProfileValue(token);
}

Ces méthodes appellent une des deux fonctions utilitaires qui lisent et écrivent les valeurs de profil d’utilisateur dans un format qui peut être lu ultérieurement par le travail de minuteur. La méthode SetUserProfile accepte les paramètres de valeur, de secret et de type du jeton. Le champ de profil utilisateur est mis à jour en fonction du type de jeton et les valeurs sont stockées dans un tableau d’objets String à quatre éléments. Lors de l’inscription initiale de l’utilisateur sur le service, les mises à jour sont réalisées par le biais d’une requête GET. L’application prend en compte cette particularité en définissant initialement l’attribut AllowUnsafeUpdates du site Web actuel sur true pour toute la durée de la transaction.

private void SetUserProfileValue(string token, string tokenSecret, TokenType type)
{
  Debug.WriteLine(String.Format("[OAuth] Set the User Profile Value for {0} {1} ", token, tokenSecret));
  UserProfile profile = GetUserProfile();
  bool allowUnsafeUpdates = SPContext.Current.Web.AllowUnsafeUpdates;
  //The tokens are stored as an array of String.
  try
  {
    SPContext.Current.Web.AllowUnsafeUpdates = true;
    //Does out Profile Field exist?
    if (profile[Globals.MSDNLI_TokenField] != null)
    {
      string[] delim = { "|" };
      string[] strTokenArr = new string[4];
      //Does the field contain values?
      if (profile[Globals.MSDNLI_TokenField].Value != null)
      {
        //Get the values.
        strTokenArr = profile[Globals.MSDNLI_TokenField].Value.ToString().Split(delim, StringSplitOptions.None);
      }
      switch (type)
      {
        case TokenType.AccessToken:
          strTokenArr[0] = token;
          strTokenArr[1] = tokenSecret;
          break;
        case TokenType.InvalidToken:
          break;
        case TokenType.RequestToken:
          strTokenArr[2] = token;
          strTokenArr[3] = tokenSecret;
          break;
        default:
          break;
      }
      profile[Globals.MSDNLI_TokenField].Value = String.Format("{0}|{1}|{2}|{3}", strTokenArr);
      profile.Commit();
    }
  }
  catch (Exception ex)
  {
    Debug.WriteLine(String.Format("Failed to load the User Profile. The error was: {0}", ex.Message));
  }
  finally
  {
    SPContext.Current.Web.AllowUnsafeUpdates = allowUnsafeUpdates;
  }
}

La méthode GetUserProfile accepte une valeur de jeton et renvoie le secret de jeton correspondant préalablement stocké.

private string GetUserProfileValue(string token)
{
  Debug.WriteLine("[OAuth] Get the User Profile Value for " + token);
  UserProfile profile = GetUserProfile();
  //Check the LinkedIn properties.
  try
  {
    if (profile[Globals.MSDNLI_TokenField] != null)
    {
      string[] delim = { "|" };
      string[] strTokenArr = new string[4];
      strTokenArr = profile[Globals.MSDNLI_TokenField].Value.ToString().Split(delim, StringSplitOptions.None);
      //Get the values.
      return strTokenArr[Array.IndexOf(strTokenArr, token)+1]; 
    }
    else
    {
      return null;
    }
  }
  catch (Exception ex)
  {
    Debug.WriteLine(String.Format("Failed to load the User Profile. The error was: {0}", ex.Message));
    return null;
  }
}

Création de la page d’adhésion au service

Lorsque l’utilisateur visite la page d’adhésion au service, l’objet UserProfile représentant l’utilisateur actuel est vérifié et l’état d’authentification LinkedIn de cet utilisateur est identifié. Si le jeton adéquat est trouvé, le profil LinkedIn associé est chargé. Si le jeton est introuvable, le bouton J’accepte est activé. Le code de la page gère cette interaction en implémentant des propriétés pour les objets TokenManager, AccessToken et Authorization, qui sont nécessaires pour créer les transactions pour LinkedIn. L’objet TokenManager extrait les clés globales ApiKeys collectées auprès de LinkedIn et les remet à votre application personnalisée.

L’objet TokenManager est créé à partir des valeurs de clés du consommateur et des valeurs personnalisées de la propriété UserProfile.

private SPTokenManager TokenManager
{
  get
  {
return new SPTokenManager(GetUserProfile(), consumerKey, consumerSecret);
   }

Au chargement de la page, l’application commence par tester l’état de l’objet Authorization. Elle crée un nouvel objet Authorization à l’aide de l’objet TokenManager et des objets AccessToken.

    //Begin by testing the Authorization state for the user.
    this.Authorization = new WebOAuthAuthorization(this.TokenManager, this.AccessToken);

S’il s’agit de la transaction de retour d’une visite LinkedIn destinée à initier l’authentification, la page doit terminer la procédure d’autorisation.

if (!IsPostBack)
{
  //Do we need to complete the Authorization rocess?
  string accessToken = this.Authorization.CompleteAuthorize();
                
  if (accessToken != null)
  {
    //If the AccessToken is not null, store it.
    this.AccessToken = accessToken;
    Debug.WriteLine("[OAuth] Redirect: " + Request.Path);
    //Get the user back to where they belong.
    Response.Redirect(Request.Path);
   }
  }

Une fois que l’application a terminé la procédure, elle peut utiliser le jeton d’accès et essayer de récupérer le profil LinkedIn de l’utilisateur actuel.

//Finally, if ready, get the LinkedIn profile.
if (this.AccessToken != null)
{
  try
  {
    LoadLinkedInProfile();
  }
  catch (Exception ex)
  {
    Debug.WriteLine("[MSDN] Error loading LinkedIn Profile: " + ex.Message);
  }
 }

Si l’utilisateur n’a jamais utilisé cette fonctionnalité auparavant, le bouton J’accepte est activé. Lorsqu’il clique dessus, la méthode BeginAuthorize est appelée. Le kit de ressources LinkedIn s’empare alors des clés d’API et les transmet à LinkedIn. LinkedIn renvoie une page dans laquelle l’utilisateur peut entrer ses informations d’identification. L’opération est entièrement gérée par l’API LinkedIn.

protected void btnOptIn_Click(object sender, EventArgs e)
{
  //Initiate the authorization process.
  this.Authorization.BeginAuthorize();           
}

Mise à jour du travail de minuteur LinkedIn

Le travail de minuteur personnalisé assurant la connexion avec LinkedIn utilise l’objet SPTokenManager pour lire et autoriser les mises à jour du statut de l’utilisateur. Le travail de minuteur interroge à intervalle régulier les objets UserProfile dont les champs Status Note ont été mis à jour. Le travail du minuteur crée une liste d’objets UserProfile et les transmet à la méthode chargée de procéder à la mise à jour. Dans la méthode RetrieveUserProfileChanges, le code suivant détermine si la notification de statut contient la chaîne #li et ajoute le profil à la liste des profils dont le statut a été mis à jour.

//If the property has the token in it, add it to the list.
if (statusNote.Trim().EndsWith(Globals.MSDNLI_StatusTag))
{
  Debug.WriteLine("[MSDN] We found the change token in: " + statusNote);
  changedUsers.Add(propertyChange.AccountName, propertyChange.ChangedProfile);
}

Enfin, tous les profils utilisateur sont énumérés et les mises à jour sont envoyées une par une à LinkedIn par la méthode UpdateLinkedIn.

private void UpdateLinkedIn(UserProfile profile)
{
  //Init AccessToken to null; we are going to get it from the profile.
  string AccessToken = null;

  //Use the UserProfile to fetch the LinkedIn tokens.
  try
  {
    if ((profile[Globals.MSDNLI_TokenField] != null) && (profile[Globals.MSDNLI_TokenField].Value != null))
    {
      string[] delim = {"|"};
      string[] strTokenArr = profile[Globals.MSDNLI_TokenField].Value.ToString().Split(delim, StringSplitOptions.None);
      //Get the values.
      if ((strTokenArr != null) || (strTokenArr.Length == 4))
      {
        //Retrieve the access token.
        AccessToken = strTokenArr[0];
        Debug.WriteLine(String.Format("[MSDN] Retrieved the LinkedIn token for user: {0}", profile.DisplayName));
       }
       else
       {
         throw new Exception(String.Format("[MSDN] LinkedIn update failed for user {0}. Profile token field is not formatted correctly.", profile.DisplayName));
        }
      }
    }
    catch (Exception ex)
    {
      Debug.WriteLine("[MSDN] " + ex.Message);
    }

    if ((AccessToken != null) && (AccessToken != String.Empty))
    {
      try
      {
        //Create a token manager.
        SPTokenManager TokenManager = new SPTokenManager(profile, Globals.liApiKey, Globals.liSecretKey);
        //Prep the Authorization state for the user.
        WebOAuthAuthorization Authorization = new WebOAuthAuthorization(TokenManager, AccessToken);
        //Get an instance of the service.
        LinkedInService service = new LinkedInService(Authorization);
        //Issue the update.
        string statusMessage = profile[Globals.MSDNLI_ProfileField].Value.ToString();
        Debug.WriteLine(String.Format("[MSDN] Sending status update to LinkedIn: {0}", statusMessage ));
        service.UpdateStatus(statusMessage);
                    
       }
       catch (LinkedInException li)
       {
         Debug.WriteLine(String.Format("[MSDN] LinkedIn threw an error: {0}", li.Message));
       }
       catch (Exception ex)
       {
         Debug.WriteLine(String.Format("[MSDN] Error updating LinkedIn for user {0} the error was: {1}", profile.DisplayName, ex.Message));
       }
                
     }
   }

Mise à jour du statut de l’utilisateur

Pour l’utilisateur, la publication d’une mise à jour de statut sur LinkedIn est une opération très simple. Il doit simplement ajouter le texte #li dans la zone de texte à la fin de son statut SharePoint. Lors de l’exécution du travail du minuteur, la mise à jour est envoyée à LinkedIn.

Génération et exécution de l’exemple

Les étapes suivantes montrent comment tester ce projet sur le site de développement ou de test.

Pour générer l’exemple

  1. Créez un dossier appelé Microsoft.SDK.Server.Samples, puis décompressez dans ce dossier le fichier MSDN LinkedIn Code.zip.

  2. Ajoutez les fichiers LinkedIn.dll et DotNetOpenAuth.dll (qui se trouvent dans MSDN LinkedIn Code\Shared Libraries\) au Global Assembly Cache. Pour savoir comment réaliser cette opération dans votre environnement de développement ou de test, voir How to: Install an Assembly into the Global Assembly Cache.

  3. Démarrez Visual Studio 2010, puis ouvrez le fichier LinkedInConnection.sln qui se trouve dans le dossier créé à l’étape 1.

  4. Dans la fenêtre Propriétés, spécifiez la valeur d’URL de site correspondant à l’adresse absolue de votre site de test ou de développement (par exemple, http:// monsite/). Pensez à inclure la barre oblique de fermeture. Configurez également l’instruction (using (SPSite site = new SPSite("servername"))) dans le fichier LinkedInConnection.EventReceiver.cs de sorte qu’il utilise cette même valeur d’URL.

  5. Dans le fichier SendColleagueURLNote.EventReceiver.cs, spécifiez l’URL de votre site Administration centrale de SharePoint dans la méthode SendColleagueURLNoteEventReceiver.

  6. Si elles ne sont pas déjà présentes, ajoutez au projet des références aux assemblys suivants :

    • Microsoft.SharePoint.dll

    • Microsoft.SharePoint.ApplicationPages.Administration

    • Microsoft.SharePoint.Security

    • Microsoft.Office.Server.dll

    • Microsoft.Office.Server.UserProfiles.dll

    • DotNetOpenAuth.dll

    • LinkedIn.dll

  7. Obtenez une clé d’API auprès de LinkedIn (éventuellement en anglais).

  8. Configurez l’URL de redirection OAuth de LinkedIn pour qu’elle corresponde à votre page SharePoint. Par exemple, http:// monsite/_layouts/msdn/lisettings.aspx.

  9. Dans le fichier Elements\menuItem.xml, configurez la propriété Url de l’élément UrlAction pour qu’il pointe vers la même page SharePoint (http://monsite/_layouts/msdn/lisettings.aspx).

  10. Dans le fichier Globals.cs, configurez les valeurs MSDNLI_ConsumerKey et MSDNLI_SecretKey en utilisant vos clés.

  11. Dans le menu Générer, sélectionnez Déployer la solution. Une fois la génération terminée, la page d’application est installée sur votre site de test ou de développement.

Pour exécuter l’exemple

  1. Une fois la solution générée et déployée, accédez à votre site Administration centrale pour configurer le travail de minuteur LinkedIn (http://mon_site_administration_centrale/_admin/msdn/LinkedInTimer.aspx).

  2. Accédez à http://monsite/_layouts/msdn/lisettings.aspx puis cliquez sur J’accepte pour vous connecter à votre compte LinkedIn et accepter la communication avec le connecteur.

Voir aussi

Tâches

Procédure : créer et modifier une propriété de profil utilisateur

Autres ressources

Creating Custom Timer Jobs in Windows SharePoint Services 3.0

Documentation LinkedIn Developer API

Clés d’API LinkedIn (éventuellement en anglais)

Site OAuth Standards (éventuellement en anglais)

DotNetOpenAuth

Kit de ressources LinkedIn (éventuellement en anglais)