自習用」カテゴリーアーカイブ

Power AppsからTeamsに直接メッセージを送信する。Power Automateを介さずに。

以前PowerAutomateを介してPowerAppsからTeamsにメッセージを送信したり、投稿に返信したりする方法をご紹介しました。ここでは、Power Automateを使わずPowerAppsから直接Teamsへメッセージを送信する方法を紹介します。

PowerAutomateを介さないメリットは2つあります。ひとつは、管理すべき対象がアプリひとつになること。共有するのも楽です。もうひとつは、投稿者をアプリを開いているユーザーに簡単にできること。PowerAutomateだと実行可能ユーザーとして権限設定してやる必要があります。

PowerAutomateを介さないデメリットは、アプリにアクセスしたユーザーの権限でしか動かせないことです。チームにメンバーを追加するなど特別な権限が必要な処理を行えない可能性があります。


本ページに関連する資料をまとめます。

操作画面付きの実施例が他サイトで紹介されています。PowerAppsに慣れない方はリンク先をご覧いただくことをお勧めします。
Power Apps のキャンバスアプリから直接 Teams にメッセージを投稿しよう


ボットチャットにメッセージを送信する

ボットチャットからのメッセージが投稿されるWorkflowsとのチャットにメッセージを送信する方法です。宛先メールアドレスとメッセージ本文が必要です。メッセージ本文はHTMLで記述できます。isAlertがtrueだとメンション等と同じく通知が受信者に届きますが、falseだと通知が届かず未読メッセージが増えるような挙動になります。

以下の例ではlocBotChatMessageで作成したメッセージをPostMessageToConversation関数で送信しています。

//メッセージを作成
UpdateContext({
    locBotChatMessage:
    {
        recipient: 宛先メールアドレス,
        messageBody: メッセージ本文,
        isAlert:true
    }
    });
//メッセージを送信。戻り値をlocResultBotChatMessageに格納
UpdateContext({locResultBotChatMessage:
MicrosoftTeams.PostMessageToConversation(
    "Flow bot",
    "Chat with Flow bot",
    ParseJSON(JSON(locBotChatMessage))
)});

グループチャットにメッセージを送信する

まじめにPowerAppでTeamsコネクタを使いこなすよりも、宛先・タイトル・本文をURIスキーマで指定し、Luanch関数でTeamsを起動する方が楽なような気がします。とはいえPowerAppでがんばる場合の作例をまとめておきましょう。

グループチャットにメッセージを送信するには、まずチャットの場を作ってその会話IDを基にチャットにメッセージを送信するという流れになります、注意が必要なのですが1対1の会話でもグループチャットと呼びます。

詳細は後日加筆。CreateChat関数でグループチャットを作成し会話IDを得る。PostMessageToConversation関数から会話IDをrecipientにしてメッセージを送信する。posterはFlow bot”, locationは”Group chat”


前準備

メッセージを投稿する先のチャネルの情報を確認します。
チャネルのURLからチームIDとチャネルIDを読み取る方法を参照してください。

新しいメッセージをチャネルに投稿する

新規メッセージを投稿する場合の方法を紹介します。新しいスレッドを建てることです。

PostMessageToConversation関数を使います。

以下の例文のうち、チームID、チャネルID、メッセージ本文、メッセージタイトルにそれぞれ文字列を設定します、メッセージタイトルは必須ではないので、タイトルが必要ない場合はsubjectを消してください。メッセージ本文はHTMLで記述できるので、リンクを張ったり文字を大きくしたり色を付けたりできます。

//メッセージを作成
UpdateContext({
    locPostMessage:
    {
        recipient:{
            groupId: チームID,
            channelId: チャネルID
        },
        messageBody: メッセージ本文,
        subject: メッセージタイトル
    }}
);
//メッセージを送信。戻り値をlocResultPostMessageに格納
UpdateContext({locResultPostMessage:
MicrosoftTeams.PostMessageToConversation(
    "user",
    "Channel",
    ParseJSON(JSON(locPostMessage))
)});

戻り値は、conversationId, id, messageLinkの3つが入ったレコードです。

conversationIdはチャネルID、idは後述する返信で使用するメッセージID、messageLinkはその投稿へアクセスできるURLです。

既存のスレッドに返信する

ReplyWithMessageToConversation関数を使います。

チームID、チャネルID、メッセージID、メッセージ本文が必要です。
メッセージIDは前節でPostMessageToConversation関数の戻り値のidとして得られることを紹介しています。

なお、ReplyWithMessageToConversation関数の戻り値はPostMessageToConversation関数の戻り値と同じ構造です。

