Pleasanter Tips: メール送信をする機能を作ってみた

# コトの発端（情報漏洩の発覚）
（中身は無いので暇なら読んでください。）
とある日、Pleasanterに登録しているユーザーに情報に送りたいと思い掲示板作成しました。そのあと登録した情報をメールで送ろう！と思い付き、通知で送れば良いよね・・・登録完了。。。  
仕事でメールを確認したら通知で送ったメールが届いておりフト見ると・・・  
あれ？TOに全メールが入っている～～～～ま・ず・い  
ここから謝罪メールの作成などなど対応に追われ、ショックで落ち込んでいました。  
そもそも通知の仕様を調べて見ると、「複数の固定アドレスを指定した場合には、全ての固定アドレスをTOとして送信します」と。そうだったのか。  
PleasanterでDMで使用できる方法が無いかと調べましたが見つからず。  
私の希望する機能が無いので作る事にしました。

# 機能
0. 掲示板を登録するとユーザーにメールを送る。
0. ユーザーはPleasanterのユーザーとする。
0. 送信先は指定した組織に所属するユーザーとする。
0. 各メールの送り先はTOに設定し、1送り先1メールとする。

※ユーザーは200名までしか対応していません。200名以上の場合はユーザー取得方法を修正する必要があります。

## 参考画面
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63349/dce7273a-3e0c-2d8b-c054-b8ff427ac46a.png)

## 参考動画
https://youtu.be/cvg6PWH8VY4

# 設定
## 状況の設定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63349/069a1bb5-ba9f-fadd-e678-c7c3e7db3ece.png)
読取専用  
既定値:100
選択肢一覧  
```
100,未送信,未,status-new
200,送信可,可,status-inprogress
900,送信済,済,status-closed
```

## プロセス
送信スクリプトでは状態（Status）を判断して動作を行います。  
プロセスで処理ボタンを作成します。  
メール未送信時のボタン
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63349/5742321d-9840-2eed-d40f-e7f48d524480.png)
メール送信済時のボタン
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63349/1b3000f4-4dea-8d4a-4875-51183e248c89.png)
各ボタンで状況の変更を行います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63349/3a7420b0-0053-8be2-630e-560c3ba7d3d1.png)
### メール送信プロセス
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63349/8eb6b63e-8034-072f-2822-4ecda513edb8.png)
### メール送信もどしプロセス
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63349/90088bbb-4827-c6a7-4e8e-176621e830b8.png)

## サーバスクリプト
本機能はサーバスクリプトで更新前に処理します。更新前に起動する理由としては、処理簿に状態（Status）を変更する為です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63349/5d26ed15-88b4-58e3-131a-65fe5e045a04.png)
|設定項目|設定値|
|-|-|
|ApiKey | PleasanterのAPI設定で作成したAPIキーを設定します。|
|UrlBase | APIを実行する際の/api/より前の部分を設定します。|
|FromUser |メールの送信者を指定します。|
|MailFrom | 送信者名およびメールアドレスを設定します。この設定はFromUserが0及び担当者・管理者の場合でもメールアドレスが設定されていない場合に使用します。|
|MailHeader|メールの上部に記述する内容です。|
|MailFooter|メールの下部に記述する内容です。|
|UsersFilterHash|ユーザーを検索する際のフィルター設定です。ViewのColumnFilterHashに指定する内容です。{"DeptId":1,"Disabled":false}の場合は組織IDが1で有効のみの指定方法です。|
|Send|falseを指定するとメールは送信されません。テスト用です。|

