コード サンプル: SharePoint から LinkedIn へのコネクタ
最終更新日: 2011年8月22日
適用対象: SharePoint Server 2010
この記事の内容
LinkedIn 統合の概要
ソリューションの構成
OAuth 認証と LinkedIn
LinkedIn API の使用
タイマー ジョブの作成
SharePoint ユーザー プロファイル格納領域
トークンの管理
[オプトイン] ページの作成
LinkedIn タイマー ジョブの更新
ユーザー ステータスの更新
サンプルのビルドと実行
このサンプルは、他のソーシャル ネットワーキング サイトのソーシャル データを自分の MySite に追加する方法、新しいユーザー プロファイル プロパティを作成して使用する方法、および SharePoint サーバーの全体管理サイトで管理される新しいタイマー ジョブを実装する方法を説明します。ソリューションの最初のパートでは、ユーザーに代わって操作できる権限を SharePoint アプリケーションに付与する必要があります。この "オプトイン" プロセスは、LinkedIn と他の多くのソーシャル コンピューティング サイトで使用される OAuth 認証標準に基づいて処理されます。サインアップ ページでは、アプリケーションが LinkedIn データを更新できるようにするために必要なトークンの取得と格納を実行します。ソリューションの 2 番目のパートは、ユーザー プロファイルの変更をクエリするタイマー ジョブとして実装されます。タイマー ジョブには、タイマー ジョブを有効/無効にするための管理ページが必要です。
サンプル コードの提供者: Mathew McDermott、Catapult Systems | Andrew Connell、Critical Path Training, LLC
Microsoft SharePoint 2010 Software Development Kit (英語) (SDK) をダウンロードするか、Code Gallery (英語) からサンプルをダウンロードして、このコード サンプルをコンピューターにインストールします。SharePoint 2010 SDK をダウンロードした場合は、サンプルはファイル システムの C:\Program Files\Microsoft SDKs\SharePoint 2010\Samples\Social Data and User Profiles にインストールされます。
LinkedIn 統合の概要
企業は企業全体でソーシャル コンピューティングを採用しているので、場合によっては、組織内部からソーシャル コンピューティング プラットフォームの外部プロバイダーに情報を発行する必要があります。これらのプロバイダーは幅広いユーザーをサポートしているので、プロバイダーの多くは、ユーザーの情報にアクセスするアプリケーションを作成できるオープン API を実装しています。
Microsoft SharePoint Server 2010 ユーザーが Status Note の最後にテキスト #li を追加すると、このソリューションは、そのユーザーの LinkedIn のステータスを更新します。プラットフォームからのアプリケーション開発で直面する大きな課題は、カスタム アプリケーションからサービス プロバイダーへの安全な認証です。LinkedIn では、認証と API アクセスに OAuth 標準を使用します。このために開発者は、API アクセスを申請し、アプリケーションで使用する API キー ペアを受け取る必要があります。このプロセスが完了したら、ソリューションは展開可能になり、ユーザーはサインアップしてアプリケーションを使用できるようになります。
ソリューションの構成
ソリューションの最初のパートでは、ユーザーに代わって操作できる権限を SharePoint アプリケーションに付与する必要があります。この "オプトイン" プロセスは、OAuth 認証標準に基づいて処理されます。LinkedIn 認証プロセスに関する詳細は、「LinkedIn Developer API documentation」を参照してください。このソリューションは、個々のユーザーがアプリケーションにオプトインできるサインアップ ページを実装します。サインアップ ページ (図 1) では、アプリケーションが LinkedIn データを更新できるようにするために必要なトークンの取得と格納を実行する必要があります。
図 1. [LinkedIn オプトイン] ページ
ソリューションの 2 番目のパートは、ユーザー プロファイルの変更をクエリするタイマー ジョブです。LinkedIn の Status Note フィールドが変更され、テキスト #li が追加されると、ソリューションは更新内容をユーザーの LinkedIn アカウントに送信します。タイマー ジョブには、タイマー ジョブを有効/無効にするための管理ページ (図 2) が必要です。
図 2. LinkedIn タイマー ジョブの管理ページ
OAuth 認証と LinkedIn
OAuth 標準の詳細については、「OAuth Standards site (英語)」を参照してください。オープン ソースの .NET ライブラリ DotNetOpenAuth は、OAuth トランザクションの実装に必要な対話操作に対応します。DotNetOpenAuth を使用する LinkedIn Toolkit の CodePlex プロジェクトは、このソリューション用の LinkedIn API の情報を処理します。
LinkedIn API の使用
LinkedIn API を使用して開発を始めるには、API キーを申請し、以下の基本手順に従う必要があります。
LinkedIn API の使用を開始するには
LinkedIn Developer Portal (英語) に API キーを申請します。
ユーザーがアプリケーションを登録できるように、アプリケーションでこのキーを使用します。
ユーザーがサインアップしたら、アプリケーションがユーザーの代わりにステータスの更新を実行できるように、ユーザーのトークンを格納します。
タイマー ジョブの作成
タイマー ジョブの作成に必要な手順については、「Creating Custom Timer Jobs in Windows SharePoint Services 3.0」を参照してください。このソリューションでは、記事にある実装の詳細情報を一部変更して、LinkedIn のステータスの更新を実行します。
SharePoint ユーザー プロファイル格納領域
このソリューションの最初の課題は、SharePoint から外部システムへの認証を実行することと、ユーザーのパスワードを要求しないで認証トークンを格納することです。ユーザー プロファイルを使用すると、各ユーザーの認証トークンを格納する作業が簡単になります。表 1 のフィールドは、ファーム管理者または FeatureActivated イベント レシーバーが設定する必要があります。トークン フィールドはユーザーには表示されません。
表 1. ファーム管理者または FeatureActivated イベント レシーバーが設定する必要があるフィールド
名前 |
表示名 |
目的 |
型 |
---|---|---|---|
LI-Token |
LinkedIn セキュリティ トークン |
アプリケーションによって要求される認証トークンを格納します。 |
String |
LI-Status |
LinkedIn のステータスの更新 |
LinkedIn のステータスを更新するために、ユーザーの設定を格納します。 |
Boolean |
トークンの管理
LinkedIn Toolkitト と DotNetOpenAuth ライブラリでは、開発者は、OAuth トークンの格納と取得を処理するトークン マネージャーを作成する必要があります。このトークンの処理は、IConsumerTokenManager インターフェイスを実装するカスタム クラスで行われます。IConsumerTokenManager インターフェイスは、OAuth 標準でのユーザーの認証に必要なトークンの格納と取得を行うために、DotNetOpenAuth ライブラリから呼び出されます。
このソリューションの SPTokenManager クラスでは、トークンをユーザー プロファイルのプロパティに格納します。
コンストラクターは、UserProfile オブジェクトをパラメーターとして取ります。
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;
}
ExpireRequestTokenAndStoreNewAccessToken メソッドは、以前に格納された要求トークンをクリアし、新しいアクセス トークンを格納します。
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);
}
StoreNewRequestToken メソッドは、認証の要求と応答を受け取り、必要なトークンの抽出と格納を行います。
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);
}
GetTokenSecret メソッドは、要求されたトークン (要求トークンまたはアクセス トークン) に対応するトークン シークレットを返します。
public string GetTokenSecret(string token)
{
//Return the token secret for the request token OR access token that
//you are given.
return GetUserProfileValue(token);
}
これらのメソッドは、ユーザー プロファイル値の読み取りおよび書き込みを行う 2 つのユーティリティ機能のどちらかを、タイマー ジョブが後から取得できる形式で呼び出します。SetUserProfile メソッドは、トークン値、トークン シークレット、およびトークンの種類のパラメーターを取得します。ユーザー プロファイル フィールドはトークンの種類に基づき更新され、値は文字列のアイテムが配列されている 4 つのオブジェクトとして格納されます。ユーザーが最初にサービスにサインアップするとき、更新が GET 要求として実行されます。アプリケーションはトランザクションの期間中に、最初に現在の Web の AllowUnsafeUpdates 属性を true に設定することにより、これを処理します。
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;
}
}
GetUserProfile メソッドはトークン値を取得し、関連するトークン シークレットを記憶領域から返します。
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;
}
}
[オプトイン] ページの作成
ユーザーがオプトイン ページにアクセスすると、現在のユーザーを表す UserProfile オブジェクトが調べられ、そのユーザーの LinkedIn の認証ステータスが判断されます。適切なトークンが見つかったら、関連する LinkedIn プロファイルが読み込まれます。トークンが見つからなかったら、[オプトイン] ボタンが有効になります。ページのコードは、LinkedIn のトランザクションの作成に必要な TokenManager オブジェクト、AccessToken オブジェクト、および Authorization オブジェクトのプロパティを実装することにより、この対話処理を実行します。TokenManager は、LinkedIn からグローバルな ApiKeys を取得し、それをカスタム アプリケーションに提供します。
TokenManager オブジェクトは、コンシューマーのキー値とカスタム UserProfile プロパティ値から作成されます。
private SPTokenManager TokenManager
{
get
{
return new SPTokenManager(GetUserProfile(), consumerKey, consumerSecret);
}
ページが読み込まれると、アプリケーションは、最初に Authorization オブジェクトの状態をテストします。アプリケーションは、TokenManager オブジェクトと AccessToken オブジェクトを使用して、新しい Authorization オブジェクトを作成します。
//Begin by testing the Authorization state for the user.
this.Authorization = new WebOAuthAuthorization(this.TokenManager, this.AccessToken);
これが、認証を初期化するために LinkedIn にアクセスして戻る途中であれば、ページは認証プロセスを完了する必要がります。
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);
}
}
アプリケーションは、プロセスを完了したら、アクセス トークンを取得して、現在のユーザー用の LinkedIn プロファイルの取得を試みることができます。
//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);
}
}
ユーザーがこの機能を一度も使ったことがない場合は、[オプトイン] ボタンは有効になっています。クリックすると、BeginAuthorize メソッドが呼びされます。LinkedIn ツールキットは API キーを取得し、それを LinkedIn に渡します。LinkedIn は、ユーザーが認証資格情報を入力できるページを返します。これはすべて LinkedIn API によって処理されます。
protected void btnOptIn_Click(object sender, EventArgs e)
{
//Initiate the authorization process.
this.Authorization.BeginAuthorize();
}
LinkedIn タイマー ジョブの更新
カスタムの LinkedIn 接続タイマー ジョブは、SPTokenManager オブジェクトを使用して、ユーザーのステータス更新情報を読み取り承認します。タイマー ジョブは、一定間隔で、Status Note フィールドが更新された UserProfile オブジェクトをクエリします。タイマー ジョブは UserProfile オブジェクトのリストを作成し、それを、更新を実行するメソッドに渡します。RetrieveUserProfileChanges メソッドでは、以下のコードで Status Note に #li 文字列が含まれているかどうかを判断し、ステータスが更新されたプロファイルのリストにプロファイルを追加します。
//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);
}
最後に、すべてのユーザー プロファイルが列挙され、UpdateLinkedIn メソッドによって、更新内容が 1 つずつ LinkedIn に送信されます。
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));
}
}
}
ユーザー ステータスの更新
ステータスの更新を LinkedIn にポストするためのユーザー エクスペリエンスは、シンプルです。ユーザーは、テキスト #li を、SharePoint のステータス テキストボックスの最後に追加します。タイマー ジョブが実行されると、更新が LinkedIn に送信されます。
サンプルのビルドと実行
以下の手順では、開発サイトあるいはテスト サイトで、このプロジェクトをテストする方法を説明します。
サンプルをビルドするには
Microsoft.SDK.Server.Samples というフォルダーを作成し、MSDN LinkedIn Code.zip ファイルをそのフォルダーに解凍します。
LinkedIn.dll ファイルと DotNetOpenAuth.dll ファイル (MSDN LinkedIn Code\Shared Libraries\ にあります) を、グローバル アセンブリ キャッシュに追加します。開発環境またはテスト環境でこれを行う方法については、「How to: Install an Assembly into the Global Assembly Cache」を参照してください。
Visual Studio 2010 を開始し、手順 1. で作成したフォルダーにある LinkedInConnection.sln ファイルを開きます。
[プロパティ] ウィンドウで、開発サイトまたはテスト サイトの絶対アドレスのサイト URL 値を指定します (例: http://mysite/)。必ず、閉じスラッシュを含めてください。また、LinkedInConnection.EventReceiver.cs ファイルで using ステートメント (using (SPSite site = new SPSite("servername"))) を使用して、同じ URL 値が使用されるように構成します。
SendColleagueURLNote.EventReceiver.cs ファイルで、SharePoint サーバーの全体管理の URL を SendColleagueURLNoteEventReceiver メソッドに指定します。
もし存在しない場合は、プロジェクトに以下のアセンブリへの参照を追加します。
Microsoft.SharePoint.dll
Microsoft.SharePoint.ApplicationPages.Administration
Microsoft.SharePoint.Security
Microsoft.Office.Server.dll
Microsoft.Office.Server.UserProfiles.dll
DotNetOpenAuth.dll
LinkedIn.dll
LinkedIn (英語) から API キーを取得します。
LinkedIn OAuth リダイレクト URL を SharePoint のページに再度設定します。たとえば、http://mysite/_layouts/msdn/lisettings.aspx のように設定します。
Elements\menuItem.xml ファイルで、UrlAction 要素の Url プロパティを、同じ SharePoint ページをポイントするように設定します (http://mysite/_layouts/msdn/lisettings.aspx)。
Globals.cs ファイルで、MSDNLI_ConsumerKey 値と MSDNLI_SecretKey 値をキーを使用して設定します。
[ビルド] メニューの [ソリューションの展開] をクリックします。ビルドが完了した後で、アプリケーション ページは開発あるいはテスト サイトにインストールされます。
サンプルを実行するには
ソリューションがビルドされ展開されたら、サーバーの全体管理サイトに移動し、LinkedIn タイマー ジョブを設定します (http://myCentralAdminSite/_admin/msdn/LinkedInTimer.aspx)。
http://mysite/_layouts/msdn/lisettings.aspx に移動し、[オプトイン] をクリックして LinkedIn アカウントにサインインし、コネクタにオプトインします。
関連項目
タスク
[方法] ユーザー プロファイル プロパティを作成および編集する
その他の技術情報
Creating Custom Timer Jobs in Windows SharePoint Services 3.0