//メッセージを作成
UpdateContext({
    locReplyMessage:
    {
        recipient:{
            groupId: チームID,
            channelId: チャネルID
        },
        parentMessageId: メッセージID,
        messageBody: メッセージ本文
    }}
);

//返信する
UpdateContext({locResultReplyMessage:
MicrosoftTeams.ReplyWithMessageToConversation(
    "user",
    "Channel",
    ParseJSON(JSON(locReplyMessage))
)});

Teamsで音声通話やチャットを開くリンクの作り方

URIスキーマを使うと、リンクを開いたとき自動的に特定のユーザーとの音声通話を開始したり、チャット画面を開いたりできます。宛先は複数でも可能。
ユーザーがTeamsで投稿ボタンを押す動作が必要ですが、PowerAppsやPowerAutomateで頑張ってチャット送信の仕組みを作るよりも断然楽です。

今ページでは、メールアドレス example@onmicrosoft.com を宛先の例に使います。複数の宛先を用いる場合はカンマで繋げてください。

音声通話を開始する

Teamsの音声通話を開始するURIの書き方は以下の通りです。参考リンク

https://teams.microsoft.com/l/call/0/0?users=example@onmicrosoft.com

&withVideo=trueを追加すると、ビデオ通話を開始します。

https://teams.microsoft.com/l/call/0/0?users=example@onmicrosoft.com&withVideo=true

チャットを開始する

Teamsのチャットを開くURIの書き方は以下の通りです。参考リンク

https://teams.microsoft.com/l/chat/0/0?users=example@onmicrosoft.com

グループチャットのタイトルはtopicNameで指定できます。さらに、メッセージの下書きはmessageで指定できます。

ここで注意していただきたいのが、日本語の使用する場合はURIエンコードした文字列を埋め込むということです。
以下の例では
topicName=ここにタイトルを書く
message=ここに本文を書く
という文字列をURIエンコードした文字列を使用することで、グループチャットのタイトルと本文を指定した状態でTeamsを開かせることができます。

https://teams.microsoft.com/l/chat/0/0?&users=example1@onmicrosoft.com,example2@onmicrosoft.com&topicName=%E3%81%93%E3%81%93%E3%81%AB%E3%82%BF%E3%82%A4%E3%83%88%E3%83%AB%E3%82%92%E6%9B%B8%E3%81%8F&message=%E3%81%93%E3%81%93%E3%81%AB%E6%9C%AC%E6%96%87%E3%82%92%E6%9B%B8%E3%81%8F

新しいデジタルツールを組織に導入するには

会社のような組織でTeamsのような現代的なデジタルツールを利用できるようになったとしても、それだけ同僚の多くが業務に活用するようになるわけではありません。それどころか、ツールが導入されただけで使われないまま何年も経過することさえあります。

デジタルツールを業務で活用していくためには、実際の業務で実践できることが何よりも肝心です。新規プロジェクトを例にとってデジタルツールを導入できるか否かは、以下の3点が揃った人物が行動するか否かにかかっています。

  • プロジェクトの主導的な立場である
    プロジェクトのやり方が決まる前にデジタルツールを使おうと言える立場であること。
    発起人、とりまとめ役、初期メンバーのいずれかである。
  • デジタルツールを使える
    Teamsで新しいチームを作れる、Plannerで新しいPlanやTaskを作れる、など少し調べればたいていの操作を自分でできる
  • 協働する同僚をサポートできる
    ツールの使い方を教える、業務に合わせて適切な使い方を実践して見せる、などツールを使って実際の業務をみんなで進めるために他人を支援できる。

ユーザーの手間を最小化 Param関数を使おう

業務アプリは使ってもらって価値を生みます。たくさん使ってもらうには、作業の手間、難易度、認知的負荷のすべてを小さくすることが有効です。

業務アプリの中でも、PowerAppsでQRコードを読み込むアプリを作っている方に、Param関数の利用をおすすめします。

PowerAppsのバーコードリーダーコントロールを使用すれば、アプリの中でQRコードやバーコードを認識し、埋め込まれた文字列を取り出せます。IDや情報を埋め込むことで、PowerAppsで詳しい情報を表示したり、手続きを行ったりできます。

ところで、普段お店などでQRコードを読み込むとき、わざわざ何かのアプリを起動するでしょうか?たいていはスマホの標準カメラに内蔵されたQRコードを読み込む機能を使用すると思います。PowerAppsの中でバーコードリーダーを使うよりも、スマホの標準カメラでQRコードを読んだ方がひと手間少なく済みます。ユーザーの手間を惜しみ、できるだけ少ない手数でユーザーが作業を完遂できるように工夫するのがアプリ制作者の腕の見せ所です。

