Skip to content

Commit ef813f1

Browse files
Merge pull request #220 from matomo-org/PG-4671-msteams-alert
Adds ability to send alerts to MicrosoftTeams Channel, #PG-4671
2 parents 203d4e5 + b0cf86d commit ef813f1

File tree

18 files changed

+162
-70
lines changed

18 files changed

+162
-70
lines changed

API.php

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -130,13 +130,14 @@ public function getAlerts($idSites, $ifSuperUserReturnAllAlerts = false)
130130
* @param bool|string $reportValue
131131
* @param array $reportMediums
132132
* @param string $slackChannelID
133+
* @param string $msTeamsWebhookUrl
133134
* @return int ID of new Alert
134135
*/
135-
public function addAlert($name, $idSites, $period, $emailMe, $additionalEmails, $phoneNumbers, $metric, $metricCondition, $metricValue, $comparedTo, $reportUniqueId, $reportCondition = false, $reportValue = false, array $reportMediums = [], string $slackChannelID = '')
136+
public function addAlert($name, $idSites, $period, $emailMe, $additionalEmails, $phoneNumbers, $metric, $metricCondition, $metricValue, $comparedTo, $reportUniqueId, $reportCondition = false, $reportValue = false, array $reportMediums = [], string $slackChannelID = '', string $msTeamsWebhookUrl = '')
136137
{
137138
$idSites = Site::getIdSitesFromIdSitesString($idSites);
138139

139-
$this->checkAlert($idSites, $name, $period, $emailMe, $additionalEmails, $phoneNumbers, $slackChannelID, $metricCondition, $metric, $comparedTo, $reportCondition, $reportUniqueId, $reportMediums);
140+
$this->checkAlert($idSites, $name, $period, $emailMe, $additionalEmails, $phoneNumbers, $slackChannelID, $msTeamsWebhookUrl, $metricCondition, $metric, $comparedTo, $reportCondition, $reportUniqueId, $reportMediums);
140141

141142
$name = Common::unsanitizeInputValue($name);
142143
$login = Piwik::getCurrentUserLogin();
@@ -148,7 +149,7 @@ public function addAlert($name, $idSites, $period, $emailMe, $additionalEmails,
148149

149150
$metricValue = Common::forceDotAsSeparatorForDecimalPoint((float)$metricValue);
150151

151-
return $this->getModel()->createAlert($name, $idSites, $login, $period, $emailMe, $additionalEmails, $phoneNumbers, $metric, $metricCondition, $metricValue, $comparedTo, $reportUniqueId, $reportCondition, $reportValue, $reportMediums, $slackChannelID);
152+
return $this->getModel()->createAlert($name, $idSites, $login, $period, $emailMe, $additionalEmails, $phoneNumbers, $metric, $metricCondition, $metricValue, $comparedTo, $reportUniqueId, $reportCondition, $reportValue, $reportMediums, $slackChannelID, $msTeamsWebhookUrl);
152153
}
153154

154155
private function filterAdditionalEmails($additionalEmails)
@@ -182,13 +183,14 @@ private function filterPhoneNumbers($phoneNumbers)
182183
return array_values($phoneNumbers);
183184
}
184185

