Dynamics CRM では、 CRMインストールフォルダのISV フォルダに、カスタムページを作成することができます。ISVフォルダにアプリケーション用のフォルダを作成し、そのフォルダ配下にASPXのWebページを配置します。

今回のアプリでは、AD用のUrlとIFD用のUrlのどちらでもうまく動作するようにサイトマップに登録します。また、Rollup2 以降でサポートされる方法でdllファイルをデプロイします。つまり、ISV/アプリケーションフォルダ/bin フォルダにdllをデプロイします。

動作確認

  • Windows 2003 Enterprise Edition全部入り完了。 Dynamics CRM 4.0 には Rollup 5 が適用されています。
  • 開発環境:Visual Studio 2008 Professional
  • .NET 3.5
  • クライアント:IE6, IE7

1. Crm SDK アセンブリをパブリックフォルダに登録する

今後の開発作業を容易にするために、SDK に付属するアセンブリ microsoft.crm.sdk.dll と microsoft.crm.sdktypeproxy.dll をVisual Studio 2008 のパブリックアセンブリフォルダに格納します。今回の作業は必須ではないですが、このフォルダにdllを保存すると、Visual Studio のソリューションで参照の追加時に参照の追加ダイアログの.NET のタブにアセンブリが表示されるようになります。

パブリックフォルダは、Visual Studio 2008 を標準のパスにインストールした場合、以下のパスになります。このパスに、CrmのSDKに付属する、microsoft.crm.sdk.dll と microsoft.crm.sdktypeproxy.dll をコピーします。

C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\PublicAssemblies

パブリックアセンブリフォルダにdllをコピーすると、参照の追加時に、.NETタブにdllが表示されるようになります(下図参照)。

2. 今回作成するアプリ

今回はログインしているユーザの、crm上の情報と、ad上の情報を比較して表示するカスタムページを作成します。このページは、IFD,AD認証した場合に動作するようにします。

下図が完成図です。以下はAD認証により、ログインしたユーザの情報をカスタムアプリケーションで表示した図です。

以下は、IFD認証でログインしたユーザの情報をカスタムアプリケーションで表示した図です。

3. カスタムWebアプリケーションの作成

3.1 プロジェクトの作成

Visual Studio を起動して、webアプリケーションプロジェクトを作成します。今回は、WhoAmIという名前で作成しました。既定で作成されるWeb.config の認証 <authentication mode="Windows" /> は、使用しないので、コメントにしておいてください。

3.2 dll参照の追加

参照の追加で、microsoft.crm.sdk.dll と microsoft.crm.sdktypeproxy.dll を追加します。Visual Studio のパブリックアセンブリフォルダにdllを格納していない場合は、参照の追加ダイアログの参照タブで直接dllを指定します。 今回のアプリはActive Directory に接続するので、System.DirectoryServices.AccountManagement の参照も追加します。

3.3 ASPXの作成

webアプリケーションを作成したときに既定で作成されるDefault.aspx を次のように編集します。ASPXで特に注意していただきたいのが、ASPXの先頭に@Assembly ディレクティブを使用していることです。コードビハインドが格納されるdll名を指定します。この記述を含めることで、Rollup 2 以降でサポートされるようになった、ISV配下のbinフォルダに各アプリケーション用のアセンブリを格納することが可能になります。Rollup2より前は、CrmのインストールルートのbinフォルダがGACにしか、拡張アプリケーションのdllを格納する方法がありませんでした。

dllをCRMのインストールルートのbinフォルダやGACにデプロイする場合は、@Assemblyディレクティブは必要ないので注意して下さい。

<%@ Assembly Name="WhoAmI" %>
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="WhoAmI._Default" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>無題のページ</title>
    <link href="style.css" rel="stylesheet" type="text/css" />