このような処理をするには、PowerAppsのアプリURLにパラメータを付加する手法が有効です。

Param関数を使ったアプリとしては過去にオフィス内宅配ロッカーを作成しました。

Power Automateでカンマ区切りメールアドレスから複数人にTeamsのメンションをかける

Teamsで複数人にメンションをかける方法が知られています。リンク先の方法では、複数のメールアドレスが配列として得られています。一方で、複数人の宛先を記述するよくやる方法として、カンマ,やセミコロン;で区切って複数のメールアドレスを書き並べた文字列を作ることがあります。本記事では、このようなケースでカンマ区切り文字列から複数のメールアドレスの配列を生成します。

なお、この記事で紹介するフローであれば、カンマ区切りで連結していない単独のメールアドレスが入力であるときも正常に動作します。

フローの全体像

Power Automateのフローは以下の通りです。各処理の中身は後述します。

各アクションの解説

カンマ区切りのメールアドレス

組み込み>データ操作>作成アクションを使用して、このフローで使用するカンマ区切りテキスト形式のメールアドレスを格納します。実際のフローでは、PowerAppsトリガーなどを経由して値を渡します。

ここでは “aa@exaple.com, bb@example.com, cc@example.com” のような形でメールアドレスが記述されています。

メールアドレス配列の初期化

組み込み>変数>変数を初期化するアクションを使用して、カンマ区切り文字列から配列を生成します。

split関数に分割する文字列と、分割する位置を指定する文字(ここではカンマ,)を与えることで、文字列を分割します。注意すべきことに、split関数を使うだけではPower Automateのフローで繰り返し操作の対象となる配列になりません。split関数の出力は文字列です。変数を初期化するアクションに配列のもとと「値」に、このsplit関数の出力を渡す必要があります。

式にはsplit(outputs(‘カンマ区切りメールアドレス’),’,’)のように記述します。

この配列は「メールアドレス配列」という名前で後段で使用できます。

メンショントークンの初期化

各メールアドレスから生成したメンショントークンを格納します。組み込み>変数>変数を初期化するアクションを使用して、空の配列を用意します。

Apply to each

組み込み>コントロール>それぞれに適用するアクションを使用して、「メールアドレス配列」から各メールアドレス要素を取り出してメンショントークンを生成する処理を行います。


ユーザープロフィールの取得

標準>Office365Users>ユーザープロフィールの取得(V2)アクションを使用して、メールアドレスからユーザープリンシパル名を取得します。@mentionトークンを取得するのにユーザープリンシパル名が必要なのに対して、所属組織によってはメールアドレスがユーザープリンシパル名と異なる場合があるためです。

ここで入力には以下のように記述しています。
trim(items(‘Apply_to_each’))
trim関数はスペースを削除する関数です。カンマ区切りテキスト形式でメールアドレスを入力する際に、メールアドレス間にスペースを入力することが多いので、スペースを除去する処理を介在させた方が安全です。

ユーザーの@mentionトークンを取得する

Teamsの当該アクションで、ユーザープリンシパル名から@mentionトークンを生成します。

配列変数に追加

組み込み>変数>配列変数に追加アクションで、生成した@mentionトークンを配列に格納します。

メンショントークンの結合

組み込み>データ操作>結合アクションで、「メンショントークン」配列の要素を結合します。「次を使用して結合」はカンマ,を使用していますが、半角スペースなどでもOKです。

チャットまたはチャネルでメッセージを投稿する

「メンショントークンの結合」アクションの出力をメッセージの中で使用すれば、メンションがかかります。

なお、複数のメンショントークンを作成し、結果としてひとつのメッセージの中で同じユーザーに対して何度もメンションがかかるような本文ができたときは、本文中で最初にそのユーザーが登場する部分だけメンションがかかったように表示されます。

Power AutomateでTeamsのスレッドに返信する

Power AutomateでTeamsのスレッドに返信のメッセージを投稿するには、チャネル内のメッセージで応答しますを使用します。新しいスレッドを建てるチャットまたはチャネルでメッセージを投稿するに比べて少々わかりにくい名称ですし、リファレンスを読んでもよくわかりません。こういうときは、Power Automate上で実際に作ってみるのが早いです。

投稿者はFlow BotまたはUserから選択します。投稿先はChannelの一択です。

Message IDはチャットまたはチャネルでメッセージを投稿するのメッセージIDを入力します。チャットまたはチャネルでメッセージを投稿するの出力は、メッセージ ID、メッセージリンク、会話 IDの3個です。なお、私の環境で試したところ会話IDはチャネルIDと同じでした。

