6
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【Apex】ドキュメントをなるべく読まずにChatterを投稿したい人に読んでほしい記事

Last updated at Posted at 2022-08-24

はじめに

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);

image.png

今回は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.​FeedElement​Type列挙型 → FeedItemのみ使用可
  • text : 投稿するテキスト → \nで改行可能(最大10,000文字)

リッチテキストを投稿する

上記の例でも最低限の投稿は可能ですが、メンションやリンク等を用いた投稿は行えません。
これらを実現するためにはConnectApi.​FeedItem​​Inputクラスを用いた投稿を行う必要があります。
ConnectApi.​FeedItem​​Inputを用いた、最初の例と同じ投稿を行うコードが以下です。

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)

順を追って、と言いつついきなり最終行に飛びます。
feedElementConnectApi.​FeedElementInputクラス型で、フィード要素(Chatter投稿に必要な情報の集合)のコンテナです。
postFeedElement()を実行するため、フィード要素を適切に設定して引数に渡すことがゴールになります。

関連クラスの関係図

image.png

  • FeedElementInput : 抽象クラスのため直接インスタンスを生成することはできない。ドキュメント上はこのクラスが引数の型として記載されている。
  • FeedItemInput : FeedElementInputのサブクラスで実際にインスタンス化するのはこのクラス(例4行目)。FeedElementInputのプロパティであるsubjectId / feedElementTypeはインスタンス化した後で直接設定する。
  • MessageBodyInputFeedItemInput.bodyの型で、message​​Segmentsプロパティに本文を設定する。
  • MessageSegmentInputMessageBodyInput.message​​Segmentsがこのクラスのリスト型。計8つのサブクラスを組み合わせて本文を生成する。

まとめると以下の流れが基本形となります。

  1. FeedElementInputを除く3つのクラスを使ってインスタンスを生成
  2. MessageBodyInput.messageSegmentsに本文を追加
  3. 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.​Message​Segment​Inputのサブクラス使用例

続いて、本文の生成に関わる8つのクラスについて説明します。
これらは一部の例外を除きそれぞれ組み合わせて使用することが可能です。
コード例は全て1つ前のコード例「本文を生成」箇所に挿入して実行するものとします。

1. TextSegmentInput

最も基本的な、単なるテキストを生成するクラスです。
textプロパティに本文を指定します。

※「リッチテキストを投稿する」直後のコードとほぼ同様

ConnectApi.TextSegmentInput textSegment = new ConnectApi.TextSegmentInput();
textSegment.text = 'メッセージ\nメッセージ';
body.messageSegments.add(textSegment);

image.png

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);

image.png

グループへのメンション(ユーザへのメンションと併用可)

// グループへのメンション
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);

image.png

3. LinkSegmentInput

urlプロパティに有効なURLを渡すとリンクが生成されます。

ConnectApi.LinkSegmentInput linkSegment = new ConnectApi.LinkSegmentInput();
linkSegment.url = 'https://www.salesforce.com/jp/';
body.messageSegments.add(linkSegment);

image.png

4. EntityLinkSegmentInput

LinkSegmentInputと混同しがちですが、こちらはURLではなくSalesforce上のレコードIDへのリンクを生成します。
entityIdにレコードIDを渡すと、そのレコードのName項目がリンクとして表示されます。
※レコードへのアクセス権が無い場合は表示されないので注意

ConnectApi.EntityLinkSegmentInput entityLinkSegment = new ConnectApi.EntityLinkSegmentInput();
entityLinkSegment.entityId = '0015h00000u16rFAAQ';
body.messageSegments.add(entityLinkSegment);

image.png

5. InlineImageSegmentInput

fileIdプロパティにContentDocumentのIDを渡すことでインライン画像が生成されます。(ContentVersionのIDは使用不可)
altTextプロパティは任意です。

ConnectApi.InlineImageSegmentInput inlineImageSegment = new ConnectApi.InlineImageSegmentInput();
inlineImageSegment.altText = 'ロゴ';
inlineImageSegment.fileId = '0695h00000DRoUJAA1';
body.messageSegments.add(inlineImageSegment);

image.png

6. HashtagSegmentInput

tagプロパティにタグ名を指定することでハッシュタグが生成されます。

ConnectApi.HashtagSegmentInput hashtagSegment = new ConnectApi.HashtagSegmentInput();
hashtagSegment.tag = 'ハッシュタグだよ';
body.messageSegments.add(hashtagSegment);

image.png

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>

image.png

こちらの例を言語化すると、以下のような手順となります。

  1. 本文に<b>を挿入(Chatter投稿画面の「太字」ボタンをONにしている状態)
  2. テキストを挿入(Chatter投稿画面で文字を入力)
  3. 本文に</b>を挿入(Chatter投稿画の「太字」ボタンをOFFに変更)

この手順の結果<b>メッセージ</b>が本文に設定されている状態になり、画面へ太字で表示されます。

ハイパーリンク

markupTypeHyperlinkを設定した時のみ、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>

image.png

ハイパーリンク + リスト

上記例の前後にリストを挿入したパターンです。

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>

image.png

注意点(改行について)

MarkupBeginSegmentInputMarkupEndSegmentInputで囲まれているテキストは、\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>

image.png

改行する場合は以下のどちらかで対応してください。

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>

image.png

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 = '&nbsp;';
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>

image.png

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が指定されてしまうとただの文字列として表示されてしまうので注意が必要です。
image.png

----------------------- 2022/8/29追記分ここまで -----------------------

おわりに

Chatterの投稿に関してはある程度説明できたんじゃないかと思います。
しかし、コメントやいいねなどApexで出来ることはまだまだたくさんあるので興味があれば以下のリンクからご確認ください。
何ができるのかだけでも知っておくといつか役に立つ日が来るかもしれません、、!

6
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?