</head>
<body>
    <form id="form1" runat="server">
    <div>
        
        <table style="width:100%;border-width:2px;" cellspacing="2px" cellpadding="2px">
            <tr class="header">
                <td>
                    &nbsp;</td>
                <td>
                    CRM情報</td>
                <td>
                    AD情報</td>
            </tr>
            <tr>
                <td>
                    せい</td>
                <td>
                    <asp:Literal ID="lblCrmLast" runat="server"></asp:Literal></td>
                <td>
                    <asp:Literal ID="lblADLast" runat="server"></asp:Literal></td>
            </tr>
            <tr>
                <td>
                    めい</td>
                <td>
                    <asp:Literal ID="lblCrmFirst" runat="server"></asp:Literal></td>
                <td>
                    <asp:Literal ID="lblADFirst" runat="server"></asp:Literal></td>
            </tr>
        </table>
        
    </div>
    </form>
</body>
</html>

コードビハインドファイルは次のようになります。CrmService を作成して、Crmの情報を取得します。また、System.DirectoryServices.AccountManagement 名前空間のクラスを使用してログインしているユーザのAD上の情報を取得しています。

using System;
using System.Collections;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Linq;
using Microsoft.Crm.SdkTypeProxy;
using Microsoft.Crm.Sdk;
using Microsoft.Crm.Sdk.Query;
using System.Net;
using System.DirectoryServices.AccountManagement;

namespace WhoAmI
{
    public partial class _Default : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            WhoAmI();
        }
        private void WhoAmI()
        {
            using (new CrmImpersonator())
            {
                CrmService service = GetCrmService(Request.QueryString["orgname"], "localhost");

                string samaccount = SetCrmInfo(service);
                SetADInfo(samaccount);
                
            }
        }
        private string SetCrmInfo(CrmService service)
        {
            ColumnSet cols = new ColumnSet();

            cols.Attributes.Add("domainname");
            cols.Attributes.Add("firstname");
            cols.Attributes.Add("lastname");

            Guid systemuserGuid = service.CrmAuthenticationTokenValue.CallerId;

            systemuser su = (systemuser)service.Retrieve(EntityName.systemuser.ToString(), systemuserGuid, cols);

            lblCrmFirst.Text = su.firstname;
            lblCrmLast.Text = su.lastname;

            int pos = su.domainname.IndexOf('\\');
            
            return su.domainname.Substring(pos + 1);
        }
        private void SetADInfo(string samAccount)
        {
            PrincipalContext ctxt = new PrincipalContext(ContextType.Domain);
            UserPrincipal user = UserPrincipal.FindByIdentity(ctxt, IdentityType.SamAccountName, samAccount);

            lblADLast.Text = user.Surname;
            lblADFirst.Text = user.GivenName;
        }
        /// <summary>
        /// ユーザがIFD認証で認証されても動作するように作成しています。
        /// ただし、CrmServiceの認証はAD認証で行われる前提です。
        /// CrmService を必ずAD認証とするには、crmサーバをlocalhost
        /// で参照します。
        /// </summary>
        /// <param name="orgName"></param>
        /// <param name="host"></param>
        /// <returns></returns>
        private CrmService GetCrmService(string orgName, string server)
        {
            UriBuilder builder = new UriBuilder("http", server);
            builder.Path = "/mscrmservices/2007/crmservice.asmx";

            if (string.IsNullOrEmpty(orgName))
            {
                // AD認証 URL
                if (Request.Url.Segments[2].TrimEnd('/').ToLower() == "isv")
                {
                    orgName = Request.Url.Segments[1].TrimEnd('/').ToLower();
                }

                // IFD認証 URL
                if (string.IsNullOrEmpty(orgName))
                {
                    string url = Request.Url.ToString().ToLower();
                    int start = url.IndexOf("://") + 3;
                    orgName = url.Substring(start, url.IndexOf(".") - start);
                }

            }
            CrmAuthenticationToken token = null;
            token = CrmAuthenticationToken.ExtractCrmAuthenticationToken(Context, orgName);
            token.OrganizationName = orgName;
            token.AuthenticationType = 0; // 0:AD, 1:IFD

            // Crmサービス作成
            CrmService service = new CrmService();
            service.Credentials = System.Net.CredentialCache.DefaultCredentials;
            service.CrmAuthenticationTokenValue = token;
            service.Url = builder.ToString();
            return service;

        }
    }
}