TeamとChannelは投稿先のチームとチャネルを指定します。カスタム値の入力を使って、チームIDとチャネルIDから指定することもできます。チームIDとチャネルIDの取得方法は別の記事で紹介しています。

ところで、チャネル内のメッセージで応答しますの出力にもメッセージIDがあります。こちらのメッセージIDに対して返信しようとすると、メッセージが見つからないというエラーがでました。スレッドのトップのメッセージにしか返信できないのかもしれません。

Power AutomateでTeamsのチャネルのリンクからチャネルIDとチームIDを取得する

Power Automateでチャネルへの投稿を自動化したい場合、チャネルIDとチームIDが必要です。この二つのIDをユーザーが取得しやすい「チャネルのリンク」から取り出す方法を紹介します。

Teamsのチームの各チャネルには直接チャネルにアクセスできるURLがあります。このURLはTeamsのチャネル名の右にある三点ボタンからメニューを開き「チャネルへのリンクを取得」を選択することで取得できます。ここで得られるURLには、チャネルIDとチームIDが含まれています。したがって、「チャネルのリンク」を入力として、「チャネルID」文字列と「チームID」文字列を出力とするフローを組むことができます。

フローの全体像

Power Automateのフローは以下の通りです。各処理の中身は後述します。

各アクションの解説

チャネルのリンク
データ操作の作成アクションです。「チャネルへのリンクを取得」で取得したURLを与えています。このアクションの代わりに、トリガーで入力するようにしたり、Power Appsから渡すようにしておくと活用しやすいでしょう。

URIデコード
データ操作の作成アクションです。入力には、作成アクション「チャネルのリンク」の出力を用いて「decodeUriComponent(outputs('チャネルのリンク'))」のように指定します。
「チャネルへのリンクを取得」で取得したURLは本来URIで使用できない文字列をエンコードした形式になっています。これを本来の文字列に直す変換操作であるURIデコードを行います。

URIを配列に分ける
データ操作の作成アクションです。入力には、作成アクション「URIデコード」の出力を使って式を「split(outputs('URIデコード'),'/')」と指定します。
チャネルのリンクは、以下のような構造をしています。「/」で文字列を分割すれば、5番目がチームID、6番目のgroupID=以降がチャネルIDであることがわかります。なお、0番始まりで数えている点に注意してください。

https://teams.microsoft.com/l/channel/{チャネルID}/{チャネル名}?groupId={チームID}&tenantId={テナントID}

チャネルID
データ操作の作成アクションです。入力には、作成アクション「URIを配列に分ける」の出力を使って式を「outputs('URIを配列に分ける')[5]」と指定します。
末尾が@thread.tacv2という文字列だと思います。

テキストの位置の検索
テキスト関数のテキストの位置の検索アクションです。入力の「テキスト」には、作成アクション「URIを配列に分ける」の出力を使って式を「outputs('URIを配列に分ける')[6]」と指定します。また、入力の「検索テキスト」に「groupId=」と指定します。
「outputs(‘URIを配列に分ける’)[6]」で取得した文字列から「groupId=」の位置を割り出し、後段の操作でこの文字列よりも後ろの部分を抜き出します。

部分文字列
テキスト関数の部分文字列アクションです。入力の「テキスト」には、作成アクション「URIを配列に分ける」の出力を使って式を「outputs('URIを配列に分ける')[6]」と指定します。また、入力の「開始位置」に式「add(outputs(‘テキストの位置の検索’)?[‘body’],8)」と指定します。
「outputs(‘URIを配列に分ける’)[6]」で取得した文字列から「groupId=」の開始位置を割り出し、「groupId=」の文字数8を加えることで、「groupId=」の後ろの文字列を抽出します。

チームID
データ操作の作成アクションです。入力には、作成アクション「部分文字列」の出力を使って式を「split(outputs('部分文字列')?['body'],'&')[0]」と指定します。
「&」の後ろに続くtenantIdが不要なので、「&」で文字列を分割したうえで配列の先頭(0番目)の文字列を取り出します。
チームIDの文字列はGUIDの形式になっています。

取得したチームIDとチャネルIDの使い方

Teamsの「チャットまたはチャネルでメッセージを投稿する」アクションを使う場合は、投稿先をチャネルとして上で、チームとチャネルに取得したIDを指定します。取得したIDを設定するには「カスタム値の入力」を選択します。

PowerAppsの「PowerAppsまたはFlowに応答する」アクションを使う場合は、出力追加でテキストを選択し、取得したIDの出力を追加します。

