Skip to content

Commit ad0f98d

Browse files
Merge pull request #12 from matomo-org/improve-validation
Improve validation
2 parents 5734121 + 551af58 commit ad0f98d

5 files changed

Lines changed: 309 additions & 6 deletions

File tree

Slack.php

Lines changed: 155 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ public function registerEvents()
5858
'ScheduledReports.sendReport' => 'sendReport',
5959
'Template.reportParametersScheduledReports' => 'templateReportParametersScheduledReports',
6060
'Translate.getClientSideTranslationKeys' => 'getClientSideTranslationKeys',
61-
'CustomAlerts.validateReportParameters' => 'validateCustomAlertReportParameters',
61+
'CustomAlerts.validateReportParameters' => 'validateCustomAlertReportParameters',
6262
'CustomAlerts.sendNewAlerts' => 'sendNewAlerts',
6363
];
6464
}
@@ -76,6 +76,16 @@ public function getClientSideTranslationKeys(&$translationKeys)
7676
$translationKeys[] = 'Slack_SlackEnterYourSlackChannelIdHelpText';
7777
}
7878

79+
/**
80+
*
81+
* Validates the Schedule Report for Slack reportType
82+
*
83+
* @param $parameters
84+
* @param $reportType
85+
* @return void
86+
* @throws \Piwik\Exception\DI\DependencyException
87+
* @throws \Piwik\Exception\DI\NotFoundException
88+
*/
7989
public function validateReportParameters(&$parameters, $reportType)
8090
{
8191
if (!self::isSlackEvent($reportType)) {
@@ -106,8 +116,23 @@ public function validateReportParameters(&$parameters, $reportType)
106116
} elseif (empty($parameters[self::SLACK_CHANNEL_ID_PARAMETER])) {
107117
throw new \Exception(Piwik::translate('Slack_SlackChannelIdRequiredErrorMessage'));
108118
}
119+
$slackChannels = explode(',', (string) $parameters[self::SLACK_CHANNEL_ID_PARAMETER]);
120+
foreach ($slackChannels as $slackChannel) {
121+
if (!ctype_alnum($slackChannel)) {
122+
throw new \Exception(Piwik::translate('Slack_SlackChannelIdInvalidErrorMessage'));
123+
}
124+
}
109125
}
110126

