TeamsにはメッセージをTo Doにタスクを登録する機能はありますがPlannerに登録する機能はないので作ってみました。
※作業環境の都合で、Teamsのスクリーンショットが新しいチャネルエクスペリエンスと従来のチャネルエクスペリエンスが混在しております。
実現できること
- Teamsで選択したメッセージをPlannerプランにタスク登録する
実行イメージ
メッセージを選択してPlannerにタスクを登録します。
「タスク登録」をクリックします。
メッセージの件名・本文・メッセージへのリンクが引用された形でPlannerタスク登録アプリが開きます。登録先のグループ・プラン・バケットを選択し、日付・担当者を設定することでタスクを登録できます。
夏休みの自由研究(2) pic.twitter.com/dzHfBMk3rV
— Sasami (ささみ) (@sasami_axis) 2023年8月17日
必要なもの
- Microsoft Teams, Power Automate, Planner
- 職場または学校アカウントでの利用です
構成
今回はPower AutomateクラウドフローとPower Appsアプリの組み合わせです。クラウドフローだけでは作成先のプランやバケットを動的に選択することが難しかったため、クラウドフローからアプリを呼び出す形を採りました。
フロー全体図
フロー解説
1.トリガー 選択したメッセージの場合(V2)
Teamsでメッセージを選択してフローを起動するためこのトリガーを使用します。
2.作成-Url
PowerApps アプリの起動Urlにアプリに渡したいパラメータを結合しています。
アプリの起動Urlの取得
Power Apps → アプリ → アプリの「…」→詳細
WebリンクのUrlをコピーします
パラメータの生成
「選択したメッセージの場合」トリガーの出力から下記をパラメーターとして下記の式で生成しています。
- groupId:グループId
- subject:メッセージの件名
- bodyText:メッセージ本文(プレーンテキスト)
- messageUrl:メッセージのUrl
Concat( '&groupId=', encodeUriComponent(triggerBody()?['teamsFlowRunContext']?['channelData']?['team']?['aadGroupId']), '&subject=', encodeUriComponent(triggerBody()?['teamsFlowRunContext']?['messagePayload']?['subject']), '&bodyText=', encodeUriComponent(triggerBody()?['teamsFlowRunContext']?['messagePayload']?['body']?['plainText']), '&messageUrl=', encodeUriComponent(triggerBody()?['teamsFlowRunContext']?['messagePayload']?['linkToMessage']) )
3. Teamsのタスクモジュールで応答
生成したパラメータ付きUrlを開くボタンを表示するアダプティブカードを表示します。
{ "type": "AdaptiveCard", "$schema": "http://adaptivecards.io/schemas/adaptive-card.json", "version": "1.3", "body": [ { "type": "ColumnSet", "columns": [ { "type": "Column", "width": 1, "items": [ { "type": "Image", "url": "https://statics.teams.cdn.office.net/evergreen-assets/apps/teams_dev_app_largeimage.png", "size": "Large" } ] }, { "type": "Column", "width": 2, "items": [ { "type": "TextBlock", "text": "Plannerでタスクを作成", "wrap": true, "fontType": "Default", "size": "Medium" }, { "type": "TextBlock", "text": "タスク作成を続行するにはボタンをクリックしてください。", "wrap": true } ] } ] } ], "actions": [ { "type": "Action.OpenUrl", "url": "@{outputs('作成-url')}", "style": "positive", "title": "タスク登録" } ] }
フローは以上です。アプリに必要な情報を渡して起動するだけなのでシンプル構成になっています。
PowerApps Plannerタスク登録アプリの構成
アプリについては概要のみ記載します。
データ接続
「データの追加」→「データソースの選択」でPlanner と Office365グループ コネクタを追加します。
各コレクションは対応する入力コントロールのItemに設定します。
画面の構成
下図のような構成で画面を作成しています。
処理の概要
Screen1.OnVisible
"//起動パラメータ"の部分がPower Automateクラウドフローからの連携部分です。Param関数で渡された引数を取得し、各入力コントロールのDefaultに設定しています。
グループ・プラン・バケットのコントロールは動的に値が変わるので、パラメータが渡された場合のみ初期値を設定しています。
//担当者コレクションを初期化
UpdateContext({SelectedPersons:["",""]});
//グループコレクションを初期化 ClearCollect(
colAllTeams,
Sort(Office365グループ.ListGroups().value,displayName)
); //起動パラメータ UpdateContext({ paramGroupId:Param("groupId"), paramUrl:Param("messageUrl"), paramSubject:Param("subject"), paramBodyText:Param("bodyText") });
//パラメータありの場合
If(Len(paramGroupId)>0,
ClearCollect(
colAllPlans,
Sort(
Planner.ListGroupPlans(DropdownTeam.SelectedText.id).value,
title
)
); ClearCollect(
colTeamMembers,
Sort(
Office365グループ.ListGroupMembers(DropdownTeam.SelectedText.id).value,
displayName
)
); UpdateContext({ SelectedTeamId:DropdownTeam.SelectedText.id, SelectedPlanId:DropdownPlan.SelectedText.id });
//選択したチーム・プランのバケットを全取得しコレクション化 ClearCollect(
colAllBuckets,
Planner.ListBucketsV3(SelectedPlanId,SelectedTeamId).value
); //バケット名コレクションを作成 ClearCollect(colAllBucketNames, ShowColumns(colAllBuckets,"name") ); )
グループの選択(DropDownTeam.OnChange)
グループの選択状態が変わったら、プラン・グループメンバー・バケットのコレクションを再作成します。担当者の選択状態をリセットします。
//担当者選択状態をリセット UpdateContext({SelectedPersons:[""]}); Reset(ComboBoxPersons); //プラン・グループメンバーのコレクションを再作成 ClearCollect(colAllPlans,Sort(Planner.ListGroupPlans(DropdownTeam.SelectedText.id).value,title)); ClearCollect(colTeamMembers,Sort(Office365グループ.ListGroupMembers(DropdownTeam.SelectedText.id).value,displayName)); UpdateContext({ SelectedTeamId:DropdownTeam.SelectedText.id, SelectedPlanId:DropdownPlan.SelectedText.id }); //選択したチーム・プランのバケットを全取得 ClearCollect(colAllBuckets, Planner.ListBucketsV3(SelectedPlanId,SelectedTeamId).value ); //バケット名コレクションを作成 ClearCollect(colAllBucketNames, ShowColumns(colAllBuckets,"name") )
プランの選択(DropDownPlan.OnChange)
プランの選択状態が変わったら、バケットコレクションを再作成します。
UpdateContext({ SelectedTeamId:DropdownTeam.SelectedText.id, SelectedPlanId:DropdownPlan.SelectedText.id }); //選択したチーム・プランのバケットを全取得 ClearCollect(colAllBuckets, Planner.ListBucketsV3(SelectedPlanId,SelectedTeamId).value ); //バケット名コレクションを作成 ClearCollect(colAllBucketNames, ShowColumns(colAllBuckets,"name") )
バケット作成・選択の切り替え
トグルボタン(ToggleBucket)でバケットの新規作成 or 選択を切り替えています。
ListBoxBucket.Visible
Not(ToggleBucket.Value)
TextInputBucket.Visible と LabelBucketWarn.Visible
ToggleBucket.Value
バケット新規作成する場合の重複チェック(LabelBucketWarn.Text)
同名のバケットは見通しが悪くなる為重複しないようにチェックしています(個人的好み)
If(TextInputBucket.Text in colAllBucketNames, "同名のバケット存在する為作成できません", "" )
担当者の全選択ボタン(ButtonSelectAll.OnSelect) 選択解除ボタン(ButtonDeselectedAll.OnSelect)
SelectedPersons コレクションに選択グループの全メンバーをセットします。
SelectedPersons コレクションはCombBoxPersons.DefaultSelectedItemsに設定されている為、コンボボックスは全選択状態になります。
UpdateContext({
SelectedPersons:Sort(
Office365グループ.ListGroupMembers(
DropdownTeam.SelectedText.id).value,
displayName
)
}); Reset(ComboBoxPersons)
選択解除も同様です。
UpdateContext({SelectedPersons:[""]}); Reset(ComboBoxPersons)
作成ボタンの制御(ButtonCreate.DisplayMode)
下記の条件をチェックしいずれかに該当する場合は作成ボタンが押せないよう制御しています。
- グループが未選択
- プランが未選択
- (バケット新規作成の場合)バケット名が未入力
- (バケット新規作成の場合)バケット名が既存バケットと重複
- (既存バケット選択の場合)バケットが未選択
- タスク名が未入力
- 担当者が未選択
- 担当者に20人より多く選択済
//必須入力・選択チェック If(Or( Len(DropdownTeam.SelectedText.id)=0, Len(DropdownPlan.SelectedText.id)=0, And( ToggleBucket.Value = false, Len(ListBoxBucket.Selected.id)=0 ), And( ToggleBucket.Value = true, Or( Len(TextInputBucket.Text) = 0, TextInputBucket.Text in colAllBucketNames ) ), Len(TextInputTask.Text)=0, Len(
Trim(
Concat(
ComboBoxPersons.SelectedItems,ThisRecord.displayName,
""
)
)
)=0, CountRows(SelectedPersons)>20 ), DisplayMode.Disabled, DisplayMode.Edit )
作成ボタン タスク作成(ButtonCreate.OnSelect)
入力・選択した内容でPlannerプランにタスクを登録します。
//入力値を変数に格納
UpdateContext({ SelectedTeamId:DropdownTeam.SelectedText.id, SelectedPlanId:DropdownPlan.SelectedText.id, SelectedBucketId:ListBoxBucket.Selected.id, NewBucketName:TextInputBucket.Text, NewTaskName:TextInputTask.Text, NewTaskMemo:TextInputMemo.Text, NewTaskStart:DatePickerStart.SelectedDate, NewTaskLimit:DatePickerLimit.SelectedDate, NewTaskUrl:TextInputUrl.Text, NewTaskAssignments:Concat(SelectedPersons,id,";") }); //バケット新規作成 If(ToggleBucket.Value=true, // バケットを作成 UpdateContext({
NewBucket:Planner.CreateBucketV2(
NewBucketName,
SelectedTeamId,
SelectedPlanId
)
}); //バケットIDを取得 UpdateContext({SelectedBucketId:First(Table(NewBucket)).id}) ); //タスク登録 UpdateContext({ NewTask:Planner.CreateTaskV3( SelectedTeamId, SelectedPlanId, NewTaskName, { bucketId:SelectedBucketId, startDateTime:NewTaskStart, dueDateTime:NewTaskLimit, assignments:NewTaskAssignments } ) }); //タスク詳細にメモ追加 If(Len(NewTaskMemo)>0, Planner.UpdateTaskDetailsV2( NewTask.id, { description: NewTaskMemo } ) ); //タスク詳細にURL追加 If(Len(NewTaskUrl)>0, Planner.UpdateTaskDetailsV2( NewTask.id, { references: Table( { alias:"リンク", resourceLink:NewTaskUrl, type:ParseJSON(JSON("Other")) } ) } ) ); Notify( "タスクを作成しました。", NotificationType.Information, 0 );
//バケットコレクションを初期化
ClearCollect(colAllBuckets, Planner.ListBucketsV3(SelectedPlanId,SelectedTeamId).value ); ClearCollect(colAllBucketNames, ShowColumns(colAllBuckets,"name") ); //入力アイテムを初期化
UpdateContext({
paramUrl:"",
paramSubject:"",
paramBodyText:"",
SelectedPersons:[""]
});
Reset(TextInputBucket);
Reset(TextInputTask); Reset(TextInputMemo); Reset(DatePickerStart); Reset(DatePickerLimit); Reset(TextInputUrl); UpdateContext({SelectedPersons:[""]}); Reset(ComboBoxPersons);
以上です。
今後の課題
- アプリの作り込みがまだまだ
- 担当者の選択が煩雑なところ
- 入力コントロールの依存構成ができることをアプリを作ってから気づいたので、もっとシンプルにできるかもしれません
- 指定対象リリース(Teamsパブリックプレビュー)のユーザーではメッセージの件名が取得できない
- 通常リリースのユーザーでは問題ありませんのでTeamsの不具合と思われます
参考にしたページ