Power Automateの実行ユーザーから特定の個人へTeamsチャットのメッセージ送信

PowerAutomateのインスタントクラウドフローは、フローの所有者のほかに「実行のみユーザー」という権限でほかのユーザーに共有できます。この機能を使って、フローを実行するユーザーAから別のユーザーBへTeamsチャットでメッセージを送信することを考えます。

Teamsコネクタのアクションを調べていくと、チャットまたはチャネルでメッセージを投稿するを使用すればチャットのメッセージを送れるような気がします。ところが、チームのチャネルに投稿する方法とグループチャットに投稿する方法はわかりやすいわりに、普通のチャットすなわち個人から個人へチャットする場合はどうしたら良いかぱっと見でわかりません。そこで、このページでは個人から個人へチャットを送る方法を2つ紹介します。

フローの例

フローの例を下図に示します。分岐の右側のフローが方法1、左側が方法2です。

方法1: グループチャットの宛先に1人だけ入れる

それアリ?という感じがしますが、これが最もそれらしい振る舞いをします。

まず、チャットの作成アクションで追加するメンバーに宛先となるメールアドレスを入力します。ここで、宛先のメールアドレスがひとつだけの場合、フロー実行者と宛先の2者間のチャットを指定する会話IDが返ります。グループチャットのタイトルを指定する欄がありますが、タイトルを何にしようと影響はなく、常に2者間の通常のチャットを指定する会話IDが返ります。

こうして得られた会話IDを使って、チャットまたはチャネルでメッセージを投稿するアクションを動作させます。投稿者をユーザーとすればフローの実行者がチャットを送信することになります。投稿先をグループチャット、グループチャットには先ほどの会話IDを入れます。メッセージの記入欄が表示されるので、そこに動的なコンテンツを含むメッセージ本文を記入します。リンクを張りたい場合などは、ボタンを押してHTMLを記述します。

チャットの作成アクションの宛先について注意が必要な点として、実行ユーザー自身を指定できません。もしチャットの宛先にフローの実行者が入る可能性がある場合は、後述する方法2を使用します。

チャットの作成アクションは、宛先が複数のときと今回試したような宛先が1つのときで挙動が違うのに注意してください。宛先が複数あるとき、すなわちグループチャットを作成する場合にはグループチャットの構成メンバーとタイトルが同一であろうとチャットの作成アクションが動作するごとに異なる会話IDが生成されます。

方法2: フローボットとのチャット

チャットまたはチャネルでメッセージを投稿するアクションで、投稿者を「フローボット」、投稿先を「フローボットとチャットをする」に設定します。
宛先の指定は、Recipientに受信者のメールアドレスを指定するだけで十分です。
メッセージの記入欄が表示されるので、そこに動的なコンテンツを含むメッセージ本文を記入します。リンクを張りたい場合などは、ボタンを押してHTMLを記述します。

番外編: チャット相手を指定してTeamsクライアントを立ち上げる

URIスキーマを使うと、宛先を指定した状態でTeamsクライアントのチャット画面を開かせることができます。
宛先のメールアドレスがexample@onmicrosoft.comのとき
https://teams.microsoft.com/l/chat/0/0?users=example@onmicrosoft.com
のように書きます。
PowerAppsのLaunch関数でこのようなURIスキーマを開かせることで、通常のTeamsクライアントでチャットを送る動作をとれます。

番外編: Power AppsからTeamsコネクタを使ってメッセージを送る

Power AppsのSharePointコネクタでリストを読み込んだり、Outlookコネクタでメールを送信したりするのと同様に、Teamsコネクタでメッセージを送信できます。私はやり方がわからなかったのでAutomateで処理しましたが、Power AppsからTeamsのグループチャットに直接メッセージを送る方法を真似すればできそうな気がします。

Power Apps上で処理が完結できると、エラー処理を含めてユーザーにフィードバックを与えながら処理をできるので、全体としてUXが向上すると期待できます。

追記: PowerAppsから直接Teamsにメッセージを送信する方法をまとめました。

PowerAppsカレンダー画面の拡張 データソースをOutlookからSharePointリストに変えて

PowerAppsではシナリオとして典型的な操作を行うための画面が用意されています。複雑な機能を実現するのにシナリオで用意された画面を改変することで自分の目的を達成することができます。この記事では、予定表の表示に使用する「カレンダー」シナリオを拡張する例を紹介します。

シナリオで用意された画面の構成を調べる

改変するには元がどうなっているかを理解する必要があります。「カレンダー」シナリオで用意された画面の構成を調べてみましょう。

