Salesforce Visualforce + Apexで入力フォーム作成 (SalesforceからSlackへメッセージを通知する)

はじめに
===

SalesforceのVisualforceページ及びApexクラスで入力フォームを作成し、フォームから入力したメッセージをSlackへ通知するプログラムを作ってみました。

Salesforceではなるべく標準機能を活用した方が良く、むやみにVisualforceページやApexクラスは作らない方が良いとは思っておりますが、自分の単純な興味と勉強がてら、Developer Editionで試してみました。

入力フォームの挙動、実際のVisualforceとApexのソースは後述致します。

参考資料
===

Slack APIキー発行手順については、以下の記事を参考にさせて頂きました。ありがとうございます。

Slack API 推奨Tokenについて
https://qiita.com/ykhirao/items/3b19ee6a1458cfb4ba21

実行環境
===

・Salesforce Developer Edition

作成したVisualforce + Apex入力フォームについて
===

## (1) 入力フォーム概要

VisualforceとApexで以下のような簡単な入力フォームを作りました。

<img width="1194" alt="スクリーンショット 2018-12-17 5.44.22.png" src="https://qiita-image-store.s3.amazonaws.com/0/63647/f7014438-4bce-38e7-a286-782b6de0c03d.png">

## (2) 入力フォームで入力する内容

入力フォームの各テキストエリアやテキストボックスでは、以下のような内容を入力します。

