Skip to content

Commit ab01aad

Browse files
johnatricorocksJohn mcconnachie
andauthored
Transition to use Microsoft Teams Adaptive Cards (#35)
* adaptive card updates started * updates * updates * updated to array method and tests * adpative cards update * update notes --------- Co-authored-by: John mcconnachie <[email protected]>
1 parent 71f5610 commit ab01aad

14 files changed

+1401
-974
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
All notable changes to `microsoft-teams` will be documented in this file
44

5+
Adaptive Cards
6+
57
## 1.1.4 - 2023-01-25
68
Update Readme to support Laravel 10.x
79

README.md

Lines changed: 122 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,33 @@
77
This package makes it easy to send notifications using [Microsoft Teams](https://products.office.com/en-US/microsoft-teams/group-chat-software) with Laravel 5.5+, 6.x, 7.x, 8.x, 9.x, 10.x, 11.x and 12.x
88

99
```php
10-
return MicrosoftTeamsMessage::create()
11-
->to(config('services.microsoft_teams.sales_url'))
12-
->type('success')
10+
return MicrosoftTeamsAdaptiveCard::create()
11+
->to(config('services.microsoft_teams.webhook_url'))
1312
->title('Subscription Created')
14-
->content('Yey, you got a **new subscription**. Maybe you want to contact him if he needs any support?')
15-
->button('Check User', 'https://foo.bar/users/123');
13+
->content([
14+
TextBlock::create()
15+
->setText('Yey, you got a **new subscription**. Maybe you want to contact him if he needs any support?')
16+
->setFontType('Monospace')
17+
->setWeight('Bolder')
18+
->setSize('ExtraLarge')
19+
->setSpacing('ExtraLarge')
20+
->setStyle('Heading')
21+
->setHorizontalAlignment('Center')
22+
->setSeparator(true),
23+
FactSet::create()
24+
->setSpacing('ExtraLarge')
25+
->setSeparator(true)
26+
->setFacts([
27+
Fact::create()->setTitle('Subscription Created')->setValue('Today'),
28+
])
29+
])
30+
->actions([
31+
ActionOpenUrl::create()
32+
->setMode('Primary')
33+
->setStyle('Positive')
34+
->setTitle('Contact Customer')
35+
->setUrl("https://foo.bar"),
36+
]);
1637
```
1738
## Contents
1839

@@ -23,8 +44,10 @@ return MicrosoftTeamsMessage::create()
2344
- [Setting up the MicrosoftTeams service](#setting-up-the-microsoftteams-service)
2445
- [Usage](#usage)
2546
- [On-Demand Notification Usage](#on-demand-notification-usage)
26-
- [Available Message methods](#available-message-methods)
27-
- [Sections](#sections)
47+
- [Available Adaptive Card methods](#available-adaptive-cards-methods)
48+
- [Content Block Types](#content-block-types)
49+
- [Action Types](#action-types)
50+
2851
- [Changelog](#changelog)
2952
- [Testing](#testing)
3053
- [Security](#security)
@@ -52,7 +75,7 @@ Next, if you're using Laravel _without_ auto-discovery, add the service provider
5275

5376
### Setting up the Connector
5477

55-
Please check out [this](https://docs.microsoft.com/en-gb/microsoftteams/platform/webhooks-and-connectors/how-to/add-incoming-webhook#add-an-incoming-webhook-to-a-teams-channel) for setting up and adding a webhook connector to your Team's channel. Basic Markdown is supported, please also check out the [message card reference article](https://docs.microsoft.com/en-us/outlook/actionable-messages/message-card-reference#httppost-action) which goes in more detail about the do's and don'ts.
78+
Please check out [this](https://support.microsoft.com/en-us/office/create-incoming-webhooks-with-workflows-for-microsoft-teams-8ae491c7-0394-4861-ba59-055e33f75498) for setting up and adding a webhook connector to your Team's channel. Please also check out the [adaptive card reference](https://learn.microsoft.com/en-us/adaptive-cards/) which goes in more detail about adaptive cards.
5679

5780
### Setting up the MicrosoftTeams service
5881

@@ -98,12 +121,33 @@ class SubscriptionCreated extends Notification
98121

99122
public function toMicrosoftTeams($notifiable)
100123
{
101-
return MicrosoftTeamsMessage::create()
102-
->to(config('services.microsoft_teams.sales_url'))
103-
->type('success')
124+
return MicrosoftTeamsAdaptiveCard::create()
125+
->to(config('services.microsoft_teams.webhook_url'))
104126
->title('Subscription Created')
105-
->content('Yey, you got a **new subscription**. Maybe you want to contact him if he needs any support?')
106-
->button('Check User', 'https://foo.bar/users/123');
127+
->content([
128+
TextBlock::create()
129+
->setText('Yey, you got a **new subscription**. Maybe you want to contact him if he needs any support?')
130+
->setFontType('Monospace')
131+
->setWeight('Bolder')
132+
->setSize('ExtraLarge')
133+
->setSpacing('ExtraLarge')
134+
->setStyle('Heading')
135+
->setHorizontalAlignment('Center')
136+
->setSeparator(true),
137+
FactSet::create()
138+
->setSpacing('ExtraLarge')
139+
->setSeparator(true)
140+
->setFacts([
141+
Fact::create()->setTitle('Subscription Created')->setValue('Today'),
142+
])
143+
])
144+
->actions([
145+
ActionOpenUrl::create()
146+
->setMode('Primary')
147+
->setStyle('Positive')
148+
->setTitle('Contact Customer')
149+
->setUrl("https://foo.bar"),
150+
]);
107151
}
108152
}
109153
```
@@ -127,36 +171,72 @@ Notification::route(MicrosoftTeamsChannel::class,null)
127171
->notify(new SubscriptionCreated());
128172
```
129173

174+
### Available Adaptive Card methods
130175

131-
### Available Message methods
132-
| **Method** | **Description** |
133-
|------------|-----------------|
134-
| `to(string $webhookUrl)` | Recipient's webhook url |
135-
| `title(string $title)` | Title of the message |
136-
| `summary(string $summary)` | Summary of the message |
137-
| `type(string $type)` | Type which is used as theme color (any valid hex code or one of: primary|secondary|accent|error|info|success|warning) |
138-
| `content(string $content)` | Content of the message (Markdown supported) |
139-
| `button(string $text, string $url = '', array $params = [])` | Text and url of a button. Wrapper for an potential action |
140-
| `action(string $text, $type = 'OpenUri', array $params = [])` | Text and type for a potential action. Further params can be added depending on the action. For more infos about different types check out [this link](https://docs.microsoft.com/en-us/outlook/actionable-messages/message-card-reference#actions). |
141-
| `options(array $options, $sectionId = null)` | Add additional options to pass to the message payload object |
142-
143-
#### Sections
144-
It is possible to define one or many sections inside a message card. The following methods can be used within a section
145-
| **Method** | **Description** |
146-
|------------|-----------------|
147-
| `addStartGroupToSection($sectionId = 'standard_section')` | Add a startGroup property which marks the start of a logical group of information |
148-
| `activity(string $activityImage = '', string $activityTitle = '', string $activitySubtitle = '', string $activityText = '', $sectionId = 'standard_section')` | Add an activity to a section |
149-
| `fact(string $name, string $value, $sectionId = 'standard_section')` | Add a fact to a section (Supports Markdown) |
150-
| `image(string $imageUri, string $title = '', $sectionId = 'standard_section')` | Add an image to a section |
151-
| `heroImage(string $imageUri, string $title = '', $sectionId = 'standard_section')` | Add a hero image to a section |
152-
153-
Additionally the title, content, button and action can be also added to a section through the optional `params` value:
154-
| **Method** | **Description** |
155-
|------------|-----------------|
156-
| `title(string $title, array $params = ['section' => 'my-section'])` | Title of the message and add it to `my-section` |
157-
| `content(string $content, array $params = ['section' => 'my-section'])` | Content of the message and add it to `my-section` (Markdown supported) |
158-
| `button(string $text, string $url = '', array $params = ['section' => 'my-section'])` | Text and url of a button and add it to `my-section` |
159-
| `action(string $text, $type = 'OpenUri', array $params = ['section' => 'my-section'])` | Text and type of an potential action and add it to `my-section` |
176+
- `create()`: Static factory method to create a new instance of the MicrosoftTeamsAdaptiveCard.
177+
- `to(string $webhookUrl)`: Sets the recipient's webhook URL. Required for sending notifications.
178+
- `title(string $title)`: Sets the title of the adaptive card with appropriate text styling (heading style, bold weight, and large size).
179+
- `content(array $contentBlocks)`: Adds content blocks to the adaptive card body. Accepts an array of content block objects like TextBlock, FactSet, Icon, etc.
180+
- `actions(array $actions)`: Adds action buttons to the adaptive card. Accepts an array of action objects like ActionOpenUrl.
181+
- `getWebhookUrl()`: Returns the currently set webhook URL.
182+
- `toNotGiven()`: Checks if the webhook URL has been provided. Returns true if no webhook URL is set.
183+
- `toArray()`: Returns the complete payload as an associative array.
184+
185+
### Content Block Types
186+
You can use various content block types in the content() method:
187+
188+
#### `TextBlock::create()`: Creates a text block with options for:
189+
190+
- `setText(string $text)`: Sets the text content (supports markdown formatting)
191+
- `setFontType(string $fontType)`: Sets the font type (e.g., 'Default', 'Monospace')
192+
- `setWeight(string $weight)`: Sets text weight (e.g., 'Lighter', 'Default', 'Bolder')
193+
- `setSize(string $size)`: Sets text size (e.g., 'Small', 'Default', 'Medium', 'Large', 'ExtraLarge')
194+
- `setSpacing(string $spacing)`: Sets spacing around the element (e.g., 'None', 'Small', 'Default', 'Medium', 'Large', 'ExtraLarge', 'Padding')
195+
- `setStyle(string $style)`: Sets text style (e.g., 'Default', 'ColumnHeader', 'Heading')
196+
- `setHorizontalAlignment(string $alignment)`: Sets text alignment (e.g., 'Left', 'Center', 'Right')
197+
- `setSeparator(bool $separator)`: Adds a separator line above the element when true
198+
- `setWrap(bool $wrap)`: Sets whether text should wrap or not
199+
- `setColor(string $color)`: Sets text color (e.g., 'Default', 'Dark', 'Light', 'Accent', 'Good', 'Warning', 'Attention')
200+
- `setIsSubtle(bool $isSubtle)`: Sets whether text should be subtle or not
201+
- `setMaximumLines(int $maxLines)`: Sets the maximum number of lines for the text block
202+
- `toArray()`: Returns the TextBlock properties as an array
203+
204+
205+
#### `Icon::create()`: Creates an icon with options for:
206+
207+
- `setName(string $name)`: Sets the name of the icon
208+
- `setColor(string $color)`: Sets icon color (e.g., 'Default', 'Dark', 'Light', 'Accent', 'Good', 'Warning', 'Attention')
209+
- `setSpacing(string $spacing)`: Sets spacing around the icon (e.g., 'None', 'Small', 'Default', 'Medium', 'Large', 'ExtraLarge', 'Padding')
210+
- `setSeparator(bool $separator)`: Adds a separator line above the element when true
211+
- `setSize(string $size)`: Sets icon size (e.g., 'xxSmall', 'xSmall', 'Small', 'Standard', 'Medium', 'Large', 'xLarge', 'xxLarge')
212+
- `setStyle(string $style)`: Sets icon style (e.g., 'Regular', 'Filled')
213+
- `setHorizontalAlignment(string $alignment)`: Sets icon alignment (e.g., 'Left', 'Center', 'Right')
214+
- `toArray()`: Returns the Icon properties as an array
215+
216+
#### `FactSet::create()`: Creates a set of facts with options for:
217+
218+
- `setFacts(array $facts)`: Sets an array of Fact objects
219+
- `setSpacing(string $spacing)`: Sets spacing around the element (e.g., 'None', 'Small', 'Default', 'Medium', 'Large', 'ExtraLarge', 'Padding')
220+
- `setSeparator(bool $separator)`: Adds a separator line above the element when true
221+
- `toArray()`: Returns the FactSet properties as an array
222+
223+
224+
#### `Fact::create()`: Creates a single fact (key-value pair) with methods:
225+
226+
- `setTitle(string $title)`: Sets the fact's title/key
227+
- `setValue(string $value)`: Sets the fact's value
228+
- `toArray()`: Returns the Fact properties as an array
229+
230+
### Action Types
231+
You can use various action types in the actions() method:
232+
233+
#### `ActionOpenUrl::create()`: Creates a button that opens a URL with options for:
234+
235+
- `setTitle(string $title)`: Sets the button text
236+
- `setUrl(string $url)`: Sets the URL to open when clicked
237+
- `setStyle(string $style)`: Sets button style (e.g., 'Default', 'Positive', 'Destructive')
238+
- `setMode(string $mode)`: Sets the interaction mode (e.g., 'Primary', 'Secondary')
239+
- `toArray()`: Returns the ActionOpenUrl properties as an array
160240

161241
## Changelog
162242

UPGRADE.md

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
# Upgrade Guide
2+
3+
## Migrating from Message Cards to Adaptive Cards
4+
5+
### Overview
6+
Microsoft is deprecating **O365 Connectors** within Microsoft Teams, which means **Message Cards** are now considered legacy and will no longer be supported. As a result, this package has been updated to use **Adaptive Cards** instead.
7+
8+
With this major update, you will need to:
9+
#### Outside Your Application
10+
- **Create new incoming webhooks using Workflows for Microsoft Teams** (O365 Connectors are no longer valid).
11+
12+
#### Inside Your Application
13+
- **Migrate from Message Cards to Adaptive Cards** for Teams notifications.
14+
- **Adjust your message formatting** (Adaptive Cards use structured blocks instead of Markdown).
15+
16+
### Breaking Changes
17+
- **Message Cards are no longer supported**
18+
- **O365 Connectors cannot be used**; users must create new webhooks with Workflows
19+
- **Adaptive Cards require structured content blocks** instead of Markdown formatting
20+
21+
### Upgrade Steps
22+
23+
#### 1. Create a New Webhook
24+
Since O365 Connectors are being deprecated, you must create a new incoming webhook using Microsoft Teams Workflows:
25+
26+
[Official Microsoft Guide](https://support.microsoft.com/en-us/office/create-incoming-webhooks-with-workflows-for-microsoft-teams-8ae491c7-0394-4861-ba59-055e33f75498)
27+
28+
Once created, update your `.env` file with the new webhook URL:
29+
30+
```env
31+
MICROSOFT_TEAMS_NOTIFICATION_WEBHOOK_URL=https://your-new-webhook-url
32+
```
33+
34+
Update `config/services.php` to reference the new webhook:
35+
36+
```php
37+
'microsoft_teams' => [
38+
'notification_webhook_url' => env('MICROSOFT_TEAMS_NOTIFICATION_WEBHOOK_URL'),
39+
]
40+
```
41+
42+
#### 2. Replace Message Cards with Adaptive Cards
43+
##### Before (Message Cards):
44+
45+
```php
46+
return MicrosoftTeamsMessage::create()
47+
->to(config('services.microsoft_teams.notification_webhook_url'))
48+
->type('success')
49+
->title('Subscription Created')
50+
->content('Yey, you got a **new subscription**. Maybe you want to contact him if he needs any support?')
51+
->button('Check User', 'https://foo.bar/users/123');
52+
```
53+
54+
##### After (Adaptive Cards):
55+
56+
```php
57+
return MicrosoftTeamsAdaptiveCard::create()
58+
->to(config('services.microsoft_teams.notification_webhook_url'))
59+
->title('Subscription Created')
60+
->content([
61+
TextBlock::create()
62+
->setText('Yey, you got a **new subscription**. Maybe you want to contact him if he needs any support?')
63+
->setWeight('Bolder')
64+
->setSize('Large'),
65+
])
66+
->actions([
67+
ActionOpenUrl::create()
68+
->setTitle('Check User')
69+
->setUrl('https://foo.bar/users/123'),
70+
]);
71+
```
72+
73+
### Additional Resources
74+
- **Microsoft Announcement on O365 Connector Deprecation:** [Read here](https://devblogs.microsoft.com/microsoft365dev/retirement-of-office-365-connectors-within-microsoft-teams/)
75+
- **Adaptive Cards Documentation:** [Visit AdaptiveCards.io](https://adaptivecards.io/)
76+

src/Actions/ActionOpenUrl.php

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
<?php
2+
3+
namespace NotificationChannels\MicrosoftTeams\Actions;
4+
5+
class ActionOpenUrl
6+
{
7+
/** @var string Property Type */
8+
protected $type = 'Action.OpenUrl';
9+
10+
/** @var string Title of the Action */
11+
protected $title;
12+
13+
/** @var string $mode Mode of the Action. (Primary, Secondary) */
14+
protected $mode;
15+
16+
/** @var string $mode Style of the Action Button. (Positive, Destructive) */
17+
protected $style;
18+
19+
/** @var string $url URL the Action goes to. */
20+
protected $url;
21+
22+
/**
23+
* @return self
24+
*/
25+
public static function create(): self
26+
{
27+
return new self();
28+
}
29+
30+
/**
31+
* Set the title.
32+
*
33+
* @param string $title
34+
*
35+
* @return ActionOpenUrl
36+
*/
37+
public function setTitle(string $title) : self
38+
{
39+
$this->title = $title;
40+
return $this;
41+
}
42+
43+
/**
44+
* Set the mode.
45+
*
46+
* @param string $mode
47+
*
48+
* @return ActionOpenUrl
49+
*/
50+
public function setMode(string $mode) : self
51+
{
52+
$this->mode = $mode;
53+
return $this;
54+
}
55+
56+
/**
57+
* Set the style.
58+
*
59+
* @param string $style
60+
*
61+
* @return ActionOpenUrl
62+
*/
63+
public function setStyle(string $style) : self
64+
{
65+
$this->style = $style;
66+
return $this;
67+
}
68+
69+
/**
70+
* Set the url.
71+
*
72+
* @param string $url
73+
*
74+
* @return ActionOpenUrl
75+
*/
76+
public function setUrl(string $url) : self
77+
{
78+
$this->url = $url;
79+
return $this;
80+
}
81+
82+
/**
83+
* Returns Action properties as an array.
84+
*
85+
* @return array
86+
*/
87+
public function toArray() : array
88+
{
89+
return [
90+
'type' => $this->type,
91+
'title' => $this->title,
92+
'mode' => $this->mode,
93+
'style' => $this->style,
94+
'url' => $this->url
95+
];
96+
}
97+
}

0 commit comments

Comments
 (0)