PowerAppで新しい携帯電話レイアウトのキャンバスアプリを作成します。続いて、新しい画面を使いする操作から、シナリオの「カレンダー」を選択します。

操作に成功すると、次のような画面が表示されます。画面上部のドロップダウンはPowerAppsで表示する予定表を選択するのでしょうか。ドロップダウンの右のカレンダーアイコンはまだよくわかりません。画面の下部はまずドロップダウンから予定表を選択せよとの指示です。

アプリの動作を確認するため、画面右上の再生ボタンかAltキーを押しながら画面をクリックします。ドロップダウンから「日本の休日」を選択すると、画面が以下のように変わります。

この画面を見ると、「カレンダー」シナリオの画面構成がどうなっているか、だいたい見当がつくでしょう。画面上段は表示すべきカレンダーの選択、画面中段は日付の選択、画面下段は選択した日に入っている予定の一覧です。

また、ドロップダウンを操作したときに画面の大部分が表示されたことから、ドロップダウンに主要なデータ操作が仕込まれていると予想できます。そこで、ドロップダウンのOnSelectやOnChangeを見てみましょう。

ドロップダウンのOnSelectを見ると、何行にもわたるコードが記述されていました。各行を読んでいくと、Office365Outlookコネクタを利用して予定表に登録されている予定の一覧を読み出すGetEventsCalendarViewV2を実行している行があります。この行ではClearCollect関数を使って、読みだした予定の一覧をMyCalendarEventsという名前のコレクション(テーブル)に保持するようだとわかります。

予定表から読みだされるデータはどのような形式かを確認します。PowerAppsの編集画面からコレクションの中身を確認してみましょう。次のような横に長い画面が出てきます。

ここで大事なのは列名とその列にどのようなデータを入れることになっているかです。また、多くの列がありますがほとんどPowerAppsのアプリでは使用していないデータです。一通り列名とデータを確認すると、このあと使いそうなデータは次の4つくらいでした。

列名入っているデータ(予想)入っているデータの型(予想)
End予定の終了年月日時刻時刻
Location予定の場所テキスト
Start予定の開始年月日時刻時刻
Subject予定の題名テキスト

Outlookの予定表から読みだした各予定の情報は高々この程度です。これらがClearCollect関数でMyCalendarEventsという名前のテーブルに格納されているとわかったら、同じ形式のテーブルを用意してやれば、「カレンダー」シナリオの画面をそのまま使いまわせるでしょう。具体定期には、SharePointリストで上の表にまとめたような列を持つリストをOutlook予定表の代わりにデータソースにできるはずです。

SharePointリストに登録したアイテムを予定として読み出す

前のセクションで予定を入れるのに必要なSharePointリストの列は4つ(End, Start, Location, Subject)だろうと見積もりました。このような列を持つSharePointリストを作ってみます。

EndとStartは「日付と時刻」型の列とします。時間を含めるを「はい」にして列を作成します。LocationとSubjectは1行テキストとします。

試しにデータを入れてみました。ここでは「Outlook代替」という名前のリストにしています。

ここで作成したSharePointリストをPowerAppsで読み込んでみましょう。

まずSharePointリストをPowerAppsから参照できるようにします。「データ」>「データの追加」>「SharePoint」で作成した「Outlook代替」リストを指定します。

次にSharePointリストのデータをPowerAppsの「カレンダー」シナリオに合うように読み込みます。単にMyCalendarEventsという名前のテーブルに「Outlook代替」リストの中身を流し込めば十分です。下の例では「SharePoint読込」というボタンのOnSelectに以下のような関数を定義しています。

ClearCollect(MyCalendarEvents, Outlook代替);

「日本の休日」カレンダーには存在しない「hoge」なるイベントを予定表に表示できました。「Outlook代替」で仮に登録したデータが表示されています。

ついでに表示される項目を変更してみましょう。上の図で太字のhogeと表示されているのはSubject列の値です。この部分のTextにはThisItem.Subjectが定義されています。これをThisItem.TitleにすればTitle列の値が表示されます。

「カレンダー」シナリオでは、開始時刻Start、件名Subject、所要時間(終了時刻Endと開始時刻Startの差分を分単位で表示)、場所Locationを表示する構成になっています。これを置き換えたり、追加したり削除したりすることで、自分の用途に必要十分な予定表を作れるでしょう。

予定表に表示される予定の条件を変更する

「カレンダー」シナリオの初期条件では、選択した日付に開始される予定が一覧に表示されます。予定を一覧表示するギャラリーのItemsは次のように定義されています。Text関数はDateTimeFormat.ShortDateで年月日を返します。