　・Chatwork投稿メッセージ 及び メール本文
　・Chatwork投稿メッセージタイトル 及び メール件名 
　・メッセージ投稿先のChatworkルーム番号( https://www.chatwork.com/#!rid******** の ridより後ろの番号部分)
・Slackへメッセージを投稿する時のSlackチャネル名( メッセージ投稿先のSlackチャネル名を入力します)
　・送信先メールアドレス

入力フォームにある「Chatwork+Slack+メールでメッセージ送信」ボタンをクリックしますと、メッセージをSlackへ通知します。

本記事では例示用として、メッセージ投稿先のSlacチャネル名は「general　」として記載しています。

<img width="1209" alt="スクリーンショット 2018-12-17 14.20.18.png" src="https://qiita-image-store.s3.amazonaws.com/0/63647/5f289573-98fd-980f-fd20-5d6f9dce23ad.png">

## (3) 入力フォームから入力した内容のSlack通知

Salesforce Apexの入力フォームで「Chatwork+Slack+メールでメッセージ送信」ボタンをクリックすると、入力したメッセージがSlackチャネル「Channel」→「general」に通知されます。

<img width="1203" alt="スクリーンショット 2018-12-17 14.20.31.png" src="https://qiita-image-store.s3.amazonaws.com/0/63647/e1c3a641-3db3-11bd-8bf6-b9fbbb01834f.png">

Slack側の事前設定
===

Slackへのメッセージ通知はSlack APIを使用します。

まずは、以下の操作でSlack APIキーを発行します。

## (1) Slackアカウントを発行します。

Webブラウザから以下へアクセスし、Slackアカウントを発行します。
本記事の例では、私のアカウント名(na0aaooq)を指定してSlackアカウントを作成します。

https://slack.com/

## (2) 発行したSlackアカウントのワークスペースにサインインします。

Webブラウザから以下へアクセスし、Slackアカウントのワークスペースへサインインします。

https://自分のSlackアカウント名.slack.com/messages/C0*******/

<img width="1205" alt="スクリーンショット 2018-12-17 3.26.55.png" src="https://qiita-image-store.s3.amazonaws.com/0/63647/09616d76-0cd7-f7f1-eac1-2689630112ce.png">

## (3) Slack Apps作成画面を開きます。

Webブラウザから以下へアクセスします。

https://api.slack.com/apps

## (4) Slack Apps作成画面で「Create An App」をクリックします。

以下のようにSlack Appを作成します。

<img width="1206" alt="スクリーンショット 2018-12-17 3.28.58.png" src="https://qiita-image-store.s3.amazonaws.com/0/63647/189f4756-20ca-156a-ed3f-a75a9c1c496e.png">

| 設定 | 入力する内容例 |
|:------------:|:------------:|
| App Name | Example Salesforce Slack Info |
| Development Slack WorkSpace | na0aaooq (自分のSlackアカウントワークスペースを指定する) |

「Create App」をクリックします。

<img width="1198" alt="スクリーンショット 2018-12-17 3.36.31.png" src="https://qiita-image-store.s3.amazonaws.com/0/63647/0450b91f-53a0-3a31-22c4-a80002c1b4e2.png">

## (5) 「Slack Apps」 -> 「Settings」->「Basic Information」画面が表示されます。作成するSlack Appのアクセス可能なワークスペース等を指定します。

前述の操作により、作成した 「Slack Apps」 -> 「Settings」->「Basic Information」画面が表示されます。

Salck Appがアクセス可能なワークスペース等を指定します。

「Add features and functionality」は「Bots」をクリックします。

「Install your app to your workspace | Please add at least one feature or permission scope to install your app.」で「permission scope」をクリックします。

| 設定 | 入力する内容例 |
|:------------:|:------------:|
| Add features and functionality | Bots |
| Install your app to your workspace | Please add at least one feature or permission scope to install your app. |

<img width="1198" alt="スクリーンショット 2018-12-17 3.41.40.png" src="https://qiita-image-store.s3.amazonaws.com/0/63647/7e071ab7-7c6c-353c-8f2e-dff04508bb70.png">

<img width="1199" alt="スクリーンショット 2018-12-17 3.46.05.png" src="https://qiita-image-store.s3.amazonaws.com/0/63647/9c6a2dac-2608-bfc7-cd2e-b540b2a77e8c.png">

## (6) 「Slack Apps」 -> 「Settings」->「Features」画面が表示されます。作成するSlack Appのスコープを指定します。

 「Slack Apps」 -> 「Settings」->「Features」画面を下方向にスクロールして、「Scopes」の部分までスクロールします。

<img width="1203" alt="スクリーンショット 2018-12-17 3.49.42.png" src="https://qiita-image-store.s3.amazonaws.com/0/63647/a3966545-ca55-c4d3-4ec4-4969a9e3f04a.png">

前述の操作で作成したAppへの通知として、「Scopes」->「Select Permission Scopes」->「Send messages as Example Salesforce Slack Info」を選択します。

<img width="1207" alt="スクリーンショット 2018-12-17 3.50.50.png" src="https://qiita-image-store.s3.amazonaws.com/0/63647/3bb39e32-e2c3-c2c5-5b16-8446a9930825.png">

「Save Changes」をクリックします。

<img width="849" alt="スクリーンショット 2018-12-17 3.54.30.png" src="https://qiita-image-store.s3.amazonaws.com/0/63647/b01da039-f95a-8778-7c57-7f536c92d886.png">

## (7) 「Slack Apps」 -> 「Settings」->「Features」画面が表示されます。作成するSlack Appをインストールします。

 「Slack Apps」 -> 「Settings」->「Features」画面を上方向にスクロールして、「OAuth & Permissions」の部分までスクロールします。

「Install App to Workspace」をクリックします。

<img width="1194" alt="スクリーンショット 2018-12-17 3.58.29.png" src="https://qiita-image-store.s3.amazonaws.com/0/63647/458df7d4-d0db-2ac7-76a6-dff1d2b726c0.png">

## (8) 作成したSlack Appを「Autorize」します。

「This app was created by a member of your workspace, Slackアカウント名」と表示されるので、「Autorize」をクリックします。

<img width="1204" alt="スクリーンショット 2018-12-17 4.00.03.png" src="https://qiita-image-store.s3.amazonaws.com/0/63647/7fb71b78-bff2-cbb1-82ea-83d4424d981d.png">

Slack APIの動作確認
===

## (1) 作成したSlack App及びSlack APIキーによるSlackメッセージ通知の動作確認を行います。

 「Slack Apps」 -> 「Settings」->「Features」画面の「OAuth Tokens & Redirect URLs」を表示します。

「OAuth Tokens & Redirect URLs」の「OAuth Access Token」に、Slack App経由でSlackへメッセージを通知する為の、Slack APIトークンが表示されています。「Copy」をクリックします。(以下の例では、トークン文字列を消して掲載しています)

<img width="1203" alt="スクリーンショット 2018-12-17 4.08.18.png" src="https://qiita-image-store.s3.amazonaws.com/0/63647/6e98e77c-fab6-2eff-bc80-37965b0e56aa.png">

例えば、Slack APIのOAuthトークンが以下であるとします。

```bash
xoxp-4********8-4********0-5**********9-2******************************b
```

curlでSlack App経由でSlackへメッセージを通知できるかテストします。
curlで指定するパラメータは以下の通りです。

| 設定 | 入力する内容例 |
|:------------:|:------------:|
| Token | 作成したSlack AppのSlack APIキー(OAuthトークン) |
| channel | メッセージ通知先として指定したSlackワークスペースの通知先チャネル |
| text | Slackチャネルに通知するメッセージ内容 |

以下のようにcurlを実行します。

```
$ curl -X POST -d "token=xoxp-4********8-4********0-5**********9-2******************************b" -d "channel=general" -d "text=test-messsages" https://slack.com/api/chat.postMessage
```

レスポンスとして「ok」が返却される事を確認します。

```
$ curl -X POST -d "token=xoxp-4********8-4********0-5**********9-2******************************b" -d "channel=general" -d "text=test-messsages" https://slack.com/api/chat.postMessage

{"ok":true,"channel":"C********","ts":"1545023860.000900","message":{"type":"message","subtype":"bot_message","text":"test-messsages","ts":"1545023860.000900","username":"Example Salesforce Slack Info","bot_id":"B********3"}}
```

Slackチャネルにメッセージが通知された事を確認します。

<img width="1198" alt="スクリーンショット 2018-12-17 4.18.15.png" src="https://qiita-image-store.s3.amazonaws.com/0/63647/0eed2982-be48-54ed-15fa-e60972ccae4b.png">


Salesforce側の事前設定
===

## (1) Salesforceの「リモートサイト設定」にSlack APIエンドポイントURLを追加します。

「設定」->「管理」->「セキュリティのコントロール」->「セキュリティのコントロール」->「リモートサイトの設定」をクリックし、Slack APIエンドポイントURL( https://slack.com )を追加します。

追加する設定は以下の通りです。

| 設定 | 入力する内容例 |
|:------------:|:------------:|
| リモートサイト名 | SlackAPI |
| リモートサイトの URL | https://slack.com |
| プロトコルセキュリティの無効化 | チェックなし |
| 説明 | 任意の説明 |
| 有効 | チェック |

<img width="1191" alt="スクリーンショット 2018-12-17 5.27.47.png" src="https://qiita-image-store.s3.amazonaws.com/0/63647/71d82b59-b18d-6e41-308a-20a1ea7fcb4b.png">


「リモートサイトの設定」追加操作の流れは以下と同じです。

https://qiita.com/na0AaooQ/items/6f4a5ff6229bf040c571


## (2) Salesforceの「カスタム設定」にSlack APIキーを追加します。

「設定」->「ビルド」->「開発」 ->「カスタム設定」をクリックし、Slack APIトークン等の環境変数を追加します。

追加する設定は以下の通りです。

| 項目の表示ラベル | API参照名 | データ型 |　入力する内容例 |
|:------------:|:------------:|:------------:|:------------:|
| SlackAPIエンドポイントURL | SlackApiEndPoinUrl | URL(255) (ユニーク) | https://slack.com |
| SlackAPIエンドポイントURI(メッセージ通知) | SlackApiUriPostMessage | テキスト(255) (ユニーク、大文字と小文字を区別しない) | /api/chat.postMessage |
| SlackAPIトークン | SlackApiToken | テキスト(255) (ユニーク、大文字と小文字を区別しない) | (Slack APIキー文字列) xoxp-4********8-4********0-5**********9-2******************************b |

<img width="1201" alt="スクリーンショット 2018-12-18 13.25.06.png" src="https://qiita-image-store.s3.amazonaws.com/0/63647/2b2992eb-8e37-dd44-92b5-98a486762d57.png">



「カスタム設定」追加操作の流れは以下と同じです。

https://qiita.com/na0AaooQ/items/6f4a5ff6229bf040c571

作成したVisualforceページのソース
===

基本的な処理の流れは以下と同じです。

https://qiita.com/na0AaooQ/items/6f4a5ff6229bf040c571

## (1) 入力フォーム用のVisualforceページ

・PostMessageFormページ

```html:PostMessageForm.page
<apex:page controller="PostMessage">
    <apex:form >
    <apex:pageBlock title="PostMessageForm">
    <apex:pageMessages />

    <table class="chatwork_messages">
         <tr>
             <th>
                 Chatworkへ投稿するメッセージタイトル 及び 送信するメール件名を入力
             </th>
             <td>
                 <apex:pageBlockSection columns="1">
                     <apex:inputText id="chatwork_title" value="{!chatwork_title}" required="true" />
                 </apex:pageBlockSection>
             </td>
         </tr>

         <tr>
             <th>
                 Chatworkへ投稿するメッセージ 及び メール本文を入力
             </th>
             <td>        
                 <apex:pageBlockSection columns="1">
                     <apex:inputTextarea id="chatwork_meesage" value="{!chatwork_message}" required="true" />
                 </apex:pageBlockSection>
             </td>
         </tr>
         
        <tr>
             <th>
                 Chatworkへメッセージを投稿する時の投稿先ルーム番号を入力
             </th>
             <td>        
                 <apex:pageBlockSection columns="1">
                     <apex:inputText id="chatwork_post_room" value="{!chatwork_post_room}" required="true" />
                 </apex:pageBlockSection>
             </td>
         </tr>
         
         <tr>
             <th>
                 Slackへメッセージを投稿する時のSlackチャネル名を入力
             </th>
             <td>        
                 <apex:pageBlockSection columns="1">
                     <apex:inputText id="slack_channel" value="{!slack_channel}" required="true" />
                 </apex:pageBlockSection>
             </td>
         </tr>

        <tr>
             <th>
                 メッセージ送信先のメールアドレスを入力
             </th>
             <td>        
                 <apex:pageBlockSection columns="1">
                     <apex:inputText id="to_mail_address" value="{!to_mail_address}" required="true" />
                 </apex:pageBlockSection>
             </td>
         </tr>
         
    </table>
    
    <apex:commandButton action="{!post_message}" value=" Chatwork+Slack+メールでメッセージ送信 "/>

    </apex:pageBlock>
    </apex:form>
</apex:page>
```

作成したApexクラスのソース
===

## (1) Visualforceで作った入力フォームのコントローラクラス

・PostMessageクラス

```java:PostMessage.apex
////////////////////////////////////////
// Visualforceで入力されたメッセージ内容やタイトル等をChatworkやメール配信するApexクラス
////////////////////////////////////////
public with sharing class PostMessage {

    // Visualforceページのテキストエリアで入力されたChatworkへ投稿するメッセージ本文を取得
    public String chatwork_message { get; set; }

    // Visualforceページのテキストボックスで入力されたChatworkへ投稿するメッセージタイトルを取得
    public String chatwork_title { get; set; }
    
    // Visualforceページのテキストボックスで入力されたメッセージ投稿先のChatworkルーム番号を取得
    public String chatwork_post_room { get; set; }
    
     // Visualforceページのテキストエリアで入力されたSlackへ投稿するメッセージ本文を取得
    public String slack_message { get; set; }

    // Visualforceページのテキストボックスで入力されたメッセージ投稿先のSlackチャネル名を取得
    public String slack_channel { get; set; }

    // Visualforceページのテキストボックスで入力されたメール送信先アドレスを取得
    public String to_mail_address { get; set; }
    
    public void post_message() {
    
        PostChatworkMessage post_chatwork = new PostChatworkMessage();
        PostSlackMessage post_slack = new PostSlackMessage();
        SendMailMessage send_mail_message = new SendMailMessage();
        
        String mail_subject = chatwork_title;
        String mail_message = chatwork_message;
         String slack_message = chatwork_message;
        
        // Visualforceページで入力されたメッセージをChatworkへ投稿する
        post_chatwork.post_chatwork_message( chatwork_title, chatwork_message, chatwork_post_room );
 
         // Visualforceページで入力されたメッセージをSlackeへ投稿する
        post_slack.post_slack_message( slack_message, slack_channel );

        // Visualforceページで入力されたメッセージをメール送信する
        send_mail_message.send_mail_message( mail_subject, mail_message, to_mail_address );
        
    }
        
}
```

## (2) Slackへのメッセージ送信クラス

・PostSlackMessageクラス

```java:PostSlackMessage.apex
////////////////////////////////////////
// Slackにメッセージを投稿するApex
////////////////////////////////////////
public with sharing class PostSlackMessage {

    // カスタム設定からSlack APIトークン情報等を取得
    private static String SlackApiEndPointUrl;
    private static String SlackApiToken;
    private static String SlackApiUri;
    
    static {
        SlackApiSetting__c setting = SlackApiSetting__c.getInstance();
        
        SlackApiEndPointUrl = setting.SlackApiEndPoinUrL__c;
        SlackApiToken       = setting.SlackApiToken__c;
        SlackApiUri     = setting.SlackApiUriPostMessage__c;
        
    }

    public void post_slack_message(String slack_message, String slack_channel) {
    
        String result_message = '';
        
        Http http = new Http();
        HttpRequest request = new HttpRequest();

        request.setEndpoint(SlackApiEndPointUrl + '/' + SlackApiUri);
   
        request.setMethod('POST');
        
        request.setBody('token=' + SlackApiToken + '&' +  'channel=' + slack_channel + '&' + 'text=' + slack_message);
        
        HttpResponse response = http.send(request);
        Integer status = response.getStatusCode();
        
        if ( status == 200 ) {
            result_message = response.getBody();
        } else {
            result_message = status.format() + response.getBody();
        }
        
    }
        
}
```

***

以上になります。