3.4 スタイルシートの作成

Webアプリケーション用のスタイルシートを作成します。ソリューションエクスプローラ上のWebアプリケーションプロジェクトを右クリックし、追加→新しい項目 を選択して、スタイルシートを追加します。ファイル名は、style.css とします。

以下が、style.css の内容です。

body
{
	background-color:#EAF3FF;
}

table
{
	background-color:Silver;
}
table .header
{
	background-color:Gray;
}

3.5 アプリケーションのデプロイ

プログラムをビルドして、エラーがないことを確認したら、アプリケーションをデプロイします。プロジェクトを右クリックし、発行を選択します。

発行先をCRMがインストールされたフォルダ配下の/ISV/WhoAmI フォルダとします。発行が成功すると、WhoAmIフォルダ配下に、aspx とcss ファイル、binフォルダにWhoAmI.dll が作成されます。デプロイが失敗する場合は、iisreset コマンドでIISを再起動してから行ってください。

準備がととのったので、以降で、Dynamics CRM からサイトマップをエクスポートしてカスタムアプリケーションに遷移できるようにします。

4. Dynamics CRM にカスタムアプリケーションへのメニュー追加

今回は、メニューのワークプレースに"私の情報"というメニュー項目を追加します。

4.1 サイトマップのエクスポート

システム管理者やシステムカスタマイザ権限をもつユーザでCRMにログインします。左メニューの設定を選択後、メニューからカスタマイズを選択します。右ペインのカスタマイズのエクスポートを選択します。

カスタマイズのエクスポート画面のビューでクライアント拡張を選択します。リストにサイトマップが表示されるので、サイトマップを選択し、リスト上部のツールバーから選択したカスタマイズのエクスポートをクリックします。エクスポートしたファイルを保存します。

4.2 サイトマップの編集

エクスポートしたzipファイルを展開します。展開されたcustomizations.xml を編集します。Id がMyWork となっているGroup タグがあるので、末尾に、SubAreaタグを挿入します。SubAreaタグにメニューに表示するタイトルと、拡張ページのURLを設定しています。各属性、要素の意味は、SDKを参照してください。編集したファイルはUTF-8 形式で保存してください。

<Group Id="MyWork" ResourceId="Group_MyWork" DescriptionResourceId="My_Work_Description">
<!-- 省略 -->
     <!-- 以下を追加 -->
      <SubArea Id="nav_whoami" Url="../../ISV/WhoAmI/Default.aspx" PassParams="1" >
         <Titles>
             <Title LCID="1041" Title="私の情報" />
         </Titles>
      </SubArea>
</Group>

4.3 サイトマップのインポート

エクスポート時と同様に、システム管理者またはシステムカスタマイザ権限を持つユーザでDynamics にログインします。左メニューの設定→カスタマイズを選択します。右ペインのカスタマイズのインポートを選択します。

カスタマイズのインポート画面が表示されるので、参照ボタンをクリックして、編集したcustomizations.xmlを選択します。アップロードボタンをクリックします。xmlファイルに問題がなければ、リストにサイトマップが表示されます。リストからサイトマップを選択して、リスト上部の選択したカスタマイズのインポートをクリックして、サイトマップをインポートします。

 

5. 動作確認

ブラウザを一度閉じ、再度、CRMにログインします。インポートが成功していれば、左ペインのワークプレースメニューに私の情報メニュー項目が表示されます。私の情報をクリックすると、カスタムWebアプリケーションに問題がなければ、2. 今回作成するアプリで掲載したような結果が、右ペインに表示されます。

6. まとめ

今回の説明は以上です。問題点、疑問点などありましたらご連絡ください。

今回の拡張アプリケーションと本質的には関係ないのですが、AD認証の場合、URLの先頭に組織名が付与されて、Web.configやスタイルシートがうまく読み込めないという現象があります。今回のサンプルのように../../という相対パスを拡張アプリケーションに付与するとうまく読み込めるようになります。少なくともIE6,7ではですが。

動作確認は、IE6とIE7で行っています。