SortByColumns(Filter(MyCalendarEvents, Text(Start, DateTimeFormat.ShortDate) = Text(_dateSelected, DateTimeFormat.ShortDate)), "Start")

それでは、複数日にまたがる予定はどうなるでしょうか?先に作成した「Outlook代替」リストには25日から27日またがる予定「ほげ」が入っています。しかし、この予定表では26日や27日に予定「ほげ」が表示されません。

関数を書き換えて、日をまたぐ予定も表示されるようにしましょう。SortByColumnsはただの並べ替えですので、まず注目すべきはFilter関数の中身です。

Filter(MyCalendarEvents, Text(Start, DateTimeFormat.ShortDate) = Text(_dateSelected, DateTimeFormat.ShortDate))

予定の開始日時Startと予定の終了日時Endを使って、選択した日(0時から24時)にかかる予定を表示するようにするにはどうしたらいいでしょうか。状況を整理するため、下図を見てください。表示したい予定の条件は何通りかありますが、表示したくない予定は「終了時刻が選択日の0時より前」と「開始時刻が選択日の24時以降」の2パターンであることがわかります。

「終了時刻が選択した日の0時よりも前の予定」と「開始時刻が選択した翌日0時以降の予定」はいずれも選択した日にかかる予定ではありません。したがって、これの否定Notを取ればいいわけです。選択した日の0時_dateSelectedを使って以下のように書けます。

日付の大小関係を比較するにはDateTimeValue関数で日付を数値に変換します。また、日付の計算にはDateAddまたはDateDiff関数を使います。日付の計算は特殊なので、使い方の例も見てみましょう。

Filter(MyCalendarEvents, 
    Not(
        Or(
            DateTimeValue (End) < DateTimeValue (_dateSelected),
            DateTimeValue (Start) >= DateTimeValue (DateAdd( _dateSelected, 1, Days))
        )
    )
)

否定Notで書くと一見してわかりにくいので、ド・モルガンの法則を使って書き換えておきます。「(AまたはB)の否定」は「(Aの否定)かつ(Bの否定)」に同値です。日本語で表現すれば、予定の終了日時が選択日の0時以降でありかつ予定の開始日時が選択日の24時よりも前ということです。日本語で表現しても、それっぽい感じがします。

Filter(MyCalendarEvents, 
        And(
            DateTimeValue (End) >= DateTimeValue (_dateSelected),
            DateTimeValue (Start) < DateTimeValue (DateAdd( _dateSelected, 1, Days))
        )
    )
)

これで当日にかかる予定で複数日にまたがるものを拾えるようになりました。

「カレンダー」シナリオでは、予定が入っている日付の下に小さい丸が表示されます。これの予定が入っている判定は前述の条件と同じく予定の開始日が日付と一致するです。前節で改変したように、複数日にまたがる予定がある場合に、予定がかかるすべての日に対して小さい丸を表示するようにしてみましょう。

小さい丸のVisibleプロパティは次のように指定されています。

/*Visible if calendar events are found on this day*/               
CountRows(Filter(MyCalendarEvents,
 DateValue(Text(Start)) = DateAdd(_firstDayInView,ThisItem.Value,Days)
)) > 0 
&& !Subcircle1.Visible 
&& Title2.Visible

予定の開始日時のDateValue(年月日)が、この日と同じ
かつSubcircle1(選択された日表示)がVisibleでない
かつTitle2 (日付) がVisibleである
という意味です。

1行目のCountRows関数に与えている当日の予定を抽出するFilter条件式を改変すれば目的を達成できそうです。
上の例から、カレンダーの一覧表示において、各日付はDateAdd(_firstDayInView, ThisItem.Value, Days)で表現できることがわかりました。これは各日付の0時を指す日時になります。予定がその日にかかるかを判定するには、予定の終了がその日の0時よりも後であって、かつ予定の開始が翌日0時よりも前であれば良いです。翌日0時は上の例にならってDateAdd(_firstDayInView, ThisItem.Value + 1, Days)と書けます。に予定がかかるかを判定したいので、基準となる日付は一覧の最初の日と最後の日です。したがって、Filter関数を次のように書き換えることになります。

/*Visible if calendar events are found on this day*/               
CountRows(Filter(MyCalendarEvents, 
        And(
            DateTimeValue (End) >= DateAdd(_firstDayInView, ThisItem.Value, Days),
            DateTimeValue (Start) < DateAdd(_firstDayInView, ThisItem.Value + 1, Days)
        )
    )
) > 0 
&& !Subcircle1.Visible 
&& Title2.Visible