127+
/**
128+
*
129+
* Get report metadata for Slack scheduled report
130+
*
131+
* @param $availableReportMetadata
132+
* @param $reportType
133+
* @param $idSite
134+
* @return void
135+
*/
111136
public function getReportMetadata(&$availableReportMetadata, $reportType, $idSite)
112137
{
113138
if (!self::isSlackEvent($reportType)) {
@@ -121,25 +146,58 @@ public function getReportMetadata(&$availableReportMetadata, $reportType, $idSit
121146
);
122147
}
123148

149+
/**
150+
*
151+
* Adds Slack as a reportType in Schedule Reports
152+
*
153+
* @param $reportTypes
154+
* @return void
155+
*/
124156
public function getReportTypes(&$reportTypes)
125157
{
126158
$reportTypes = array_merge($reportTypes, self::$managedReportTypes);
127159
}
128160

161+
/**
162+
*
163+
* Adds allowed reportTypes for Slack, e.g. PDF, CSV and TSV
164+
*
165+
* @param $reportFormats
166+
* @param $reportType
167+
* @return void
168+
*/
129169
public function getReportFormats(&$reportFormats, $reportType)
130170
{
131171
if (self::isSlackEvent($reportType)) {
132172
$reportFormats = array_merge($reportFormats, self::$managedReportFormats);
133173
}
134174
}
135175

176+
/**
177+
*
178+
* Adds report parameter for Slack, e.g. SlackChannelID
179+
*
180+
* @param $availableParameters
181+
* @param $reportType
182+
* @return void
183+
*/
136184
public function getReportParameters(&$availableParameters, $reportType)
137185
{
138186
if (self::isSlackEvent($reportType)) {
139187
$availableParameters = self::$availableParameters;
140188
}
141189
}
142190

191+
/**
192+
*
193+
* Process the Schedule report for reportType Slack
194+
*
195+
* @param $processedReports
196+
* @param $reportType
197+
* @param $outputType
198+
* @param $report
199+
* @return void
200+
*/
143201
public function processReports(&$processedReports, $reportType, $outputType, $report)
144202
{
145203
if (!self::isSlackEvent($reportType)) {
@@ -153,6 +211,17 @@ public function processReports(&$processedReports, $reportType, $outputType, $re
153211
);
154212
}
155213

214+
/**
215+
*
216+
* Sets the rendered instance based on reportFormat for Slack
217+
*
218+
* @param $reportRenderer
219+
* @param $reportType
220+
* @param $outputType
221+
* @param $report
222+
* @return void
223+
* @throws \Exception
224+
*/
156225
public function getRendererInstance(&$reportRenderer, $reportType, $outputType, $report)
157226
{
158227
if (!self::isSlackEvent($reportType)) {
@@ -164,13 +233,30 @@ public function getRendererInstance(&$reportRenderer, $reportType, $outputType,
164233
$reportRenderer = ReportRenderer::factory($reportFormat);
165234
}
166235

236+
/**
237+
*
238+
* To allow multiple reports in a single file
239+
*
240+
* @param $allowMultipleReports
241+
* @param $reportType
242+
* @return void
243+
*/
167244
public function allowMultipleReports(&$allowMultipleReports, $reportType)
168245
{
169246
if (self::isSlackEvent($reportType)) {
170247
$allowMultipleReports = true;
171248
}
172249
}
173250

251+
/**
252+
*
253+
* Displays the recipients in the list of Schedule Reports
254+
*
255+
* @param $recipients
256+
* @param $reportType
257+
* @param $report
258+
* @return void
259+
*/
174260
public function getReportRecipients(&$recipients, $reportType, $report)
175261
{
176262
if (!self::isSlackEvent($reportType) || empty($report['parameters'][self::SLACK_CHANNEL_ID_PARAMETER])) {
@@ -181,6 +267,9 @@ public function getReportRecipients(&$recipients, $reportType, $report)
181267
}
182268

183269
/**
270+
*
271+
* Code to send a Schedule Report via Slack
272+
*
184273
* @param $reportType
185274
* @param $report
186275
* @param $contents
@@ -189,8 +278,11 @@ public function getReportRecipients(&$recipients, $reportType, $report)
189278
* @param $reportSubject
190279
* @param $reportTitle
191280
* @param $additionalFiles
192-
* @param Period|null $period
281+
* @param $period
193282
* @param $force
283+
* @return void
284+
* @throws \Piwik\Exception\DI\DependencyException
285+
* @throws \Piwik\Exception\DI\NotFoundException
194286
*/
195287
public function sendReport(
196288
$reportType,
@@ -232,6 +324,16 @@ public function sendReport(
232324
$scheduleReportSlack->send();
233325
}
234326

327+
/**
328+
*
329+
* Add the view template for Slack report parameters
330+
*
331+
* @param $out
332+
* @param $context
333+
* @return void
334+
* @throws \Piwik\Exception\DI\DependencyException
335+
* @throws \Piwik\Exception\DI\NotFoundException
336+
*/
235337
public function templateReportParametersScheduledReports(&$out, $context = '')
236338
{
237339
if (Piwik::isUserIsAnonymous()) {
@@ -277,17 +379,42 @@ public function uninstall()
277379
return;
278380
}
279381

382+
/**
383+
*
384+
* Validation check for CustomAlert report parameters
385+
*
386+
* @param $parameters
387+
* @param $alertMedium
388+
* @return void
389+
* @throws \Exception
390+
*/
280391
public function validateCustomAlertReportParameters($parameters, $alertMedium)
281392
{
282-
if ($alertMedium === self::SLACK_TYPE && empty($parameters[self::SLACK_CHANNEL_ID_PARAMETER])) {
283-
throw new \Exception(Piwik::translate('Slack_SlackChannelIdRequiredErrorMessage'));
393+
if ($alertMedium === self::SLACK_TYPE) {
394+
$settings = StaticContainer::get(SystemSettings::class);
395+
if (empty($settings->slackOauthToken->getValue())) {
396+
throw new \Exception(Piwik::translate('Slack_OauthTokenRequiredErrorMessage'));
397+
} elseif (empty($parameters[self::SLACK_CHANNEL_ID_PARAMETER])) {
398+
throw new \Exception(Piwik::translate('Slack_SlackChannelIdRequiredErrorMessage'));
399+
} elseif (!ctype_alnum($parameters[self::SLACK_CHANNEL_ID_PARAMETER])) {
400+
throw new \Exception(Piwik::translate('Slack_SlackChannelIdInvalidErrorMessage'));
401+
}
284402
}
285403
}
286404

405+
/**
406+
*
407+
* Code to send CustomAlerts via Slack
408+
*
409+
* @param $triggeredAlerts
410+
* @return void
411+
* @throws \Piwik\Exception\DI\DependencyException
412+
* @throws \Piwik\Exception\DI\NotFoundException
413+
*/
287414
public function sendNewAlerts($triggeredAlerts): void
288415
{
289416
if (!empty($triggeredAlerts)) {
290-
$enrichTriggerAlerts = new EnrichTriggeredAlerts();
417+
$enrichTriggerAlerts = StaticContainer::get(EnrichTriggeredAlerts::class);
291418
$triggeredAlerts = $enrichTriggerAlerts->enrichTriggeredAlerts($triggeredAlerts);
292419
$settings = StaticContainer::get(SystemSettings::class);
293420
$token = $settings->slackOauthToken->getValue();
@@ -305,6 +432,13 @@ public function sendNewAlerts($triggeredAlerts): void
305432
}
306433
}
307434

435+
/**
436+
*
437+
* Group alerts by slackChannelID to reduce number of network calls for multiple alerts
438+
*
439+
* @param array $alerts
440+
* @return array
441+
*/
308442
private function groupAlertsByChannelId(array $alerts): array
309443
{
310444
$groupedAlerts = [];
@@ -321,11 +455,27 @@ private function groupAlertsByChannelId(array $alerts): array
321455
return $groupedAlerts;
322456
}
323457

458+
/**
459+
*
460+
* Returns the alert message to send via Slack
461+
*
462+
* @param array $alert
463+
* @param string $metric
464+
* @param string $reportName
465+
* @return string
466+
*/
324467
public function getAlertMessage(array $alert, string $metric, string $reportName): string
325468
{
326469
return Piwik::translate('Slack_SlackAlertContent', [$alert['name'], $alert['siteName'], $metric, $reportName, $this->transformAlertCondition($alert)]);
327470
}
328471

472+
/**
473+
*
474+
* Transform the alert condition to text
475+
*
476+
* @param array $alert
477+
* @return string
478+
*/
329479
private function transformAlertCondition(array $alert): string
330480
{
331481
switch ($alert['metric_condition']) {

SlackApi.php

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,14 @@ public function uploadFile(string $subject, string $fileName, string $fileConten
6565
return false;
6666
}
6767

68+
/**
69+
*
70+
* Get the URL to upload the file
71+
*
72+
* @param string $fileName
73+
* @param int $fileLength
74+
* @return string
75+
*/
6876
public function getUploadURLExternal(string $fileName, int $fileLength): string
6977
{
7078
try {
@@ -93,6 +101,14 @@ public function getUploadURLExternal(string $fileName, int $fileLength): string
93101
return '';
94102
}
95103

104+
/**
105+
*
106+
* Upload the file contents to the URL received from getUploadURLExternal method
107+
*
108+
* @param string $uploadURL
109+
* @param string $fileContents
110+
* @return bool
111+
*/
96112
public function sendFile(string $uploadURL, string $fileContents): bool
97113
{
98114
try {
@@ -111,6 +127,15 @@ public function sendFile(string $uploadURL, string $fileContents): bool
111127
return strtolower($response) === ('ok - ' . strlen($fileContents));
112128
}
113129

130+
131+
/**
132+
*
133+
* Post the uploaded file to a channel
134+
*
135+
* @param string $channel
136+
* @param string $subject
137+
* @return bool
138+
*/
114139
public function completeUploadExternal(string $channel, string $subject): bool
115140
{
116141
try {
@@ -135,8 +160,21 @@ public function completeUploadExternal(string $channel, string $subject): bool
135160
return !empty($data['ok']);
136161
}
137162

163+
/**
164+
*
165+
* Send a text message to a Slack channel
166+
*
167+
* @param string $message
168+
* @param string $channel
169+
* @return bool
170+
*/
138171
public function sendMessage(string $message, string $channel): bool
139172
{
173+
if (empty($message) || empty($channel)) {
174+
$this->logger->debug('Empty message or channel for sending message');
175+
return false;
176+
}
177+
140178
try {
141179
$response = $this->sendHttpRequest(
142180
self::SLACK_POST_MESSAGE_URL,
@@ -158,6 +196,18 @@ public function sendMessage(string $message, string $channel): bool
158196
return !empty($data['ok']);
159197
}
160198

199+
/**
200+
*
201+
* Wrapper to send HTTP request
202+
*
203+
* @param string $url
204+
* @param int $timeout
205+
* @param array $requestBody
206+
* @param array $additionalHeaders
207+
* @param $requestBodyAsString
208+
* @return array|bool|int[]|string
209+
* @throws \Exception
210+
*/
161211
public function sendHttpRequest(string $url, int $timeout, array $requestBody, array $additionalHeaders = [], $requestBodyAsString = false)
162212
{
163213
if ($requestBodyAsString && !empty($requestBody[0])) {

lang/en.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
"OauthTokenSettingDescription": "Enter your Slack OAuth Token generated from your Slack. %1$sLearn more%2$s.",
88
"PleaseFindYourReport": "Here is your %1$s report for %2$s",
99
"SlackChannelIdRequiredErrorMessage": "Slack Channel ID cannot be empty.",
10+
"SlackChannelIdInvalidErrorMessage": "Invalid Slack Channel ID, only alphanumeric values are allowed.",
1011
"SlackChannel": "Slack Channel",
1112
"SlackEnterYourSlackChannelIdHelpText": "Enter the Slack Channel ID of the channel that will receive these reports. To find the ID, go to Slack and open the channel details > About tab. %1$sLearn more%2$s",
1213
"SlackAlertContent": "%1$s has been triggered for website %2$s as the metric %3$s in report %4$s %5$s."

0 commit comments

Comments
 (0)