はじめに
ApexでChatterを送信する時に使用するConnectApiには大量のクラスがあります。
全てのドキュメントに目を通すのはかなり辛いので、Chatterを投稿するために最低限必要な部分だけまとめました。
※2022/8/29追記:FeedItemオブジェクトを使って投稿する
テキストのみを投稿する
「とりあえずテキストを投稿できればいい!急いでるんだ!」というせっかちな方向けのシンプルな例です。
String subjectId = '0015h00000GHHciAAH';
String text = 'メッセージ\nメッセージ';
ConnectApi.FeedElement feedElement = ConnectApi.ChatterFeeds.postFeedElement(Network.getNetworkId(), subjectId, ConnectApi.FeedElementType.FeedItem, text);
今回はsubjectIdに取引先IDを指定したため、取引先レコードに対してChatterが投稿されました。
こちらのメソッドを用いた上記の例がChatter投稿の最小構成となります。
postFeedElement(communityId, subjectId, feedElementType, text)
- communityId :
Experience CloudサイトID | internal | null
→ 例のようにNetwork.getNetworkId()
を指定しておけば環境に合わせてIDかnull(非コミュニティ時)が自動で設定されるので、特に指定する必要がなければNetwork.getNetworkId()
でOK - subjectId : 投稿先のID →
ユーザID | ChatterグループID | レコードID
- feedElementType : ConnectApi.FeedElementType列挙型 →
FeedItem
のみ使用可 - text : 投稿するテキスト →
\n
で改行可能(最大10,000文字)
リッチテキストを投稿する
上記の例でも最低限の投稿は可能ですが、メンションやリンク等を用いた投稿は行えません。
これらを実現するためにはConnectApi.FeedItemInputクラスを用いた投稿を行う必要があります。
ConnectApi.FeedItemInput
を用いた、最初の例と同じ投稿を行うコードが以下です。
String subjectId = '0015h00000GHHciAAH';
String text = 'メッセージ\nメッセージ';
ConnectApi.FeedItemInput input = new ConnectApi.FeedItemInput();
input.subjectId = subjectId;
input.feedElementType = ConnectApi.FeedElementType.FeedItem;
ConnectApi.MessageBodyInput body = new ConnectApi.MessageBodyInput();
body.messageSegments = new List<ConnectApi.MessageSegmentInput>();
input.body = body;
ConnectApi.TextSegmentInput textSegment = new ConnectApi.TextSegmentInput();
textSegment.text = text;
body.messageSegments.add(textSegment);
ConnectApi.FeedElement feedElement = ConnectApi.ChatterFeeds.postFeedElement(Network.getNetworkId(), input);
最初の例では1行で実装できていた部分が10行になった上、見慣れないクラスが突如複数出現しました。
一見理解が難しそうですが、feedElement
クラスの構造さえ掴めれば勝ったも同然です。
順を追って解説していきます。
postFeedElement(communityId, feedElement)
順を追って、と言いつついきなり最終行に飛びます。
feedElement
はConnectApi.FeedElementInputクラス型で、フィード要素(Chatter投稿に必要な情報の集合)のコンテナです。
postFeedElement()
を実行するため、フィード要素を適切に設定して引数に渡すことがゴールになります。
関連クラスの関係図
- FeedElementInput : 抽象クラスのため直接インスタンスを生成することはできない。ドキュメント上はこのクラスが引数の型として記載されている。
-
FeedItemInput : FeedElementInputのサブクラスで実際にインスタンス化するのはこのクラス(例4行目)。FeedElementInputのプロパティである
subjectId / feedElementType
はインスタンス化した後で直接設定する。 -
MessageBodyInput :
FeedItemInput.body
の型で、messageSegments
プロパティに本文を設定する。 -
MessageSegmentInput :
MessageBodyInput.messageSegments
がこのクラスのリスト型。計8つのサブクラスを組み合わせて本文を生成する。
まとめると以下の流れが基本形となります。
-
FeedElementInput
を除く3つのクラスを使ってインスタンスを生成 -
MessageBodyInput.messageSegments
に本文を追加 -
postFeedElement()
の引数にFeedItemInput
のインスタンスを渡して実行
String subjectId = '0015h00000GHHciAAH';
ConnectApi.FeedItemInput input = new ConnectApi.FeedItemInput();
input.subjectId = subjectId;
input.feedElementType = ConnectApi.FeedElementType.FeedItem;
ConnectApi.MessageBodyInput body = new ConnectApi.MessageBodyInput();
body.messageSegments = new List<ConnectApi.MessageSegmentInput>();
input.body = body;
/**
本文を生成
*/
ConnectApi.FeedElement feedElement = ConnectApi.ChatterFeeds.postFeedElement(Network.getNetworkId(), input);
ConnectApi.MessageSegmentInput
のサブクラス使用例
続いて、本文の生成に関わる8つのクラスについて説明します。
これらは一部の例外を除きそれぞれ組み合わせて使用することが可能です。
コード例は全て1つ前のコード例「本文を生成」箇所に挿入して実行するものとします。
1. TextSegmentInput
最も基本的な、単なるテキストを生成するクラスです。
text
プロパティに本文を指定します。
※「リッチテキストを投稿する」直後のコードとほぼ同様
ConnectApi.TextSegmentInput textSegment = new ConnectApi.TextSegmentInput();
textSegment.text = 'メッセージ\nメッセージ';
body.messageSegments.add(textSegment);
2. MentionSegmentInput
ユーザをメンションする場合はid / username
どちらのプロパティでも使用可能ですが、グループをメンションする場合はid
でのみ指定可能です。
ユーザへのメンション
// idを用いたメンション
ConnectApi.MentionSegmentInput mentionSegment = new ConnectApi.MentionSegmentInput();
mentionSegment.id = '0055h000005kMrEAAU';
body.messageSegments.add(mentionSegment);
// usernameを用いたメンション
ConnectApi.MentionSegmentInput mentionSegment = new ConnectApi.MentionSegmentInput();
mentionSegment.username = 'hoge-YamadaHanako@example.com';
body.messageSegments.add(mentionSegment);
グループへのメンション(ユーザへのメンションと併用可)
// グループへのメンション
ConnectApi.MentionSegmentInput mentionSegment = new ConnectApi.MentionSegmentInput();
mentionSegment.id = '0F95h0000016FVaCAM';
body.messageSegments.add(mentionSegment);
// ユーザへのメンション
ConnectApi.MentionSegmentInput mentionSegment2 = new ConnectApi.MentionSegmentInput();
mentionSegment2.username = 'hoge-YamadaHanako@example.com';
body.messageSegments.add(mentionSegment2);
3. LinkSegmentInput
url
プロパティに有効なURLを渡すとリンクが生成されます。
ConnectApi.LinkSegmentInput linkSegment = new ConnectApi.LinkSegmentInput();
linkSegment.url = 'https://www.salesforce.com/jp/';
body.messageSegments.add(linkSegment);
4. EntityLinkSegmentInput
LinkSegmentInput
と混同しがちですが、こちらはURLではなくSalesforce上のレコードIDへのリンクを生成します。
entityId
にレコードIDを渡すと、そのレコードのName
項目がリンクとして表示されます。
※レコードへのアクセス権が無い場合は表示されないので注意
ConnectApi.EntityLinkSegmentInput entityLinkSegment = new ConnectApi.EntityLinkSegmentInput();
entityLinkSegment.entityId = '0015h00000u16rFAAQ';
body.messageSegments.add(entityLinkSegment);
5. InlineImageSegmentInput
fileId
プロパティにContentDocument
のIDを渡すことでインライン画像が生成されます。(ContentVersion
のIDは使用不可)
altText
プロパティは任意です。
ConnectApi.InlineImageSegmentInput inlineImageSegment = new ConnectApi.InlineImageSegmentInput();
inlineImageSegment.altText = 'ロゴ';
inlineImageSegment.fileId = '0695h00000DRoUJAA1';
body.messageSegments.add(inlineImageSegment);
6. HashtagSegmentInput
tag
プロパティにタグ名を指定することでハッシュタグが生成されます。
ConnectApi.HashtagSegmentInput hashtagSegment = new ConnectApi.HashtagSegmentInput();
hashtagSegment.tag = 'ハッシュタグだよ';
body.messageSegments.add(hashtagSegment);
7 / 8. MarkupBeginSegmentInput / MarkupEndSegmentInput
これらはそれぞれHTMLタグの開始 / 終了を宣言することができます。
タグの入れ子も一部例外を除いて可能なため、書式調整の自由度はかなり高いです。
また、ハイパーリンクを使用する場合もこちらのクラスを使用する必要があります。
使用可能なタグ一覧はこちらから確認できます。
太字テキスト
最もシンプルな例です。
// ①マークアップタグ開始
ConnectApi.MarkupBeginSegmentInput boldBegin = new ConnectApi.MarkupBeginSegmentInput();
boldBegin.markupType = ConnectApi.MarkupType.Bold;
body.messageSegments.add(boldBegin);
// ②タグで囲むテキストを挿入
ConnectApi.TextSegmentInput textSegment = new ConnectApi.TextSegmentInput();
textSegment.text = 'メッセージ';
body.messageSegments.add(textSegment);
// ③マークアップタグ終了
ConnectApi.MarkupEndSegmentInput boldEnd = new ConnectApi.MarkupEndSegmentInput();
boldEnd.markupType = ConnectApi.MarkupType.Bold;
body.messageSegments.add(boldEnd);
// 結果
// <b>メッセージ</b>
こちらの例を言語化すると、以下のような手順となります。
- 本文に
<b>
を挿入(Chatter投稿画面の「太字」ボタンをONにしている状態) - テキストを挿入(Chatter投稿画面で文字を入力)
- 本文に
</b>
を挿入(Chatter投稿画の「太字」ボタンをOFFに変更)
この手順の結果<b>メッセージ</b>
が本文に設定されている状態になり、画面へ太字で表示されます。
ハイパーリンク
markupType
にHyperlink
を設定した時のみ、altText / url(必須)
プロパティが使用可能です。
表示テキストとしてTextSegmentInput
も必須です。
// マークアップタグ開始
ConnectApi.MarkupBeginSegmentInput linkBegin = new ConnectApi.MarkupBeginSegmentInput();
linkBegin.markupType = ConnectApi.MarkupType.Hyperlink;
linkBegin.altText = '代替テキスト';
linkBegin.url = 'https://www.salesforce.com/jp/';
body.messageSegments.add(linkBegin);
// リンク表示するテキストを設定
ConnectApi.TextSegmentInput textSegment = new ConnectApi.TextSegmentInput();
textSegment.text = '表示テキスト';
body.messageSegments.add(textSegment);
// マークアップタグ終了
ConnectApi.MarkupEndSegmentInput linkEnd = new ConnectApi.MarkupEndSegmentInput();
linkEnd.markupType = ConnectApi.MarkupType.Hyperlink;
body.messageSegments.add(linkEnd);
// 結果
// <a href="https://www.salesforce.com/jp/" alt="代替テキスト" target="_blank">表示テキスト</a>
ハイパーリンク + リスト
上記例の前後にリストを挿入したパターンです。
for(Integer i = 0; i < 5; i++){
// リスト開始
ConnectApi.MarkupBeginSegmentInput listItemBegin = new ConnectApi.MarkupBeginSegmentInput();
listItemBegin.markupType = ConnectApi.MarkupType.ListItem;
body.messageSegments.add(listItemBegin);
// ハイパーリンク開始
ConnectApi.MarkupBeginSegmentInput linkBegin = new ConnectApi.MarkupBeginSegmentInput();
linkBegin.markupType = ConnectApi.MarkupType.Hyperlink;
linkBegin.altText = '代替テキスト';
linkBegin.url = 'https://www.salesforce.com/jp/';
body.messageSegments.add(linkBegin);
// ハイパーリンクのテキスト挿入
ConnectApi.TextSegmentInput textSegment = new ConnectApi.TextSegmentInput();
textSegment.text = '表示テキスト' + String.valueOf(i);
body.messageSegments.add(textSegment);
// ハイパーリンク終了
ConnectApi.MarkupEndSegmentInput linkEnd = new ConnectApi.MarkupEndSegmentInput();
linkEnd.markupType = ConnectApi.MarkupType.Hyperlink;
body.messageSegments.add(linkEnd);
// リスト終了
ConnectApi.MarkupEndSegmentInput listItemEnd = new ConnectApi.MarkupEndSegmentInput();
listItemEnd.markupType = ConnectApi.MarkupType.ListItem;
body.messageSegments.add(listItemEnd);
}
// 結果
// <ul>
// <li><a href="https://www.salesforce.com/jp/" alt="代替テキスト" target="_blank">表示テキスト0</a></li>
// <li><a href="https://www.salesforce.com/jp/" alt="代替テキスト" target="_blank">表示テキスト1</a></li>
// <li><a href="https://www.salesforce.com/jp/" alt="代替テキスト" target="_blank">表示テキスト2</a></li>
// <li><a href="https://www.salesforce.com/jp/" alt="代替テキスト" target="_blank">表示テキスト3</a></li>
// <li><a href="https://www.salesforce.com/jp/" alt="代替テキスト" target="_blank">表示テキスト4</a></li>
// </ul>
注意点(改行について)
MarkupBeginSegmentInput
とMarkupEndSegmentInput
で囲まれているテキストは、\n
による改行が無効になります。(MarkupType.Code
を除く)
以下の例は太字テキストのコードに改行を加えたものです。
今まで通り改行されてほしいところですが、太字のみ適用され改行は無視されている状態です。
ConnectApi.MarkupBeginSegmentInput boldBegin = new ConnectApi.MarkupBeginSegmentInput();
boldBegin.markupType = ConnectApi.MarkupType.Bold;
body.messageSegments.add(boldBegin);
ConnectApi.TextSegmentInput textSegment = new ConnectApi.TextSegmentInput();
textSegment.text = 'メッセージ\nメッセージ';
body.messageSegments.add(textSegment);
ConnectApi.MarkupEndSegmentInput boldEnd = new ConnectApi.MarkupEndSegmentInput();
boldEnd.markupType = ConnectApi.MarkupType.Bold;
body.messageSegments.add(boldEnd);
// 結果
// <b>メッセージメッセージ</b>
改行する場合は以下のどちらかで対応してください。
1. タグを分ける
少々面倒ですが、行ごとにMarkupType.Paragraph
で囲い段落を分けることで改行と同様の表示を実現できます。
// 1行目の段落を挿入
ConnectApi.MarkupBeginSegmentInput paragraphBegin1 = new ConnectApi.MarkupBeginSegmentInput();
paragraphBegin1.markupType = ConnectApi.MarkupType.Paragraph;
body.messageSegments.add(paragraphBegin1);
ConnectApi.MarkupBeginSegmentInput boldBegin1 = new ConnectApi.MarkupBeginSegmentInput();
boldBegin1.markupType = ConnectApi.MarkupType.Bold;
body.messageSegments.add(boldBegin1);
ConnectApi.TextSegmentInput textSegment1 = new ConnectApi.TextSegmentInput();
textSegment1.text = 'メッセージ';
body.messageSegments.add(textSegment1);
ConnectApi.MarkupEndSegmentInput boldEnd1 = new ConnectApi.MarkupEndSegmentInput();
boldEnd1.markupType = ConnectApi.MarkupType.Bold;
body.messageSegments.add(boldEnd1);
ConnectApi.MarkupEndSegmentInput paragraphEnd1 = new ConnectApi.MarkupEndSegmentInput();
paragraphEnd1.markupType = ConnectApi.MarkupType.Paragraph;
body.messageSegments.add(paragraphEnd1);
// 2行目の段落を挿入
ConnectApi.MarkupBeginSegmentInput paragraphBegin2 = new ConnectApi.MarkupBeginSegmentInput();
paragraphBegin2.markupType = ConnectApi.MarkupType.Paragraph;
body.messageSegments.add(paragraphBegin2);
ConnectApi.MarkupBeginSegmentInput boldBegin2 = new ConnectApi.MarkupBeginSegmentInput();
boldBegin2.markupType = ConnectApi.MarkupType.Bold;
body.messageSegments.add(boldBegin2);
ConnectApi.TextSegmentInput textSegment2 = new ConnectApi.TextSegmentInput();
textSegment2.text = 'メッセージ';
body.messageSegments.add(textSegment2);
ConnectApi.MarkupEndSegmentInput boldEnd2 = new ConnectApi.MarkupEndSegmentInput();
boldEnd2.markupType = ConnectApi.MarkupType.Bold;
body.messageSegments.add(boldEnd2);
ConnectApi.MarkupEndSegmentInput paragraphEnd2 = new ConnectApi.MarkupEndSegmentInput();
paragraphEnd2.markupType = ConnectApi.MarkupType.Paragraph;
body.messageSegments.add(paragraphEnd2);
// 結果
// <p><b>メッセージ</b></p>
// <p><b>メッセージ</b></p>
2. 改行用の段落を挿入する
こちらは改行したい箇所に段落を差し込むことで行を分けています。
間に空行が入っても問題ない場合はこちらの方がお手軽かもしれません。
// 1行目の太字テキスト
ConnectApi.MarkupBeginSegmentInput boldBegin1 = new ConnectApi.MarkupBeginSegmentInput();
boldBegin1.markupType = ConnectApi.MarkupType.Bold;
body.messageSegments.add(boldBegin1);
ConnectApi.TextSegmentInput textSegment1 = new ConnectApi.TextSegmentInput();
textSegment1.text = 'メッセージ';
body.messageSegments.add(textSegment1);
ConnectApi.MarkupEndSegmentInput boldEnd1 = new ConnectApi.MarkupEndSegmentInput();
boldEnd1.markupType = ConnectApi.MarkupType.Bold;
body.messageSegments.add(boldEnd1);
// 段落を挿入
ConnectApi.MarkupBeginSegmentInput paragraphBegin = new ConnectApi.MarkupBeginSegmentInput();
paragraphBegin.markupType = ConnectApi.MarkupType.Paragraph;
body.messageSegments.add(paragraphBegin);
ConnectApi.TextSegmentInput textSegmentBreak = new ConnectApi.TextSegmentInput();
textSegmentBreak.text = ' ';
body.messageSegments.add(textSegmentBreak);
ConnectApi.MarkupEndSegmentInput paragraphEnd = new ConnectApi.MarkupEndSegmentInput();
paragraphEnd.markupType = ConnectApi.MarkupType.Paragraph;
body.messageSegments.add(paragraphEnd);
// 2行目の太字テキスト
ConnectApi.MarkupBeginSegmentInput boldBegin2 = new ConnectApi.MarkupBeginSegmentInput();
boldBegin2.markupType = ConnectApi.MarkupType.Bold;
body.messageSegments.add(boldBegin2);
ConnectApi.TextSegmentInput textSegment2 = new ConnectApi.TextSegmentInput();
textSegment2.text = 'メッセージ';
body.messageSegments.add(textSegment2);
ConnectApi.MarkupEndSegmentInput boldEnd2 = new ConnectApi.MarkupEndSegmentInput();
boldEnd2.markupType = ConnectApi.MarkupType.Bold;
body.messageSegments.add(boldEnd2);
// 結果
// <b>メッセージ</b>
// <p> </p>
// <b>メッセージ</b>
FeedItemオブジェクトを使って投稿する(2022/8/29追記)
ConnectApiを使わずにFeedItemをinsertしてもいいんじゃない?というコメントを複数見掛けました。
確かに、特に簡単な投稿内容であればこちらの方が使いやすいと感じました、、
完っ全に頭から抜け落ちていたので追記させてください、、!
ご意見を下さった方々へ、この場を借りてお礼申し上げます!!!
そもそもFeedItemって?
Chatterの投稿内容を保持するオブジェクトです。
当然ですが、Salesforceの画面上や前述のConnectApiによる投稿など方法に依らず、Chatterが投稿された際はこのFeedItemのレコードが作成されます。
ここでは、今回の記事に関連する主要な項目を少しだけ紹介します。
- ParentId : 投稿先のID(ConnectApiによる投稿時の
subjectId
と同様 - body : 投稿本文(ConnectApiによる投稿時の
text | ConnectApi.MessageBodyInput
)と同様 - IsRichText : 本文がリッチテキストか判定する真偽値
これまたそれなりの項目数があったので、気になる方はドキュメントをご覧ください。(丸投げ)
ただConnectApi周りのドキュメントに比べると、こちらは単なる標準項目の説明なのでまだ読みやすいかもしれません。
テキスト
テキストのみを投稿すると同じ内容を投稿するシンプルな例です。
普通のオブジェクトを作成する時と同じ記法なので直感的で分かりやすいですね。
特にConnectApi.ChatterFeeds.postFeedElement()
のような、見る人によっては呪文のようなメソッドを使う必要が無い点は大きな利点だと感じました。
String subjectId = '0015h00000GHHciAAH';
String text = 'メッセージ\nメッセージ';
FeedItem feed = new FeedItem(
ParentId = subjectId,
Body = text
);
insert feed;
ここで作成されたFeedItemレコードをSOQLで取得すると以下のような結果が得られます。
IsRichText
の初期値はfalseなので、特に違和感無い結果だと思います。
ParentId | Body | IsRichText |
---|---|---|
0015h00000GHHciAAH | メッセージ メッセージ | false |
また、テキストのみを投稿する・リッチテキストを投稿する(冒頭の例)で投稿を行った場合に作成されるFeedItemレコードも同じ結果となります。
リッチテキスト
こちらで紹介した太字テキストを投稿する例です。
IsRichText = true
を追加しすることでリッチテキストとして解釈されます。
String subjectId = '0015h00000GHHciAAH';
String text = '<b>メッセージ</b>';
FeedItem feed = new FeedItem(
ParentId = subjectId,
Body = text,
IsRichText = true
);
insert feed;
ParentId | Body | IsRichText |
---|---|---|
0015h00000GHHciAAH | <b>メッセージ</b> | true |
IsRichText
によりBodyの解釈が変わるだけなので、レコードの作成時か作成後の更新でIsRichText = false
が指定されてしまうとただの文字列として表示されてしまうので注意が必要です。
----------------------- 2022/8/29追記分ここまで -----------------------
おわりに
Chatterの投稿に関してはある程度説明できたんじゃないかと思います。
しかし、コメントやいいねなどApexで出来ることはまだまだたくさんあるので興味があれば以下のリンクからご確認ください。
何ができるのかだけでも知っておくといつか役に立つ日が来るかもしれません、、!