以上でSharePointリストをソースとしたPower Appsカレンダー表示が完成です。

Power AppsのデータソースをSharePointリストだけでがんばる

追加出費なく使える範囲でPower Platformを使い倒すため、データソースをSharePointリストだけで頑張る方法を調べました。

Power Appsのフォームコントロールで「個人」型データを指定する

編集フォームでリストに書き込む

詳細な説明はリンク先を参照

フォームで個人型に対応する値をメールアドレスなどから構成する場合は、対応するカードのUpdateやDefaultを以下のように指定します。

{
 Claims: "i:0#.f|membership|" & User().Email,
 DisplayName: User().FullName
}

Patch関数でリストに書き込む

詳細な説明はリンク先を参照

Patch関数の更新で個人型のレコードを書き込む列名に対して、以下のような値を指定します。

Claimsにメールアドレスが正しく入ってれば、Displynameはじめほかの値は適切な値に置き換えてリストに保存される模様。

{
    Value:"",
    '@odata.type':"#Microsoft.Azure.Connectors.SharePoint.SPListExpandedUser",
    Claims:  "i:0#.f|membership|" & Lower(User().Email),
    Department: "",
    DisplayName: "",
    Email: "",
    JobTitle: "",
    Picture: ""        
  }

Power Appsで取得した画像をAutomateを経由せずにリストに格納する

詳細はリンク先を参照

画像をJSON形式で扱うことで、リストの複数行テキスト列に格納できます。この方法であれば、SharePoint内に画像ライブラリや画像の保存フォルダを用意する必要がありません。

画像を文字列で扱えるといっても、フォームコントロールを介してリストにデータを送ることはできません。代わりに、Patch関数またはCollect関数でSharePointリストにアイテムを追加します。

JSON形式で画像を扱うとき

JSON関数に画像を読み取るコントロールのphotoやimageプロパティを渡します。ここでは、グローバル変数ImageJsonにカメラコントロールCamera1で取得した画像を設定するコードを紹介します。

Set(
 ImageJson,
 JSON(Camera1.photo,IncludeBinaryData)
);

SharePointリストに投稿するとき

JSON形式の画像ImageJSONをSharePointリストの複数行テキスト列GAZOに格納します。Patch関数を使った場合は次のようになります。

Patch( リストの名前,
  Defaults( リストの名前 ),
 { Title: "タイトル列の値",
   GAZO: ImageJSON
  }
 )

JSON形式で格納された画像を表示するとき

複数行テキスト列の値を読むと、ダブルクォーテーションで囲まれた文字列が返ります。このダブルクォーテーションを除去する操作をします。画像コントロールなどのimageプロパティに以下のような設定を行います。
ここではリストの2番目のレコードのGAZO列の値を表示します。

Substitute( Index( リストの名前, 2).GAZO,"""","")

SharePointリストのアイテム数(行数)制限

リストのアイテム数にはいくつかの上限が存在します。リストを読み込むサービスによる制限もあり、注意が必要です。

明確な上限数ない注意点

PowerAppsで大量のデータを読み込ませると処理しきれずに動作が不安定になります。

明確な上限数・閾値

100

Power AutomateのSharePointコネクタ「複数の項目を取得する」アクションでは、既定の読み込み件数が100です。設定を変更すれば5000件まで行けます。詳しくはリンク先をあご覧ください。

500

Power Appsが読み込む件数は既定が500件です。設定変更で2000まで行けます。これ以上はうまく委任を使ってください。

2,000

PowerAppsで読み込める最大の件数です。これ以上の件数があるデータソースを使用する場合は、委任できる関数で2000件以下に絞って読み込む必要があります。

PowerAppsの初心者だと2000件以上のデータソースは扱えないと思っている方もいますので、委任の考え方を身に付けてこの壁を突破しましょう。

5,000

SharePointリストのビューが一度に処理できる上限です。リストの並べかえなどが適切に行えない場合があります。

5000以上のアイテム数が入ったリストを操作する場合は、委任とインデックス列を意識する必要があります。インデックス付の列の設定を忘れずに。

SharePointリストの5000件問題として有名な閾値ですが、真の上限ではありません。「委任とインデックス付列」を意識していれば大丈夫です。

30,000,000

SharePointリストに保存できるアイテム数の上限です。存在するアイテム数が3000万件を超えないようにサービスを設計しましょう。

3000万行のデータを手軽さが売りのSharePointリストでやるのかという観点はありますが、業務システムだと平気で5年10年使われることもありますので限界まで余裕を持っておきたいですね。