ActionCard.ps1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 |
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '', Scope='Function', Target='New*', Justification='New- Commands create definitions but do not change system state')] Param() Function Convert-ColorToHex { <# .Synopsis Turns a [System.Drawing.Color] into a hex string e.g. Red to ff0000 #> [cmdletbinding()] [outputType([String])] Param ( [Alias("Colour")] [System.Drawing.Color]$Color ) #return 2 hex digits for red, green and blue components "{0:x2}{1:x2}{2:x2}" -f $Color.r, $Color.g, $Color.b } Function New-CardImage { [cmdletbinding()] [Alias('CardImage')] [OutPuttype([Hashtable])] Param ( [parameter(Position=0, Mandatory=$true)] [alias('URI')] [string]$ImageURI, [string]$Title = 'Image' ) @{image = 'uri' ; title='title'} } Function New-CardSection { [CmdletBinding()] [alias('cardsection')] [OutputType([System.Collections.Specialized.OrderedDictionary])] Param ( [String]$Title = '' , [String]$ActivityImage = '' , [String]$ActivityTitle = '' , [String]$ActivitySubtitle = '' , [String]$ActivityText = '' , [bool]$StartGroup = $false , $Images ,# = @( @{image = 'uri1' ; title='title1'}, @{image = 'uri' ; title='title'}) , $Facts ,# = @( @{name1='value'}, @{name2='value2'}) , $HeroimageURI = 'HeroImageuri' , $HeroimageTitle = 'HeroImagetitle' ) $section = [ordered]@{ title = $Title startGroup = $StartGroup activityImage = $ActivityImage activityTitle = $ActivityTitle activitySubtitle = $ActivitySubtitle activityText = $ActivityText text = $Text } if ($HeroimageURI) { $heroimage = New-CardImage -ImageURI $HeroimageURI -title $HeroimageTitle $section['heroImage'] = $heroimage } if ($Facts) { $section['facts'] = $Facts } if ($Images) { $section['images'] = $Images} return $section } Function New-CardInput { <# .synopsis Creates the UI imput controls for message cards .Example > New-CardInput -InputType MultiLineText -ID 'Feedback' -Title "Let us know what you think" #Creates as a multi-line text box with the the ID "feedback" and a title #> [CmdletBinding()] [alias('cardinput')] Param ( [Parameter(ParameterSetName='SingleChoice',Mandatory=$true,Position=0)] [ValidateSet('Text','MultiLineText','DateOnly','DateTime')] [Alias('Type')] [string]$InputType, [Parameter(ParameterSetName='SingleChoice')] [int]$MaxLength, [Parameter(ParameterSetName='MultiChoice',Mandatory=$true)] [string[]]$Choices, [Parameter(ParameterSetName='MultiChoice')] [switch]$MultiSelect, [Parameter(ParameterSetName='SingleChoice',Mandatory=$true,Position=1)] [Parameter(ParameterSetName='MultiChoice',Mandatory=$true,Position=0)] [string]$ID = 'feedback' , [string]$Title, [string]$DefaultValue, [switch]$IsRequired ) if ($InputType -in ('Text','MultilineText')) { $InputControl = [ordered]@{ '@type' = 'TextInput' 'id' = $ID 'isRequired' = [bool]$IsRequired 'title' = $Title } if ($InputType -eq 'MultilineText') { $InputControl['isMultiline'] = $true } if ($MaxLength) { $InputControl['maxLength'] = $MaxLength } } elseif ($InputType -in ('DateTime','Date') ) { $InputControl = [ordered]@{ '@type' = 'DateInput' 'id' = $ID 'isRequired' = [bool]$IsRequired 'title' = $Title } if ($InputType -eq 'DateTime') {InputControl['includeTime'] = $true} } elseif ($Choices) { $InputControl = [ordered]@{ '@type' = 'MultichoiceInput' 'id' = $ID 'isRequired' = [bool]$IsRequired 'title' = $Title 'choices' = @() } if ($MultiSelect) {$InputControl['isMultiSelect'] = $true} foreach ($c in $choices) { if ($c -is [string]) { $InputControl['choices'] += @{'display' = $c ; 'value' = $c} } elseif ($c.display -and $c.value) { $InputControl['choices'] += @{'display' = $c.display ; 'value' = $c.value} } else {throw 'Invalid value for a choice.'; return} } } if ($DefaultValue) { $InputControl['value'] = $DefaultValue } return $InputControl } Function New-CardActionHttpPost { <# .Synopsis Creates an Http Post action for a message card .Description See https://docs.microsoft.com/en-gb/outlook/actionable-messages/message-card-reference#httppost-action .Example New-CardActionHttpPost -Name 'Send Feedback' -Target 'http://feedback.contoso.com' -Primary Creates a button for a form which contains which will post to the URL. need more params to be useful! #> [cmdletbinding()] [Alias("HttpPostAction")] param( [Parameter(Mandatory=$true,Position=0)] $Name , [Parameter(Mandatory=$true,Position=1)] $Target , [string[]]$Headers , $Body , [ValidateSet('application/json', 'application/x-www-form-urlencoded')] $ContentType, [switch]$Primary ) $action = [ordered]@{ '@type' = 'HttpPOST' 'name' = $Name 'target' = $target } if ($Primary) {$action['isPrimary'] = $true} if ($Headers) {$action['headers'] = $Headers} if ($Body) {$action['body'] = $Body} if ($ContentType) {$action['bodyContentType'] = $ContentType} return $action } Function New-CardActionOpenUri { <# .Synopsis Creates a button on a message card to open a link .Example New-CardActionOpenUri -Name 'Learn more' -Targets 'https://docs.microsoft.com/outlook/actionable-messages' This creates an action which a appers as button on the card [Learn More] clicking it opens the link #> [cmdletbinding()] [Alias("OpenUriAction")] param( [Parameter(Mandatory=$true,Position=0)] $Name , [Parameter(Mandatory=$true,Position=1)] $Targets , [switch]$Primary ) $Action = [ordered]@{ '@type' = 'OpenUri' 'name' = $Name 'targets' = @() } if ($Primary) {$Action['isPrimary']= $true} foreach ($t in $Targets) { if ($t -is [string]) { $Action['targets'] += @{'os' = 'default' ; 'uri' = $t} } elseif ($t.os -and $t.uri) { $Action['targets'] += @{'os' = $t.os ; 'uri' = $t.uri} } else {throw 'Invalid value for a target.'; return} } return $action } Function New-CardActionCard { <# .Synopsis Creates an "Action card" action for a message card .Description Actions are presented on the card as buttons the user can click For an "action card" action this reveals a 'sub-card' with input controls and action buttons. The buttons must be Http posts or Open URI types, they can't be nest action cards. .Example > >$inputs = @() >$inputs += New-CardInput ... >$actions = New-CardActionHttpPost -Name 'Send Feedback' -Target 'http://....' >$actioncard = New-CardActionCard -Name 'Send Feedback' -Inputs $inputs -Actions $actions This example starts by creating the input fields for the Action card It the definies a single action - the both these steps have been truncated for brevity The final step creates the action card. This will then be passed in the Actions parameter for New-Message card. The post action will usually need to send a some of the input in the body of the post see under 'Input value substitution' in https://docs.microsoft.com/en-gb/outlook/actionable-messages/message-card-reference #> [cmdletbinding()] [Alias("CardAction")] param( [Parameter(Mandatory=$true,Position=0)] $Name , $Inputs, $Actions ) [ordered]@{ '@type' = 'ActionCard' 'name' = $Name 'inputs' = @() + $Inputs 'actions' = @() + $Actions } } Function New-MessageCard { <# .Synopsis Creates a message card and either posts to a webhook or returns it for examination/tweaking. .Example > >new-messageCard -WebHookURI $hookUri -Title 'From powershell to teams using webhooks' -Text @' **James** did a _crane job_ on the logo! '@ Creates a simple card with a title and text using mark down to display a logo. This card is posted to the Webhook in $webhooURI. .Description See https://docs.microsoft.com/en-gb/outlook/actionable-messages/message-card-reference #> [cmdletbinding()] [Alias("MessageCard")] param( #The title for the card. The font for this is fixed. [string]$Title = 'Visit the Outlook Dev Portal' , #The body text for the card. This supports markdown, which you can use to include images. [String]$Text = '', #Short version of the text. [string]$Summary = '', [system.drawing.color]$Themecolor, $ThemeColorHex ,# ='0072C6', $Actions , [switch]$AsHashTable, [String]$WebHookURI ) #Build the card as a HashTable. Make it ordered when we connvert to JSON the items don't look in a strange order $CardSettings = [ordered]@{ '@context' = 'https://schema.org/extensions' '@type' = 'MessageCard' 'title' = $Title 'text' = $Text 'summary' = $Summary } #Add optional items color is a hex string but we'll take and convert system colors. if ($ThemeColorHex) {$cardSettings['themeColor'] = $ThemeColorHex} elseif ($Themecolor) {$cardSettings['themeColor'] = Convert-ColorToHex -Color $Themecolor} if ($Actions) {$cardSettings['potentialAction'] = @() + $Actions} #And either post to a webhook or return the results. if ($AsHashTable) {return $cardSettings} elseif ($WebHookURI) { Invoke-RestMethod -Method Post -Uri $WebHookURI -ContentType "application/json" -Body (ConvertTo-Json $cardSettings -Depth 99) } else {ConvertTo-Json $cardSettings -Depth 99} } <# Mobula's web hook: to avoid uploading it use git update-index --skip-worktree .\ActionCard.ps1 to commit any changes remove this part and do git update-index --no-skip-worktree .\ActionCard.ps1 $hookUri = 'https://outlook.office.com/webhook/8c4e1893-63bc-463b-b31e-abe0243c54fa@e6af5578-6d03-49e0-af3b-383cf5ec0b5f/IncomingWebhook/8c241937656d49c0a4d8446cfc3df21c/12ae949b-a88b-4d53-ab12-fd6542ddfb45' #> ############## Example #################### # Your Web hook here # $hookUri = 'https://outlook.office.com/webhook/<<teamGuid>>@<<org GUID>>/IncomingWebhook/<<id>>/<<creator guid>>' <# First we can use these with the commands being written out in max-verbose. and storing in variables $inputs = New-CardInput -InputType MultiLineText -ID 'Feedback' -Title "Let us know what you think" $actions = New-CardActionHttpPost -Name 'Send Feedback' -Target 'http://....' -Primary $actioncard = New-CardActionCard -Name 'Send Feedback' -Inputs $inputs -Actions $actions $link = New-CardActionOpenUri -Name 'Learn more' -Targets 'https://docs.microsoft.com/outlook/actionable-messages' $actions = @($link,$actionCard) $json = New-MessageCard -Actions $actions -Title 'Visit the Outlook Dev Portal' -Text 'Click **Learn More** to learn more about Actionable Messages!' #The same but using aliases and being terse with parameters. $inputs = Cardinput MultiLineText feedback -Title "Let us know what you think" $actions = HttpPostAction 'Send Feeback' 'http://....' -Primary $actioncard = CardAction 'Send Feedback' -Inputs $inputs -Actions $actions $link = OpenUriAction 'Learn more' 'https://docs.microsoft.com/outlook/actionable-messages' $json = MessageCard 'Visit the Outlook Dev Portal' 'Click **Learn More** to learn more about Actionable Messages!' -Actions $link,$actioncard #And the same again but with attitude of "We don't need no stinking variables" MessageCard -Title 'Visit the Outlook Dev Portal' -Text @' **James** did a _crane job_ on the logo! '@ -Actions @( (CardAction "Feedback" -Inputs (Cardinput MultiLineText feedback -Title "Let us know, what do you think?") ` -Actions (HttpPostAction 'Send us feedback' 'http://....' -Primary) ) , (OpenUriAction 'Learn more' 'https://docs.microsoft.com/outlook/actionable-messages') ) | clip # paste into https://messagecardplayground.azurewebsites.net/ #> |