Dynamics 365 for Marketing のイベント用の Web サイトをカスタマイズする ~ Angular でサクサク開発 ~

Dynamics 365 for Marketing にはイベント管理機能があります。その中には、イベントに参加しようと考えている人（Dynamics のユーザーではなく、一般の人）がアクセスし、イベントのセッション情報を閲覧したりイベントに参加登録したりするための Web サイトを管理する機能が含まれます。  
その Web サイトのコンテンツは標準で提供されるものがあり、Dynamics 365 ポータル でホストしたり、外部サイトでホストすることができます。  
本記事では、その標準で提供されるイベント用の Web サイトのコンテンツをカスタマイズする方法を紹介します。

## 標準で提供されるイベント用の Web サイトの機能
以下のような機能が標準で提供されます。（引用元： https://docs.microsoft.com/ja-jp/dynamics365/customer-engagement/marketing/set-up-event-portal ）
![イベント用の Web サイト](https://docs.microsoft.com/ja-jp/dynamics365/customer-engagement/marketing/media/event-portal-sitemap-ill.png)  

## Angular フレームワークで作られている
イベント用の Web サイトは、Angular フレームワーク上に構築されています。サンプルのソースコードを入手してカスタマイズすることで独自のフロントエンドを構築することができます。

## 本記事で想定する環境
本記事で想定する環境として、ホストする環境を自分で用意した Azure Storage とします。

- [x] 自己ホストで Azure Storage を利用
- [ ] Dynamics 365 ポータル

公式ドキュメントには、 Dynamics 365 ポータルでホストする場合についての解説もあります。

## 開発環境の構築
### i. サンプル ソースコードの入手
以下のページから、サンプルのイベント Web サイトのソースコードを入手します。（私は「1.15.0 (6 月リリース)」を利用しました。）
[サンプル イベント Web サイトのダウンロード](https://docs.microsoft.com/ja-jp/dynamics365/customer-engagement/marketing/developer/event-management-web-application#download-sample-event-website)

### ii. Node.js バージョン 10.x 以上をインストール
開発環境に Node.js バージョン 10.x 以上をインストールします。以下のコマンドでバージョンを確認します。

```console
node -v 
```

### iii. Angular CLI をインストール
以下のコマンドでインストールします。

```console
npm install -g @angular/cli
```

### iv. 必要なパッケージをすべて取得
入手したソースコード(.zip)を展開して、**package.json** ファイルがあるのと同じフォルダ(例えば "2019-06-v2-demo-event-website")にて以下を実行します。

```console
npm install
```

なお、公式ドキュメントに記載はありませんでしたが、私の環境では npm パッケージ **core-js** のインストールが別途必要でした。これがないと後述するビルドでエラーが発生して進めませんでした。以下のコマンドでインストールしました。

```console
npm install -S core-js
```

### v. Azure Storage アカウントを作成
新規 Azure Storage アカウントを作成します。そして、「静的な Web サイト」機能を有効にし、インデックス ドキュメント名およびエラー ドキュメントのパスに **index.html** を入力し、保存します。プライマリ エンドポイントの URL をコピーしておきます。これは、カスタム イベント Web サイトの URL です。
![静的な Web サイト](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/455051/6ed5323c-799f-e281-18c5-912f23a6fe72.png)

### vi. Web アプリケーションの登録
Dynamics 365 for Marketing アプリにて、**Web アプリケーション** レコードを新規作成します。これは、Dynamics 365 for Marketing が持つイベント API を利用するためのものです。
1. [設定] -> [詳細設定] -> [イベント管理] -> [Web アプリケーション] に移動します。
2. [新規] をクリックして、発生元に前述のプライマリ エンドポイントの URL を貼り付け、保存します。
![Web アプリケーション](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/455051/3eb1f373-acbf-ba28-8b7e-1a2f3574b177.png)
3. ここでさらに、開発時にも動作するように、`http://localhost:4200` 用にも Web アプリケーション レコードを作成しておくとよいです。
![Web アプリケーション localhost用](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/455051/00d8c324-20aa-4f6b-be19-3da084065697.png)
4. 上記 2. においても、 3. においても、作成した Web アプリケーション レコードには、トークンとエンドポイントが生成されます。後に使いますので、それらの各々区別して文字列をコピーしておきます。
![トークンとエンドポイント](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/455051/193b4a22-b8cc-a20f-2dac-27e3e0833b18.png)
5. 少し補足的な内容ですが、上記 4. で、私はトークンの文字列をコピーする方法にちょっと苦労しました。トークンの文字列は長く、文字列全体を表示させてコピーするということができませんでした。そこで、ブラウザの デベロッパー ツール （F12機能とも呼ぶ？）を呼び出して、要素を選ぶ機能を使ってトークンの値が表示されているところをクリックして、HTML をコピーしてエディタに貼り付けてから、トークンの値であろう文字列をコピーしました。（もう少しいい方法ないでしょうか？）
![デベロッパー ツール](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/455051/afa1146c-75de-f515-da72-3fb24c50cd4f.png)

### vii. 環境用 ts ファイルを編集
再度開発環境に戻ります。本記事では自己ホスト（で Azure Storage）を使用します。それに合わせて ts ファイルを編集します。  
ディレクトリ `src/environments/` に移動し、`environment.selfhosted.ts` ファイルを編集します。  
まず、**apiEndpoint** エントリです。先の作業でコピーしたエンドポイントを指定します。"**EvtMgmt/api/v2.0/**" で終わるように編集します。

```typescript:environment.selfhosted.ts
  /**
   * The URL that points to the Event Management API endpoint (which is used to retrieve information of events and to make registrations).
   * If you're using the deprecated Event Management Portal API then you need to enter the URL of your Portals.
   * If you're using the new Event Management Public API then you need to replace HOST with the value from the `Endpoint` field that you
   * get after registering your web application. Additionally you need to append 'EvtMgmt/api/v2.0/'.
   * Note: The URL **must** have a trailing slash.
   **/
  apiEndpoint: 'https://＜＜ご自身のをここに＞＞.svc.dynamics.com/EvtMgmt/api/v2.0/',
```

次に、**emApplicationtoken** エントリです。先の作業で苦労してコピーしたトークンを指定します。

```typescript:environment.selfhosted.ts
  /**
   * This authenticates your web application against our Event Management Public API.
   * You can retrieve the application token by registering a new web application in CRM.
   * Note: This token is not required if you're using the deprecated Event Management Portal API.
   */
  emApplicationtoken: 'C3Y3qOq74rh_＜＜ご自身のに合わせてください＞＞＞',
```

最後に、今編集した `environment.selfhosted.ts` ファイルの内容をすべてコピーして、同じフォルダにある `environment.ts` （空っぽのはず）に貼り付けて保存します。ビルド時には、`environment.ts` の内容のみが参照されて、`environment.selfhosted.ts` ファイルはメモ的な位置づけのもののようです。

### viii. Angular アプリをローカルで動かす
さて、この時点で開発環境ローカルでサンプル Web サイトが、Dynamics 365 for Marketing に格納されているイベント情報を動的に取得してきてページに表示されるはずです。（Dynamics 365 for Marketing でイベント関連のデータを予め登録しておく必要があります。）  
以下のコマンドを実行します。

```console
ng serve
```

ブラウザで `http://localhost:4200` にアクセスします。イベントデータが表示されることを確認します。  
※以下はあくまでもサンプルのデータです。 
![localhost でのイベント サイト](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/455051/cb7c1015-652d-952b-6036-452ec8c82746.png)
開発環境の構築が完了しました。

## カスタマイズ
さて、Angular フレームワークに慣れた方であれば、存分にカスタマイズして頂けます。私は不慣れなので少しだけやってみます。  
`src/app/services/labels.service.ts > LabelsService` のカスタマイズ。後に表示ラベルを日本語に適したものに変更するので、その前準備です。

```typescript:labels.service.ts
    // private readonly DefaultBrowserLcid = 1033;
    private readonly DefaultBrowserLcid = 1041;
    // private readonly DefaultUserLanguage = 'en-US';
    private readonly DefaultUserLanguage = 'ja-JP';
```

次に、`Localization/1041.json` に、**AvailableEvents** のエントリを追加します。

```json:1041.json
"AvailableEvents":  "利用可能なイベント",
```

※私はシンプルにするために、`Localization` フォルダのたくさんあるファイルのうち、1033.json （英語用）と1041.json （日本語用）のファイル以外は削除しました。  
次に、日本っぽい日付表示とするために、`src/app/components/home/home.component.html` の以下の部分を修正します。

```html:home.component.html
    <ng-template #showStartAndEndDdate>
        <!-- {{ event.startDate | date:'mediumDate' }} - {{ event.endDate | date:'mediumDate' }} -->
        {{ event.startDate | date:'yyyy年MM月dd日' }} - {{ event.endDate | date:'MM月dd日' }}
    </ng-template>
```

これまでのカスタマイズの結果、以下のように表示が修正されます。
![カスタマイズ後の localhost ページ](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/455051/9ca48982-aedd-24df-7052-63613800a184.png)
このようにカスタマイズを完了させます。（実際にはもっとたくさんのページが存在し、カスタマイズする領域は多岐にわたります。）

## 本番用のビルドとデプロイ
### i. ビルド
カスタマイズが終了したら、本番用にビルドするため、以下のコマンドを実行します。

```console
ng build --prod --output-hashing none --configuration self-hosted
```

なお、公式なドキュメントには最後の文字列が **selfhosted** と記載されているのですが、私の環境ではうまくいかなかったので、 **self-hosted** としたらうまくいきました。

### ii. Azure Storage にデプロイ
先の作業で「静的な Web サイト」機能を有効にした Azure Storage にデプロイします。私は Visual Studio Code の Azure Storage 拡張機能を使って、[静的 Web サイトにデプロイする機能](https://code.visualstudio.com/tutorials/static-website/deploy-website) を利用してデプロイしました。Azure Storage Explorer を使ってもよいでしょう。  
なお、デプロイすべきファイル群が格納されているフォルダはフォルダは `dist/self-hosted/` です。
![dist_self-hosted.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/455051/15fdd4c7-a4cf-34fd-4d34-21ce9e99ed2c.png)

### iii. 確認
デプロイした本番環境にアクセスして、カスタマイズした内容を確認します。
![カスタマイズ後の本番環境](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/455051/7ef36ed0-a9ef-9fec-6533-95c2f706758e.png)
確認できました。一連の手順は以上です。

## Dynamics 365 ポータルでホストする場合
本記事では Azure Storage でホストしましたが、Dynamics 365 ポータルでホストする場合には、以下のページをご参照ください。
[Dynamics 36 ポータル ホスト](https://docs.microsoft.com/ja-jp/dynamics365/customer-engagement/marketing/developer/portal-hosted)

## 参考ページ
- [イベント Web サイトをセットアップする](https://docs.microsoft.com/ja-jp/dynamics365/customer-engagement/marketing/set-up-event-portal)
- [カスタム イベント Web サイトの作成](https://docs.microsoft.com/ja-jp/dynamics365/customer-engagement/marketing/developer/event-management-web-application)