185-
private function checkAlert($idSites, $name, $period, &$emailMe, &$additionalEmails, &$phoneNumbers, &$slackChannelID, $metricCondition, $metricValue, $comparedTo, $reportCondition, $reportUniqueId, $reportMediums)
186+
private function checkAlert($idSites, $name, $period, &$emailMe, &$additionalEmails, &$phoneNumbers, &$slackChannelID, &$msTeamsWebhookUrl, $metricCondition, $metricValue, $comparedTo, $reportCondition, $reportUniqueId, $reportMediums)
186187
{
187188
Piwik::checkUserHasViewAccess($idSites);
188189
$additionalEmails = in_array('email', $reportMediums) ? $this->filterAdditionalEmails($additionalEmails) : [];
189190
$phoneNumbers = in_array('mobile', $reportMediums) ? $this->filterPhoneNumbers($phoneNumbers) : [];
190191
$emailMe = in_array('email', $reportMediums) && $emailMe;
191192
$slackChannelID = in_array('slack', $reportMediums) ? $slackChannelID : '';
193+
$msTeamsWebhookUrl = in_array('teams', $reportMediums) ? $msTeamsWebhookUrl : '';
192194

193195
$this->validator->checkName($name);
194196
$this->validator->checkPeriod($period);
@@ -227,17 +229,18 @@ private function checkAlert($idSites, $name, $period, &$emailMe, &$additionalEma
227229
* @param bool|string $reportValue
228230
* @param array $reportMediums
229231
* @param string $slackChannelID
232+
* @param string $msTeamsWebhookUrl
230233
*
231234
* @return boolean
232235
*/
233-
public function editAlert($idAlert, $name, $idSites, $period, $emailMe, $additionalEmails, $phoneNumbers, $metric, $metricCondition, $metricValue, $comparedTo, $reportUniqueId, $reportCondition = false, $reportValue = false, array $reportMediums = [], string $slackChannelID = '')
236+
public function editAlert($idAlert, $name, $idSites, $period, $emailMe, $additionalEmails, $phoneNumbers, $metric, $metricCondition, $metricValue, $comparedTo, $reportUniqueId, $reportCondition = false, $reportValue = false, array $reportMediums = [], string $slackChannelID = '', string $msTeamsWebhookUrl = '')
234237
{
235238
// make sure alert exists and user has permission to read
236239
$this->getAlert($idAlert);
237240

238241
$idSites = Site::getIdSitesFromIdSitesString($idSites);
239242

240-
$this->checkAlert($idSites, $name, $period, $emailMe, $additionalEmails, $phoneNumbers, $slackChannelID, $metricCondition, $metric, $comparedTo, $reportCondition, $reportUniqueId, $reportMediums);
243+
$this->checkAlert($idSites, $name, $period, $emailMe, $additionalEmails, $phoneNumbers, $slackChannelID, $msTeamsWebhookUrl, $metricCondition, $metric, $comparedTo, $reportCondition, $reportUniqueId, $reportMediums);
241244

242245
$name = Common::unsanitizeInputValue($name);
243246

@@ -248,7 +251,7 @@ public function editAlert($idAlert, $name, $idSites, $period, $emailMe, $additio
248251

249252
$metricValue = Common::forceDotAsSeparatorForDecimalPoint((float)$metricValue);
250253

251-
return $this->getModel()->updateAlert($idAlert, $name, $idSites, $period, $emailMe, $additionalEmails, $phoneNumbers, $metric, $metricCondition, $metricValue, $comparedTo, $reportUniqueId, $reportCondition, $reportValue, $reportMediums, $slackChannelID);
254+
return $this->getModel()->updateAlert($idAlert, $name, $idSites, $period, $emailMe, $additionalEmails, $phoneNumbers, $metric, $metricCondition, $metricValue, $comparedTo, $reportUniqueId, $reportCondition, $reportValue, $reportMediums, $slackChannelID, $msTeamsWebhookUrl);
252255
}
253256

254257
/**

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
## Changelog
22

3+
* 5.2.0 - 2025-11-24 - Added ability to send alerts to a Microsoft Teams channel
34
* 5.1.0 - 2025-09-29 Refactored UI to support different mediums to alert and changes to alert via Slack
45
* 5.0.7 - 2025-07-07 Textual changes
56
* 5.0.6 - 2024-09-23 Added check if reports used by alert are done archiving and retry if not archived

CustomAlerts.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,8 @@ public function removePhoneNumberFromAllAlerts($phoneNumber)
179179
$alert['report_condition'],
180180
$alert['report_matched'],
181181
$alert['report_mediums'],
182-
$alert['slack_channel_id']
182+
$alert['slack_channel_id'],
183+
$alert['ms_teams_webhook_url']
183184
);
184185
}
185186
}
@@ -276,6 +277,7 @@ public static function getReportMediumOptions(): array
276277
['key' => 'email', 'value' => Piwik::translate('CustomAlerts_MediumEmail'), 'disabled' => false],
277278
['key' => 'mobile', 'value' => Piwik::translate('CustomAlerts_MediumMobile'), 'disabled' => !PluginManager::getInstance()->isPluginActivated('MobileMessaging')],
278279
['key' => 'slack', 'value' => Piwik::translate('CustomAlerts_MediumSlack'), 'disabled' => !PluginManager::getInstance()->isPluginActivated('Slack')],
280+
['key' => 'teams', 'value' => Piwik::translate('CustomAlerts_MediumMicrosoftTeams'), 'disabled' => !PluginManager::getInstance()->isPluginActivated('MicrosoftTeams')],
279281
];
280282
}
281283

Model.php

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ public static function install()
3737
`email_me` BOOLEAN NOT NULL DEFAULT '0',
3838
`additional_emails` TEXT ,
3939
`phone_numbers` TEXT ,
40-
`slack_channel_id` VARCHAR(50) NULL ";
40+
`slack_channel_id` VARCHAR(50) NULL ,
41+
`ms_teams_webhook_url` VARCHAR(500) NULL ";
4142

4243
DbHelper::createTable('alert', $tableAlert);
4344

@@ -69,6 +70,7 @@ public static function install()
6970
`additional_emails` TEXT ,
7071
`phone_numbers` TEXT ,
7172
`slack_channel_id` VARCHAR(50) NULL ,
73+
`ms_teams_webhook_url` VARCHAR(500) NULL ,
7274
PRIMARY KEY (idtriggered)";
7375

7476
DbHelper::createTable('alert_triggered', $tableAlertLog);
@@ -254,11 +256,12 @@ public function getAllAlertsForPeriod($period)
254256
* @param string $reportValue
255257
* @param array $reportMediums
256258
* @param string $slackChannelID
259+
* @param string $msTeamsWebhookUrl
257260
*
258261
* @return int ID of new Alert
259262
* @throws \Exception
260263
*/
261-
public function createAlert($name, $idSites, $login, $period, $emailMe, $additionalEmails, $phoneNumbers, $metric, $metricCondition, $metricValue, $comparedTo, $reportUniqueId, $reportCondition, $reportValue, $reportMediums, $slackChannelID)
264+
public function createAlert($name, $idSites, $login, $period, $emailMe, $additionalEmails, $phoneNumbers, $metric, $metricCondition, $metricValue, $comparedTo, $reportUniqueId, $reportCondition, $reportValue, $reportMediums, $slackChannelID, $msTeamsWebhookUrl)
262265
{
263266
$idAlert = $this->getNextAlertId();
264267

@@ -278,7 +281,8 @@ public function createAlert($name, $idSites, $login, $period, $emailMe, $additio
278281
'report_condition' => $reportCondition,
279282
'report_matched' => $reportValue,
280283
'report_mediums' => json_encode($reportMediums),
281-
'slack_channel_id' => $slackChannelID
284+
'slack_channel_id' => $slackChannelID,
285+
'ms_teams_webhook_url' => $msTeamsWebhookUrl,
282286
);
283287

284288
$db = $this->getDb();
@@ -337,11 +341,12 @@ private function removeAllSites($idAlert)
337341
* @param string $reportValue
338342
* @param array $reportMediums
339343
* @param string $slackChannelID
344+
* @param string $msTeamsWebhookUrl
340345
*
341346
* @return int
342347
* @throws \Exception
343348
*/
344-
public function updateAlert($idAlert, $name, $idSites, $period, $emailMe, $additionalEmails, $phoneNumbers, $metric, $metricCondition, $metricValue, $comparedTo, $reportUniqueId, $reportCondition, $reportValue, $reportMediums, $slackChannelID)
349+
public function updateAlert($idAlert, $name, $idSites, $period, $emailMe, $additionalEmails, $phoneNumbers, $metric, $metricCondition, $metricValue, $comparedTo, $reportUniqueId, $reportCondition, $reportValue, $reportMediums, $slackChannelID, $msTeamsWebhookUrl)
345350
{
346351
$alert = array(
347352
'name' => $name,
@@ -357,7 +362,8 @@ public function updateAlert($idAlert, $name, $idSites, $period, $emailMe, $addit
357362
'report_condition' => $reportCondition,
358363
'report_matched' => $reportValue,
359364
'report_mediums' => json_encode($reportMediums),
360-
'slack_channel_id' => $slackChannelID
365+
'slack_channel_id' => $slackChannelID,
366+
'ms_teams_webhook_url' => $msTeamsWebhookUrl,
361367
);
362368

363369
$db = $this->getDb();
@@ -387,7 +393,7 @@ public function triggerAlert($idAlert, $idSite, $valueNew, $valueOld, $datetime)
387393
{
388394
$alert = $this->getAlert($idAlert);
389395

390-
$keysToKeep = array('idalert', 'name', 'login', 'period', 'metric', 'metric_condition', 'metric_matched', 'report', 'report_condition', 'report_matched', 'report_mediums', 'compared_to', 'email_me', 'additional_emails', 'phone_numbers', 'slack_channel_id');
396+
$keysToKeep = array('idalert', 'name', 'login', 'period', 'metric', 'metric_condition', 'metric_matched', 'report', 'report_condition', 'report_matched', 'report_mediums', 'compared_to', 'email_me', 'additional_emails', 'phone_numbers', 'slack_channel_id', 'ms_teams_webhook_url');
391397

392398
$triggeredAlert = array();
393399
foreach ($keysToKeep as $key) {

Updates/5.2.0.php

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?php
2+
3+
/**
4+
* Matomo - free/libre analytics platform
5+
*
6+
* @link https://matomo.org
7+
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
8+
*
9+
*/
10+
11+
namespace Piwik\Plugins\CustomAlerts;
12+
13+
use Piwik\Common;
14+
use Piwik\Updater;
15+
use Piwik\Updater\Migration\Factory as MigrationFactory;
16+
use Piwik\Updates;
17+
18+
/**
19+
*/
20+
class Updates_5_2_0 extends Updates
21+
{
22+
/**
23+
* @var MigrationFactory
24+
*/
25+
private $migration;
26+
27+
public function __construct(MigrationFactory $factory)
28+
{
29+
$this->migration = $factory;
30+
}
31+
32+
public function doUpdate(Updater $updater)
33+
{
34+
$updater->executeMigrations(__FILE__, $this->getMigrations($updater));
35+
}
36+
37+
public function getMigrations(Updater $updater)
38+
{
39+
$alertTableName = Common::prefixTable('alert');
40+
$alertTriggeredTableName = Common::prefixTable('alert_triggered');
41+
42+
return array(
43+
$this->migration->db->addColumn('alert', 'ms_teams_webhook_url', 'VARCHAR(500) NULL', 'slack_channel_id'),
44+
$this->migration->db->addColumn('alert_triggered', 'ms_teams_webhook_url', 'VARCHAR(500) NULL', 'slack_channel_id'),
45+
);
46+
}
47+
}

lang/en.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@
7777
"MediumEmail": "Email",
7878
"MediumMobile": "Mobile",
7979
"MediumSlack": "Slack",
80+
"MediumMicrosoftTeams": "Teams",
8081
"MediumTitle": "Send alerts via",
8182
"MediumDescription": "Choose how you want to receive alerts when this custom alert is triggered.",
8283
"EmptyReportMediums": "At least one delivery method must be selected.",

phpcs.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
<rule ref="Generic.Files.LineLength">
1717
<properties>
18-
<property name="lineLimit" value="300" />
18+
<property name="lineLimit" value="350" />
1919
</properties>
2020
<exclude-pattern>tests/*</exclude-pattern>
2121
</rule>

plugin.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "CustomAlerts",
33
"description": "Create custom Alerts to be notified of important changes on your website or app! ",
4-
"version": "5.1.0",
4+
"version": "5.2.0",
55
"require": {
66
"matomo": ">=5.0.0-b1,<6.0.0-b1"
77
},

tests/Fixtures/CustomAlerts.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ private function createAlert($name, $period, $idSites, $metric, $report, $login
7676
}
7777

7878
$model = new Model();
79-
$model->createAlert($name, $idSites, $login, $period, 0, $emails, $phoneNumbers, $metric, 'less_than', 5, $comparedTo = 1, $report, 'matches_exactly', $reportMatched, ['email', 'mobile'], '');
79+
$model->createAlert($name, $idSites, $login, $period, 0, $emails, $phoneNumbers, $metric, 'less_than', 5, $comparedTo = 1, $report, 'matches_exactly', $reportMatched, ['email', 'mobile'], '', '');
8080
}
8181

8282
private function triggerAlert($idAlert, $valueNew, $valueOld, $datetime)

tests/Integration/ApiTest.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ protected function createAlert(
6262
$reportCondition,
6363
'Piwik',
6464
['email', 'mobile'],
65+
'',
6566
''
6667
);
6768
return $id;
@@ -227,6 +228,7 @@ protected function assertIsAlert(
227228
'additional_emails' => array('[email protected]', '[email protected]'),
228229
'phone_numbers' => array(),
229230
'slack_channel_id' => '',
231+
'ms_teams_webhook_url' => '',
230232
'compared_to' => 1,
231233
'id_sites' => $idSites,
232234
'report_mediums' => ['email', 'mobile']
@@ -583,6 +585,7 @@ public function test_triggerAlert_getTriggeredAlertsForPeriod_ShouldMarkAlertAsT
583585
'additional_emails' => array('[email protected]', '[email protected]'),
584586
'phone_numbers' => array(),
585587
'slack_channel_id' => '',
588+
'ms_teams_webhook_url' => '',
586589
'email_me' => 0,
587590
'compared_to' => 1,
588591
'id_sites' => array(1, 2),

0 commit comments

Comments
 (0)