```
let ApiKey = "d10d9.....f5c30"; //APIKeyをセット
let UrlBase = "http://localhost";
let FromUser = 1; //0:MailFromを使用 1:担当者を使用 2:管理者を使用
let MailFrom = `管理者 <info@hoge.com>`;  //送信者の初期値
let HeaderUse = 0; //0:MailHeaderを使用 1:編集画面のガイドを使用
let MailHeader = "各位\n"
+"（メッセージヘッダー）\n\n"
let MailFooter = "--------------------------------------------------\n"
+"（メッセージフッター）\n"
let UsersFilterHash = {"DeptId":1,"Disabled":false};
let Send = true;

//サイト情報取得
if( HeaderUse==1 ){
  httpClient.RequestUri = `${UrlBase}/api/items/${model.SiteId}/getsite`;
  httpClient.Content = JSON.stringify({"ApiVersion": 1.0, "ApiKey": ApiKey});
  let site = httpClient.Post();
  site = (JSON.parse( site.toString())).Response.Data;
  MailHeader = `${site.EditorGuide}\n`;
}
//担当者取得
if( FromUser==1 || FromUser==2 ){
  let FromUID = ( FromUser == 1 ? model.Owner : model.Manager );
  httpClient.RequestUri = `${UrlBase}/api/users/${FromUID}/get`;
  httpClient.Content = JSON.stringify({
    "ApiVersion": 1.0,
    "ApiKey": ApiKey,
    "View": {
      "ApiDataType":"KeyValues",
      "ApiColumnKeyDisplayType":"ColumnName",
      "GridColumns":["UserId","DeptId","Name","MailAddresses"],
      "ApiGetMailAddresses": true
    }
  });
  let owner = httpClient.Post();
  owner = (JSON.parse(owner.toString())).response.Data;
  if( owner.length == 1 ){
    if( owner[0].MailAddresses.length > 0 ){
      MailFrom = `${owner[0].Name} <${owner[0].MailAddresses[0]}>`;
    }
  }
}

try{
  let Id;
  model.ResultId == undefined ? Id = model.IssueId : Id = model.ResultId;
  context.Log(`ID:${Id}`);
  context.Log(`Status:${model.Status}`);
  if( model.Status == 200 ){
    MailSend( Id );
    model.Status = 900;
  }
} catch(e) {
  context.Log(e.stack);
}

function MailSend( ItemId ){
  //送信先リスト作成
  httpClient.RequestUri = `${UrlBase}/api/users/get`;
  httpClient.Content = JSON.stringify({
    "ApiVersion": 1.0,
    "ApiKey": ApiKey,
    "View": {
      "ColumnFilterHash":UsersFilterHash ,
      "ApiDataType":"KeyValues",
      "ApiColumnKeyDisplayType":"ColumnName",
      "GridColumns":["UserId","DeptId","Name","MailAddresses"],
      "ApiGetMailAddresses": true
    }
  });
  let user = httpClient.Post();
  let users = (JSON.parse(user.toString())).response.Data;
  if( users.length > 0 ){
    //メール送信
    for( let c=0; c<users.length; c++ ){
      if( users[c].MailAddresses.length > 0 ){
        for( let d=0; d<users[c].MailAddresses.length; d++ ){
          context.Log(`${users[c].Name}/${users[c].MailAddresses[d]}`);
          if(Send) MailSendTo( ItemId, users[c].Name, users[c].MailAddresses[d] );
        }
      }
    }
  }
}

function MailSendTo( ItemId, Name, Address ){
  httpClient.RequestUri = `${UrlBase}/api/items/${ItemId}/OutgoingMails/Send`;
  httpClient.Content = JSON.stringify({
    "ApiVersion": 1.0,
    "ApiKey": ApiKey,
    "From": MailFrom,
    "To": `${Name} <${Address}>`,
    "Title": model.Title,
    "Body": `${MailHeader}\n${model.Body}\n${MailFooter}`
  });
  let result = httpClient.Post();
  context.Log( result );
}
```
基本的な設定は以上です。

## 送信メールを隠す
### データを消去する（SQL-Server用）
ExtendedSqlsでメールを消去（_deletedに移動）
更新時に自動的にメールを移動します。
※SiteIdListは使用環境に合わせて変更してください。  
RmMailLog_MSSQL.json
```
{
    "Name": "RmMailLog_MSSQL",
    "SiteIdList": [99999],
    "OnUpdated": true
}
```
RmMailLog_MSSQL.json.sql
```
BEGIN
INSERT INTO [OutgoingMails_deleted]
SELECT * FROM [OutgoingMails] WHERE [ReferenceId]={{Id}};
DELETE FROM [OutgoingMails] WHERE [ReferenceId]={{Id}};
END;
```
### データを消去する（PostgreSQL用）
RmMailLog_PGSQL.json
```
{
    "Name": "RmMailLog_PGSQL",
    "SiteIdList": [99999],
    "OnUpdated": true
}
```
RmMailLog_PGSQL.json.sql
```
INSERT INTO "Implem.Pleasanter"."OutgoingMails_deleted" 
SELECT * FROM "Implem.Pleasanter"."OutgoingMails" WHERE "ReferenceId"={{Id}};
DELETE FROM "Implem.Pleasanter"."OutgoingMails" WHERE "ReferenceId"={{Id}};
```
### 表示しない方法
スクリプトで設定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63349/ebd67894-6f5c-5246-e180-e623148b7c96.png)
```
$p.events.on_editor_load = function () {
  $('form#OutgoingMailsForm').remove();
}
```

# 最後に
本スクリプトにより、個別にメールを送る事ができるようになりました。  
今回はPleasanterのユーザーにメールしましたが、メール送り先のテーブルを使用する方法も有ります。SFAなどではその方が使い勝手が良いですね。  
また、内容はテキストのみでMarkdownに対応できないかとも思っています。  
時間を見て検討します。

# おまけ
今回作成したスクリプトをPleasanterパッケージで無料で配布しています。  
作成が面倒だな～と思ったり、スクリプトはちょっと苦手だと思うからはPleasanterフリーサポートからダウンロードできるのでご活用ください。

https://iwate-cloud.com/support-site/