mirror of
https://github.com/grafana/grafana.git
synced 2025-02-20 11:48:34 -06:00
feat(alerting): add support for email notifications
This commit is contained in:
parent
774add94c1
commit
212fd27252
29
emails/templates/alert_notification.html
Normal file
29
emails/templates/alert_notification.html
Normal file
@ -0,0 +1,29 @@
|
||||
<!-- This email is sent when an existing user is added to an organization -->
|
||||
|
||||
[[Subject .Subject "Grafana Alert: [ [[.State]] ] [[.Name]]" ]]
|
||||
|
||||
Alertstate: [[.State]]<br />
|
||||
[[.AlertPageUrl]]"<br />
|
||||
[[.DashboardLink]]"<br />
|
||||
[[.Description]]<br />
|
||||
|
||||
[[if eq .State "Ok"]]
|
||||
Everything is Ok
|
||||
[[end]]
|
||||
|
||||
[[if ne .State "Ok" ]]
|
||||
<table class="row">
|
||||
<tr>
|
||||
<td class="expander">Serie</td>
|
||||
<td class="expander">State</td>
|
||||
<td class="expander">Actual value</td>
|
||||
</tr>
|
||||
[[ range $ta := .TriggeredAlerts]]
|
||||
<tr>
|
||||
<td class="expander">[[$ta.Name]]</td>
|
||||
<td class="expander">[[$ta.State]]</td>
|
||||
<td class="expander">[[$ta.ActualValue]]</td>
|
||||
</tr>
|
||||
[[end]]
|
||||
</table>
|
||||
[[end]]
|
@ -8,6 +8,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/log"
|
||||
m "github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/alerting/alertstates"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
type NotifierImpl struct {
|
||||
@ -50,18 +51,28 @@ type EmailNotifier struct {
|
||||
|
||||
func (this *EmailNotifier) Dispatch(alertResult *AlertResult) {
|
||||
this.log.Info("Sending email")
|
||||
cmd := &m.SendEmailCommand{
|
||||
Data: map[string]interface{}{
|
||||
"Description": alertResult.Description,
|
||||
"TriggeredAlerts": alertResult.TriggeredAlerts,
|
||||
},
|
||||
To: []string{this.To},
|
||||
Info: "Alert result",
|
||||
Massive: false,
|
||||
Template: "",
|
||||
grafanaUrl := fmt.Sprintf("%s:%s", setting.HttpAddr, setting.HttpPort)
|
||||
if setting.AppSubUrl != "" {
|
||||
grafanaUrl += "/" + setting.AppSubUrl
|
||||
}
|
||||
|
||||
bus.Dispatch(cmd)
|
||||
cmd := &m.SendEmailCommand{
|
||||
Data: map[string]interface{}{
|
||||
"Name": "Name",
|
||||
"State": alertResult.State,
|
||||
"Description": alertResult.Description,
|
||||
"TriggeredAlerts": alertResult.TriggeredAlerts,
|
||||
"DashboardLink": grafanaUrl + "/dashboard/db/alerting",
|
||||
"AlertPageUrl": grafanaUrl + "/alerting",
|
||||
},
|
||||
To: []string{this.To},
|
||||
Template: "alert_notification.html",
|
||||
}
|
||||
|
||||
err := bus.Dispatch(cmd)
|
||||
if err != nil {
|
||||
this.log.Error("Could not send alert notification as email", "error", err)
|
||||
}
|
||||
}
|
||||
|
||||
type WebhookNotifier struct {
|
||||
|
@ -9,6 +9,12 @@ import (
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
)
|
||||
|
||||
type testTriggeredAlert struct {
|
||||
ActualValue float64
|
||||
Name string
|
||||
State string
|
||||
}
|
||||
|
||||
func TestNotifications(t *testing.T) {
|
||||
|
||||
Convey("Given the notifications service", t, func() {
|
||||
@ -34,6 +40,83 @@ func TestNotifications(t *testing.T) {
|
||||
So(sentMsg.Subject, ShouldEqual, "Reset your Grafana password - asd@asd.com")
|
||||
So(sentMsg.Body, ShouldNotContainSubstring, "Subject")
|
||||
})
|
||||
|
||||
Convey("Alert notifications", func() {
|
||||
Convey("When sending reset email password", func() {
|
||||
cmd := &m.SendEmailCommand{
|
||||
Data: map[string]interface{}{
|
||||
"Name": "Name",
|
||||
"State": "Critical",
|
||||
"Description": "Description",
|
||||
"DashboardLink": "http://localhost:3000/dashboard/db/alerting",
|
||||
"AlertPageUrl": "http://localhost:3000/alerting",
|
||||
"TriggeredAlerts": []testTriggeredAlert{
|
||||
{Name: "desktop", State: "Critical", ActualValue: 13},
|
||||
{Name: "mobile", State: "Warn", ActualValue: 5},
|
||||
},
|
||||
},
|
||||
To: []string{"asd@asd.com "},
|
||||
Template: "alert_notification.html",
|
||||
}
|
||||
|
||||
err := sendEmailCommandHandler(cmd)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
So(sentMsg.Body, ShouldContainSubstring, "Alertstate: Critical")
|
||||
So(sentMsg.Body, ShouldContainSubstring, "http://localhost:3000/dashboard/db/alerting")
|
||||
So(sentMsg.Body, ShouldContainSubstring, "Critical")
|
||||
So(sentMsg.Body, ShouldContainSubstring, "Warn")
|
||||
So(sentMsg.Body, ShouldContainSubstring, "mobile")
|
||||
So(sentMsg.Body, ShouldContainSubstring, "desktop")
|
||||
|
||||
So(sentMsg.Subject, ShouldContainSubstring, "Grafana Alert: [ Critical ] ")
|
||||
})
|
||||
|
||||
Convey("given critical", func() {
|
||||
cmd := &m.SendEmailCommand{
|
||||
Data: map[string]interface{}{
|
||||
"Name": "Name",
|
||||
"State": "Warn",
|
||||
"Description": "Description",
|
||||
"DashboardLink": "http://localhost:3000/dashboard/db/alerting",
|
||||
"AlertPageUrl": "http://localhost:3000/alerting",
|
||||
"TriggeredAlerts": []testTriggeredAlert{
|
||||
{Name: "desktop", State: "Critical", ActualValue: 13},
|
||||
{Name: "mobile", State: "Warn", ActualValue: 5},
|
||||
},
|
||||
},
|
||||
To: []string{"asd@asd.com "},
|
||||
Template: "alert_notification.html",
|
||||
}
|
||||
|
||||
err := sendEmailCommandHandler(cmd)
|
||||
So(err, ShouldBeNil)
|
||||
So(sentMsg.Body, ShouldContainSubstring, "Alertstate: Warn")
|
||||
So(sentMsg.Body, ShouldContainSubstring, "http://localhost:3000/dashboard/db/alerting")
|
||||
So(sentMsg.Body, ShouldContainSubstring, "Critical")
|
||||
So(sentMsg.Body, ShouldContainSubstring, "Warn")
|
||||
So(sentMsg.Body, ShouldContainSubstring, "mobile")
|
||||
So(sentMsg.Body, ShouldContainSubstring, "desktop")
|
||||
So(sentMsg.Subject, ShouldContainSubstring, "Grafana Alert: [ Warn ]")
|
||||
})
|
||||
|
||||
Convey("given ok", func() {
|
||||
cmd := &m.SendEmailCommand{
|
||||
Data: map[string]interface{}{
|
||||
"Name": "Name",
|
||||
"State": "Ok",
|
||||
"Description": "Description",
|
||||
"DashboardLink": "http://localhost:3000/dashboard/db/alerting",
|
||||
"AlertPageUrl": "http://localhost:3000/alerting",
|
||||
},
|
||||
To: []string{"asd@asd.com "},
|
||||
Template: "alert_notification.html",
|
||||
}
|
||||
|
||||
err := sendEmailCommandHandler(cmd)
|
||||
So(err, ShouldBeNil)
|
||||
So(sentMsg.Subject, ShouldContainSubstring, "Grafana Alert: [ Ok ]")
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
177
public/emails/alert_notification.html
Normal file
177
public/emails/alert_notification.html
Normal file
@ -0,0 +1,177 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
|
||||
</head>
|
||||
<body style="-ms-text-size-adjust: 100%; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; color: #222222; font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; font-size: 14px; font-weight: normal; line-height: 19px; margin: 0; min-width: 100%; padding: 0; text-align: left; width: 100% !important"><style type="text/css">
|
||||
body {
|
||||
width: 100% !important; min-width: 100%; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; margin: 0; padding: 0;
|
||||
}
|
||||
img {
|
||||
outline: none; text-decoration: none; -ms-interpolation-mode: bicubic; width: auto; max-width: 100%; float: left; clear: both; display: block;
|
||||
}
|
||||
body {
|
||||
color: #222222; font-family: "Helvetica", "Arial", sans-serif; font-weight: normal; padding: 0; margin: 0; text-align: left; line-height: 1.3;
|
||||
}
|
||||
body {
|
||||
font-size: 14px; line-height: 19px;
|
||||
}
|
||||
a:hover {
|
||||
color: #2795b6 !important;
|
||||
}
|
||||
a:active {
|
||||
color: #2795b6 !important;
|
||||
}
|
||||
a:visited {
|
||||
color: #2ba6cb !important;
|
||||
}
|
||||
body {
|
||||
font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;
|
||||
}
|
||||
a:hover {
|
||||
color: #ff8f2b !important;
|
||||
}
|
||||
a:active {
|
||||
color: #F2821E !important;
|
||||
}
|
||||
a:visited {
|
||||
color: #E67612 !important;
|
||||
}
|
||||
.better-button:hover a {
|
||||
color: #FFFFFF !important; background-color: #F2821E; border: 1px solid #F2821E;
|
||||
}
|
||||
.better-button:visited a {
|
||||
color: #FFFFFF !important;
|
||||
}
|
||||
.better-button:active a {
|
||||
color: #FFFFFF !important;
|
||||
}
|
||||
@media only screen and (max-width: 600px) {
|
||||
table[class="body"] img {
|
||||
width: auto !important; height: auto !important;
|
||||
}
|
||||
table[class="body"] center {
|
||||
min-width: 0 !important;
|
||||
}
|
||||
table[class="body"] .container {
|
||||
width: 95% !important;
|
||||
}
|
||||
table[class="body"] .row {
|
||||
width: 100% !important; display: block !important;
|
||||
}
|
||||
table[class="body"] .wrapper {
|
||||
display: block !important; padding-right: 0 !important;
|
||||
}
|
||||
table[class="body"] .columns {
|
||||
table-layout: fixed !important; float: none !important; width: 100% !important; padding-right: 0px !important; padding-left: 0px !important; display: block !important;
|
||||
}
|
||||
table[class="body"] table.columns td {
|
||||
width: 100% !important;
|
||||
}
|
||||
table[class="body"] .columns td.six {
|
||||
width: 50% !important;
|
||||
}
|
||||
table[class="body"] table.columns td.expander {
|
||||
width: 1px !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<table class="body" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; border-collapse: collapse; border-spacing: 0; color: #222222; font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; font-size: 14px; font-weight: normal; height: 100%; line-height: 19px; margin: 0; padding: 0; text-align: left; vertical-align: top; width: 100%">
|
||||
<tr style="padding: 0; text-align: left; vertical-align: top" align="left">
|
||||
<td class="center" align="center" valign="top" style="-moz-hyphens: auto; -webkit-font-smoothing: antialiased; -webkit-hyphens: auto; -webkit-text-size-adjust: none; border-collapse: collapse !important; color: #222222; font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; font-size: 14px; font-weight: normal; hyphens: auto; line-height: 19px; margin: 0; padding: 0; text-align: center; vertical-align: top; word-break: break-word">
|
||||
<center style="min-width: 580px; width: 100%">
|
||||
|
||||
<table class="row header" style="background: #333; border-collapse: collapse; border-spacing: 0; padding: 0px; position: relative; text-align: left; vertical-align: top; width: 100%" bgcolor="#333">
|
||||
<tr style="padding: 0; text-align: left; vertical-align: top" align="left">
|
||||
<td class="center" align="center" style="-moz-hyphens: auto; -webkit-font-smoothing: antialiased; -webkit-hyphens: auto; -webkit-text-size-adjust: none; border-collapse: collapse !important; color: #222222; font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; font-size: 14px; font-weight: normal; hyphens: auto; line-height: 19px; margin: 0; padding: 0; text-align: center; vertical-align: top; word-break: break-word" valign="top">
|
||||
<center style="min-width: 580px; width: 100%">
|
||||
|
||||
<table class="container" style="border-collapse: collapse; border-spacing: 0; margin: 0 auto; padding: 0; text-align: inherit; vertical-align: top; width: 580px">
|
||||
<tr style="padding: 0; text-align: left; vertical-align: top" align="left">
|
||||
<td class="wrapper last" style="-moz-hyphens: auto; -webkit-font-smoothing: antialiased; -webkit-hyphens: auto; -webkit-text-size-adjust: none; border-collapse: collapse !important; color: #222222; font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; font-size: 14px; font-weight: normal; hyphens: auto; line-height: 19px; margin: 0; padding: 10px 0px 0px; position: relative; text-align: left; vertical-align: top; word-break: break-word" align="left" valign="top">
|
||||
|
||||
<table class="twelve columns" style="border-collapse: collapse; border-spacing: 0; margin: 0 auto; padding: 0; text-align: left; vertical-align: top; width: 580px">
|
||||
<tr style="padding: 0; text-align: left; vertical-align: top" align="left">
|
||||
<td class="six sub-columns center" style="-moz-hyphens: auto; -webkit-font-smoothing: antialiased; -webkit-hyphens: auto; -webkit-text-size-adjust: none; border-collapse: collapse !important; color: #222222; font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; font-size: 14px; font-weight: normal; hyphens: auto; line-height: 19px; margin: 0; min-width: 0px; padding: 0px 10px 10px 0px; text-align: center; vertical-align: top; width: 50%; word-break: break-word" align="center" valign="top">
|
||||
<img src="http://docs.grafana.org/img/logo_transparent_200x75.png" style="-ms-interpolation-mode: bicubic; clear: both; display: inline; float: none; max-width: 100%; outline: none; text-decoration: none; width: 150px" align="none" />
|
||||
</td>
|
||||
<td class="expander" style="-moz-hyphens: auto; -webkit-font-smoothing: antialiased; -webkit-hyphens: auto; -webkit-text-size-adjust: none; border-collapse: collapse !important; color: #222222; font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; font-size: 14px; font-weight: normal; hyphens: auto; line-height: 19px; margin: 0; padding: 0; text-align: left; vertical-align: top; visibility: hidden; width: 0px; word-break: break-word" align="left" valign="top"></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</center>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<table class="container" style="border-collapse: collapse; border-spacing: 0; margin: 0 auto; padding: 0; text-align: inherit; vertical-align: top; width: 580px">
|
||||
<tr style="padding: 0; text-align: left; vertical-align: top" align="left">
|
||||
<td style="-moz-hyphens: auto; -webkit-font-smoothing: antialiased; -webkit-hyphens: auto; -webkit-text-size-adjust: none; border-collapse: collapse !important; color: #222222; font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; font-size: 14px; font-weight: normal; hyphens: auto; line-height: 19px; margin: 0; padding: 0; text-align: left; vertical-align: top; word-break: break-word" align="left" valign="top">
|
||||
|
||||
|
||||
{{Subject .Subject "Grafana Alert: [ {{.State}} ] {{.Name}}" }}
|
||||
|
||||
Alertstate: {{.State}}<br />
|
||||
{{.AlertPageUrl}}"<br />
|
||||
{{.DashboardLink}}"<br />
|
||||
{{.Description}}<br />
|
||||
|
||||
{{if eq .State "Ok"}}
|
||||
Everything is Ok
|
||||
{{end}}
|
||||
|
||||
{{if ne .State "Ok" }}
|
||||
<table class="row" style="border-collapse: collapse; border-spacing: 0; display: block; padding: 0px; position: relative; text-align: left; vertical-align: top; width: 100%">
|
||||
<tr style="padding: 0; text-align: left; vertical-align: top" align="left">
|
||||
<td class="expander" style="-moz-hyphens: auto; -webkit-font-smoothing: antialiased; -webkit-hyphens: auto; -webkit-text-size-adjust: none; border-collapse: collapse !important; color: #222222; font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; font-size: 14px; font-weight: normal; hyphens: auto; line-height: 19px; margin: 0; padding: 0; text-align: left; vertical-align: top; visibility: hidden; width: 0px; word-break: break-word" align="left" valign="top">Serie</td>
|
||||
<td class="expander" style="-moz-hyphens: auto; -webkit-font-smoothing: antialiased; -webkit-hyphens: auto; -webkit-text-size-adjust: none; border-collapse: collapse !important; color: #222222; font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; font-size: 14px; font-weight: normal; hyphens: auto; line-height: 19px; margin: 0; padding: 0; text-align: left; vertical-align: top; visibility: hidden; width: 0px; word-break: break-word" align="left" valign="top">State</td>
|
||||
<td class="expander" style="-moz-hyphens: auto; -webkit-font-smoothing: antialiased; -webkit-hyphens: auto; -webkit-text-size-adjust: none; border-collapse: collapse !important; color: #222222; font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; font-size: 14px; font-weight: normal; hyphens: auto; line-height: 19px; margin: 0; padding: 0; text-align: left; vertical-align: top; visibility: hidden; width: 0px; word-break: break-word" align="left" valign="top">Actual value</td>
|
||||
</tr>
|
||||
{{ range $ta := .TriggeredAlerts}}
|
||||
<tr style="padding: 0; text-align: left; vertical-align: top" align="left">
|
||||
<td class="expander" style="-moz-hyphens: auto; -webkit-font-smoothing: antialiased; -webkit-hyphens: auto; -webkit-text-size-adjust: none; border-collapse: collapse !important; color: #222222; font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; font-size: 14px; font-weight: normal; hyphens: auto; line-height: 19px; margin: 0; padding: 0; text-align: left; vertical-align: top; visibility: hidden; width: 0px; word-break: break-word" align="left" valign="top">{{$ta.Name}}</td>
|
||||
<td class="expander" style="-moz-hyphens: auto; -webkit-font-smoothing: antialiased; -webkit-hyphens: auto; -webkit-text-size-adjust: none; border-collapse: collapse !important; color: #222222; font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; font-size: 14px; font-weight: normal; hyphens: auto; line-height: 19px; margin: 0; padding: 0; text-align: left; vertical-align: top; visibility: hidden; width: 0px; word-break: break-word" align="left" valign="top">{{$ta.State}}</td>
|
||||
<td class="expander" style="-moz-hyphens: auto; -webkit-font-smoothing: antialiased; -webkit-hyphens: auto; -webkit-text-size-adjust: none; border-collapse: collapse !important; color: #222222; font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; font-size: 14px; font-weight: normal; hyphens: auto; line-height: 19px; margin: 0; padding: 0; text-align: left; vertical-align: top; visibility: hidden; width: 0px; word-break: break-word" align="left" valign="top">{{$ta.ActualValue}}</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</table>
|
||||
{{end}}
|
||||
|
||||
|
||||
<table class="row footer" style="border-collapse: collapse; border-spacing: 0; display: block; margin-top: 20px; padding: 0px; position: relative; text-align: left; vertical-align: top; width: 100%">
|
||||
<tr style="padding: 0; text-align: left; vertical-align: top" align="left">
|
||||
<td class="wrapper last" style="-moz-hyphens: auto; -webkit-font-smoothing: antialiased; -webkit-hyphens: auto; -webkit-text-size-adjust: none; border-collapse: collapse !important; color: #222222; font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; font-size: 14px; font-weight: normal; hyphens: auto; line-height: 19px; margin: 0; padding: 10px 0px 0px; position: relative; text-align: left; vertical-align: top; word-break: break-word" align="left" valign="top">
|
||||
<table class="twelve columns" style="border-collapse: collapse; border-spacing: 0; margin: 0 auto; padding: 0; text-align: left; vertical-align: top; width: 580px">
|
||||
<tr style="padding: 0; text-align: left; vertical-align: top" align="left">
|
||||
<td align="center" style="-moz-hyphens: auto; -webkit-font-smoothing: antialiased; -webkit-hyphens: auto; -webkit-text-size-adjust: none; border-collapse: collapse !important; color: #222222; font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; font-size: 14px; font-weight: normal; hyphens: auto; line-height: 19px; margin: 0; padding: 0px 0px 10px; text-align: left; vertical-align: top; word-break: break-word" valign="top">
|
||||
<center style="min-width: 580px; width: 100%">
|
||||
<p style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; color: #222222; font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; font-size: 14px; font-weight: normal; line-height: 19px; margin: 0 0 10px; padding: 0; text-align: center" align="center">
|
||||
Sent by <a href="{{.AppUrl}}" style="color: #E67612; text-decoration: none">Grafana v{{.BuildVersion}}</a>
|
||||
</p>
|
||||
</center>
|
||||
</td>
|
||||
<td class="expander" style="-moz-hyphens: auto; -webkit-font-smoothing: antialiased; -webkit-hyphens: auto; -webkit-text-size-adjust: none; border-collapse: collapse !important; color: #222222; font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; font-size: 14px; font-weight: normal; hyphens: auto; line-height: 19px; margin: 0; padding: 0; text-align: left; vertical-align: top; visibility: hidden; width: 0px; word-break: break-word" align="left" valign="top"></td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
</center>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue
Block a user