Browse Source

Add Channel.name field, users can now name integrations.

pull/211/head
Pēteris Caune 6 years ago
parent
commit
21de50d84e
No known key found for this signature in database GPG Key ID: E28D7679E9A9EDE2
16 changed files with 385 additions and 276 deletions
  1. +1
    -0
      CHANGELOG.md
  2. +18
    -0
      hc/api/migrations/0043_channel_name.py
  3. +25
    -0
      hc/api/migrations/0044_auto_20181120_2004.py
  4. +6
    -2
      hc/api/models.py
  5. +4
    -0
      hc/front/forms.py
  6. +1
    -1
      hc/front/tests/test_add_sms.py
  7. +6
    -7
      hc/front/tests/test_channels.py
  8. +54
    -0
      hc/front/tests/test_update_channel_name.py
  9. +15
    -18
      hc/front/tests/test_update_name.py
  10. +1
    -0
      hc/front/urls.py
  11. +18
    -2
      hc/front/views.py
  12. +51
    -26
      static/css/channels.css
  13. +1
    -1
      static/css/my_checks_desktop.css
  14. +1
    -19
      static/js/channels.js
  15. +138
    -155
      templates/front/channels.html
  16. +45
    -45
      templates/front/update_name_modal.html

+ 1
- 0
CHANGELOG.md View File

@ -14,6 +14,7 @@ All notable changes to this project will be documented in this file.
- Add "channels" attribute to the Check API resource
- Can specify channel codes when updating a check via API
- Added a workaround for email agents automatically opening "Unsubscribe" links
- Add Channel.name field, users can now name integrations
### Bug Fixes
- During DST transition, handle ambiguous dates as pre-transition


+ 18
- 0
hc/api/migrations/0043_channel_name.py View File

@ -0,0 +1,18 @@
# Generated by Django 2.1.3 on 2018-11-20 16:50
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('api', '0042_auto_20181029_1522'),
]
operations = [
migrations.AddField(
model_name='channel',
name='name',
field=models.CharField(blank=True, max_length=100),
),
]

+ 25
- 0
hc/api/migrations/0044_auto_20181120_2004.py View File

@ -0,0 +1,25 @@
# Generated by Django 2.1.3 on 2018-11-20 20:04
import json
from django.db import migrations
def combine_channel_names(apps, schema_editor):
Channel = apps.get_model('api', 'Channel')
for channel in Channel.objects.filter(kind="sms"):
if channel.value.startswith("{"):
doc = json.loads(channel.value)
channel.name = doc.get("label", "")
channel.save()
class Migration(migrations.Migration):
dependencies = [
('api', '0043_channel_name'),
]
operations = [
migrations.RunPython(combine_channel_names),
]

+ 6
- 2
hc/api/models.py View File

@ -231,6 +231,7 @@ class Ping(models.Model):
class Channel(models.Model):
name = models.CharField(max_length=100, blank=True)
code = models.UUIDField(default=uuid.uuid4, editable=False)
user = models.ForeignKey(User, models.CASCADE)
created = models.DateTimeField(auto_now_add=True)
@ -240,11 +241,11 @@ class Channel(models.Model):
checks = models.ManyToManyField(Check)
def __str__(self):
if self.name:
return self.name
if self.kind == "email":
return "Email to %s" % self.value
elif self.kind == "sms":
if self.sms_label:
return "SMS to %s" % self.sms_label
return "SMS to %s" % self.sms_number
elif self.kind == "slack":
return "Slack %s" % self.slack_channel
@ -327,6 +328,9 @@ class Channel(models.Model):
return error
def icon_path(self):
return 'img/integrations/%s.png' % self.kind
@property
def po_value(self):
assert self.kind == "po"


+ 4
- 0
hc/front/forms.py View File

@ -108,3 +108,7 @@ class AddSmsForm(forms.Form):
error_css_class = "has-error"
label = forms.CharField(max_length=100, required=False)
value = forms.CharField(max_length=16, validators=[phone_validator])
class ChannelNameForm(forms.Form):
name = forms.CharField(max_length=100, required=False)

+ 1
- 1
hc/front/tests/test_add_sms.py View File

@ -31,7 +31,7 @@ class AddSmsTestCase(BaseTestCase):
c = Channel.objects.get()
self.assertEqual(c.kind, "sms")
self.assertEqual(c.sms_number, "+1234567890")
self.assertEqual(c.sms_label, "My Phone")
self.assertEqual(c.name, "My Phone")
def test_it_rejects_bad_number(self):
form = {"value": "not a phone number address"}


+ 6
- 7
hc/front/tests/test_channels.py View File

@ -33,9 +33,9 @@ class ChannelsTestCase(BaseTestCase):
self.assertEqual(r.status_code, 200)
# These are inside a modal:
self.assertContains(r, "<code>http://down.example.com</code>")
self.assertContains(r, "<code>http://up.example.com</code>")
self.assertContains(r, "<code>foobar</code>")
self.assertContains(r, "http://down.example.com")
self.assertContains(r, "http://up.example.com")
self.assertContains(r, "foobar")
def test_it_shows_pushover_details(self):
ch = Channel(kind="po", user=self.alice)
@ -46,7 +46,6 @@ class ChannelsTestCase(BaseTestCase):
r = self.client.get("/integrations/")
self.assertEqual(r.status_code, 200)
self.assertContains(r, "fake-key")
self.assertContains(r, "(normal priority)")
def test_it_shows_disabled_email(self):
@ -63,7 +62,7 @@ class ChannelsTestCase(BaseTestCase):
self.client.login(username="[email protected]", password="password")
r = self.client.get("/integrations/")
self.assertEqual(r.status_code, 200)
self.assertContains(r, "(bounced, disabled)")
self.assertContains(r, "Disabled")
def test_it_shows_unconfirmed_email(self):
channel = Channel(user=self.alice, kind="email")
@ -73,7 +72,7 @@ class ChannelsTestCase(BaseTestCase):
self.client.login(username="[email protected]", password="password")
r = self.client.get("/integrations/")
self.assertEqual(r.status_code, 200)
self.assertContains(r, "(unconfirmed)")
self.assertContains(r, "Unconfirmed")
def test_it_shows_sms_label(self):
ch = Channel(kind="sms", user=self.alice)
@ -84,4 +83,4 @@ class ChannelsTestCase(BaseTestCase):
r = self.client.get("/integrations/")
self.assertEqual(r.status_code, 200)
self.assertContains(r, "My Phone (+123)")
self.assertContains(r, "SMS to +123")

+ 54
- 0
hc/front/tests/test_update_channel_name.py View File

@ -0,0 +1,54 @@
from hc.api.models import Channel
from hc.test import BaseTestCase
class UpdateChannelNameTestCase(BaseTestCase):
def setUp(self):
super(UpdateChannelNameTestCase, self).setUp()
self.channel = Channel(kind="email", user=self.alice)
self.channel.save()
self.url = "/integrations/%s/name/" % self.channel.code
def test_it_works(self):
payload = {"name": "My work email"}
self.client.login(username="[email protected]", password="password")
r = self.client.post(self.url, data=payload)
self.assertRedirects(r, "/integrations/")
self.channel.refresh_from_db()
self.assertEqual(self.channel.name, "My work email")
def test_team_access_works(self):
payload = {"name": "Bob was here"}
# Logging in as bob, not alice. Bob has team access so this
# should work.
self.client.login(username="[email protected]", password="password")
self.client.post(self.url, data=payload)
self.channel.refresh_from_db()
self.assertEqual(self.channel.name, "Bob was here")
def test_it_checks_ownership(self):
payload = {"name": "Charlie Sent This"}
self.client.login(username="[email protected]", password="password")
r = self.client.post(self.url, data=payload)
self.assertEqual(r.status_code, 403)
def test_it_handles_missing_uuid(self):
# Valid UUID but there is no check for it:
url = "/integrations/6837d6ec-fc08-4da5-a67f-08a9ed1ccf62/name/"
payload = {"name": "Alice Was Here"}
self.client.login(username="[email protected]", password="password")
r = self.client.post(url, data=payload)
self.assertEqual(r.status_code, 404)
def test_it_rejects_get(self):
self.client.login(username="[email protected]", password="password")
r = self.client.get(self.url)
self.assertEqual(r.status_code, 405)

+ 15
- 18
hc/front/tests/test_update_name.py View File

@ -9,36 +9,35 @@ class UpdateNameTestCase(BaseTestCase):
self.check = Check(user=self.alice)
self.check.save()
self.url = "/checks/%s/name/" % self.check.code
def test_it_works(self):
url = "/checks/%s/name/" % self.check.code
payload = {"name": "Alice Was Here"}
self.client.login(username="[email protected]", password="password")
r = self.client.post(url, data=payload)
r = self.client.post(self.url, data=payload)
self.assertRedirects(r, "/checks/")
check = Check.objects.get(code=self.check.code)
assert check.name == "Alice Was Here"
self.check.refresh_from_db()
self.assertEqual(self.check.name, "Alice Was Here")
def test_team_access_works(self):
url = "/checks/%s/name/" % self.check.code
payload = {"name": "Bob Was Here"}
# Logging in as bob, not alice. Bob has team access so this
# should work.
self.client.login(username="[email protected]", password="password")
self.client.post(url, data=payload)
self.client.post(self.url, data=payload)
check = Check.objects.get(code=self.check.code)
assert check.name == "Bob Was Here"
self.check.refresh_from_db()
self.assertEqual(self.check.name, "Bob Was Here")
def test_it_checks_ownership(self):
url = "/checks/%s/name/" % self.check.code
payload = {"name": "Charlie Sent This"}
self.client.login(username="[email protected]", password="password")
r = self.client.post(url, data=payload)
assert r.status_code == 403
r = self.client.post(self.url, data=payload)
self.assertEqual(r.status_code, 403)
def test_it_handles_bad_uuid(self):
url = "/checks/not-uuid/name/"
@ -55,20 +54,18 @@ class UpdateNameTestCase(BaseTestCase):
self.client.login(username="[email protected]", password="password")
r = self.client.post(url, data=payload)
assert r.status_code == 404
self.assertEqual(r.status_code, 404)
def test_it_sanitizes_tags(self):
url = "/checks/%s/name/" % self.check.code
payload = {"tags": " foo bar\r\t \n baz \n"}
self.client.login(username="[email protected]", password="password")
self.client.post(url, data=payload)
self.client.post(self.url, data=payload)
check = Check.objects.get(id=self.check.id)
self.assertEqual(check.tags, "foo bar baz")
self.check.refresh_from_db()
self.assertEqual(self.check.tags, "foo bar baz")
def test_it_rejects_get(self):
url = "/checks/%s/name/" % self.check.code
self.client.login(username="[email protected]", password="password")
r = self.client.get(url)
r = self.client.get(self.url)
self.assertEqual(r.status_code, 405)

+ 1
- 0
hc/front/urls.py View File

@ -38,6 +38,7 @@ channel_urls = [
path('add_trello/', views.add_trello, name="hc-add-trello"),
path('add_trello/settings/', views.trello_settings, name="hc-trello-settings"),
path('<uuid:code>/checks/', views.channel_checks, name="hc-channel-checks"),
path('<uuid:code>/name/', views.update_channel_name, name="hc-channel-name"),
path('<uuid:code>/remove/', views.remove_channel, name="hc-remove-channel"),
path('<uuid:code>/verify/<slug:token>/', views.verify_email,
name="hc-verify-email"),


+ 18
- 2
hc/front/views.py View File

@ -22,7 +22,8 @@ from hc.api.models import (DEFAULT_GRACE, DEFAULT_TIMEOUT, Channel, Check,
from hc.api.transports import Telegram
from hc.front.forms import (AddWebhookForm, NameTagsForm,
TimeoutForm, AddUrlForm, AddEmailForm,
AddOpsGenieForm, CronForm, AddSmsForm)
AddOpsGenieForm, CronForm, AddSmsForm,
ChannelNameForm)
from hc.front.schemas import telegram_callback
from hc.front.templatetags.hc_extras import (num_down_title, down_title,
sortchecks)
@ -498,6 +499,21 @@ def channel_checks(request, code):
return render(request, "front/channel_checks.html", ctx)
@require_POST
@login_required
def update_channel_name(request, code):
channel = get_object_or_404(Channel, code=code)
if channel.user_id != request.team.user.id:
return HttpResponseForbidden()
form = ChannelNameForm(request.POST)
if form.is_valid():
channel.name = form.cleaned_data["name"]
channel.save()
return redirect("hc-channels")
def verify_email(request, code, token):
channel = get_object_or_404(Channel, code=code)
if channel.make_token() == token:
@ -1001,8 +1017,8 @@ def add_sms(request):
form = AddSmsForm(request.POST)
if form.is_valid():
channel = Channel(user=request.team.user, kind="sms")
channel.name = form.cleaned_data["label"]
channel.value = json.dumps({
"label": form.cleaned_data["label"],
"value": form.cleaned_data["value"]
})
channel.save()


+ 51
- 26
static/css/channels.css View File

@ -9,6 +9,26 @@
border-top: 1px solid #f1f1f1;
}
.channels-table .icon-cell {
width: 40px;
}
.channels-table .icon-cell img {
margin-left: 16px;
height: 40px;
}
.channels-table .th-name,
.channels-table .th-checks {
padding-left: 15px;
}
.channels-table .unnamed {
font-style: italic;
color: #999;
}
.channels-table .value-cell {
max-width: 400px;
overflow: hidden;
@ -20,22 +40,10 @@
background: #f5f5f5;
}
table.channels-table > tbody > tr > th {
border-top: 0;
}
.channels-table .channels-add-title {
border-top: 0;
padding-top: 20px
}
.channels-table .channels-add-help {
color: #888;
border-top: 0;
}
.word-up {
color: #5cb85c;
font-weight: bold
@ -58,20 +66,32 @@ table.channels-table > tbody > tr > th {
font-size: small;
}
.edit-name, .edit-checks {
padding: 12px 6px;
border: 1px solid #FFF;
cursor: pointer;
}
.channels-help-hidden {
display: none;
.edit-name .channel-details-mini {
font-size: 11.7px;
color: #888;
max-width: 500px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.channel-details-mini span {
color: #111;
}
.channel-row:hover .edit-name,
.channel-row:hover .edit-checks {
border: 1px dotted #AAA;
}
.edit-checks {
display: inline-block;
width: 120px;
padding: 6px;
text-align: center;
line-height: 28px;
border: 1px solid #FFF;
color: #333;
}
.edit-checks:hover {
@ -79,10 +99,6 @@ table.channels-table > tbody > tr > th {
color: #000;
}
.channel-row:hover .edit-checks {
border: 1px dotted #AAA;
}
.channel-remove {
visibility: hidden;
}
@ -91,6 +107,11 @@ table.channels-table > tbody > tr > th {
visibility: visible;
}
.webhook-details td {
max-width: 300px;
}
.ai-title {
margin-top: 2em;
}
@ -212,6 +233,10 @@ table.channels-table > tbody > tr > th {
font-style: italic;
}
.channel-modal .modal-body {
padding: 15px 40px;
}
/* Add Webhook */
@ -230,4 +255,4 @@ table.channels-table > tbody > tr > th {
.zendesk-subdomain input {
border-right: 0;
}
}

+ 1
- 1
static/css/my_checks_desktop.css View File

@ -36,7 +36,7 @@
#checks-table .integrations,
#checks-table .timeout-grace,
#checks-table .last-ping {
border: 1px solid rgba(0, 0, 0, 0);
border: 1px solid #FFF;
padding: 6px;
}


+ 1
- 19
static/js/channels.js View File

@ -1,30 +1,12 @@
$(function() {
var placeholders = {
email: "[email protected]",
webhook: "http://",
slack: "https://hooks.slack.com/...",
hipchat: "https://api.hipchat.com/...",
pd: "service key"
}
$("#add-channel-kind").change(function() {
$(".channels-add-help p").hide();
var v = $("#add-channel-kind").val();
$(".channels-add-help p." + v).show();
$("#add-channel-value").attr("placeholder", placeholders[v]);
});
$(".edit-checks").click(function() {
$("#checks-modal").modal("show");
var url = $(this).attr("href");
$.ajax(url).done(function(data) {
$.ajax(this.dataset.url).done(function(data) {
$("#checks-modal .modal-content").html(data);
})
return false;
});


+ 138
- 155
templates/front/channels.html View File

@ -18,114 +18,94 @@
<table class="table channels-table">
{% if channels %}
<tr>
<th>Type</th>
<th>Value</th>
<th>Assigned Checks</th>
<th></th>
<th class="th-name">Name, Details</th>
<th class="th-checks">Assigned Checks</th>
<th>Status</th>
<th>Last Notification</th>
<th></th>
</tr>
{% for ch in channels %}
{% with n=ch.latest_notification %}
<tr class="channel-row">
<td>{{ ch.get_kind_display }}</td>
<td class="value-cell">
{% if ch.kind == "email" %}
<span class="preposition">to</span>
{{ ch.value }}
{% if not ch.email_verified %}
{% if ch.latest_notification and ch.latest_notification.error %}
<span class="channel-disabled">
(bounced, disabled)
</span>
<td class="icon-cell">
<img src="{% static ch.icon_path %}"
class="icon"
alt="Slack icon"
data-toggle="tooltip"
title="Slack" />
</td>
<td>
<div class="edit-name" data-toggle="modal" data-target="#name-{{ ch.code }}">
{% if ch.name %}
{{ ch.name }}
{% else %}
<div class="unnamed">unnamed</div>
{% endif %}
<div class="channel-details-mini">
{% if ch.kind == "email" %}
Email to <span>{{ ch.value }}</span>
{% elif ch.kind == "pd" %}
PagerDuty account <span>{{ ch.pd_account }}</span>
{% elif ch.kind == "pagertree" %}
PagerTree
{% elif ch.kind == "opsgenie" %}
OpsGenie
{% elif ch.kind == "victorops" %}
VictorOps
{% elif ch.kind == "po" %}
Pushover ({{ ch.po_value|last }} priority)
{% elif ch.kind == "slack" %}
Slack
{% if ch.slack_team %}
team <span>{{ ch.slack_team }}</span>,
channel <span>{{ ch.slack_channel }}</span>
{% endif %}
{% elif ch.kind == "webhook" %}
Webhook
{% elif ch.kind == "pushbullet" %}
Pushbullet
{% elif ch.kind == "discord" %}
Discord
{% elif ch.kind == "telegram" %}
Telegram
{% if ch.telegram_type == "group" %}
chat <span>{{ ch.telegram_name }}</span>
{% elif ch.telegram_type == "private" %}
user <span>{{ ch.telegram_name }}</span>
{% endif %}
{% elif ch.kind == "hipchat" %}
HipChat
{% elif ch.kind == "sms" %}
SMS to <span>{{ ch.sms_number }}</span>
{% elif ch.kind == "trello" %}
Trello
board <span>{{ ch.trello_board_list|first }}</span>,
list <span>{{ ch.trello_board_list|last }}</span>
{% else %}
<span class="channel-unconfirmed">
(unconfirmed)
</span>
{{ ch.kind }}
{% endif %}
</span>
{% endif %}
{% elif ch.kind == "pd" %}
{% if ch.pd_account %}
<span class="preposition">account</span>
{{ ch.pd_account}},
{% endif %}
<span class="preposition">service key</span>
{{ ch.pd_service_key }}
{% elif ch.kind == "pagertree" %}
<span class="preposition">URL</span>
{{ ch.value }}
{% elif ch.kind == "opsgenie" %}
<span class="preposition">API key</span>
{{ ch.value }}
{% elif ch.kind == "victorops" %}
<span class="preposition">Post URL</span>
{{ ch.value }}
{% elif ch.kind == "po" %}
<span class="preposition">user key</span>
{{ ch.po_value|first }}
({{ ch.po_value|last }} priority)
{% elif ch.kind == "slack" %}
{% if ch.slack_team %}
<span class="preposition">team</span>
{{ ch.slack_team }},
<span class="preposition">channel</span>
{{ ch.slack_channel }}
{% else %}
{{ ch.value }}
{% endif %}
{% elif ch.kind == "webhook" %}
{% if ch.url_down %}
<span class="preposition">down</span> {{ ch.url_down }}
{% endif %}
{% if ch.url_up and not ch.url_down %}
<span class="preposition">up</span> {{ ch.url_up }}
{% endif %}
{% if ch.url_up or ch.post_data or ch.headers %}
<a href="#"
data-toggle="modal"
data-target="#channel-details-{{ ch.code }}">(details)</a>
{% endif %}
{% elif ch.kind == "pushbullet" %}
<span class="preposition">API key</span>
{{ ch.value }}
{% elif ch.kind == "discord" %}
{{ ch.discord_webhook_id }}
{% elif ch.kind == "telegram" %}
{% if ch.telegram_type == "group" %}
<span class="preposition">chat</span>
{% elif ch.telegram_type == "private" %}
<span class="preposition">user</span>
{% endif %}
{{ ch.telegram_name }}
{% elif ch.kind == "hipchat" %}
{{ ch.hipchat_webhook_url }}
{% elif ch.kind == "zendesk" %}
{{ ch.zendesk_subdomain }}.zendesk.com
{% elif ch.kind == "sms" %}
{% if ch.sms_label %}
{{ ch.sms_label }} ({{ ch.sms_number }})
</div>
</div>
</td>
<td>
<div class="edit-checks"
data-url="{% url 'hc-channel-checks' ch.code %}">
{{ ch.n_checks }} checks
</div>
</td>
<td>
{% if ch.kind == "email" and not ch.email_verified %}
{% if n and n.error %}
<span class="label label-danger">Disabled</span>
{% else %}
{{ ch.sms_number }}
<span class="label label-default">Unconfirmed</span>
{% endif %}
{% elif ch.kind == "trello" %}
<span class="preposition">board</span>
{{ ch.trello_board_list|first }},
<span class="preposition">list</span>
{{ ch.trello_board_list|last }}
{% else %}
{{ ch.value }}
Ready to deliver
{% endif %}
</td>
<td class="channels-num-checks">
<a
class="edit-checks"
href="{% url 'hc-channel-checks' ch.code %}">
{{ ch.n_checks }} of {{ num_checks }}
</a>
</td>
<td>
{% with n=ch.latest_notification %}
{% if n %}
{% if n.error %}
<span class="text-danger" data-toggle="tooltip" title="{{ n.error }}">
@ -140,7 +120,6 @@
{% if ch.kind == "sms" %}
<p>Used {{ profile.sms_sent_this_month }} of {{ profile.sms_limit }} sends this month.</p>
{% endif %}
{% endwith %}
</td>
<td>
<button
@ -155,6 +134,7 @@
</td>
</tr>
{% endwith %}
{% endfor %}
{% endif %}
</table>
@ -364,69 +344,72 @@
</div>
{% for ch in channels %}
{% if ch.kind == "webhook" %}
<div id="channel-details-{{ ch.code }}" class="modal channel-details">
<div id="name-{{ ch.code }}" class="modal channel-modal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">&times;</button>
<h4>Integration Details</h4>
</div>
<div class="modal-body">
<table>
<tr>
<th>Request Method</th>
<td>
{% if ch.post_data %}
POST
{% else %}
GET
{% endif %}
</td>
</tr>
<tr>
<th>URL for "down" events</th>
<td>
{% if ch.url_down %}
<code>{{ ch.url_down }}</code>
{% else %}
<span class="missing">(not set)</span>
{% endif %}
</td>
</tr>
{% if ch.url_up %}
<tr>
<th>URL for "up" events</th>
<td>
{% if ch.url_up %}
<code>{{ ch.url_up }}</code>
{% else %}
<span class="missing">(not set)</span>
{% endif %}
</td>
</tr>
{% endif %}
{% if ch.post_data %}
<tr>
<th>POST data</th>
<td><code>{{ ch.post_data }}</code></td>
</tr>
{% endif %}
{% for k, v in ch.headers.items %}
<tr>
<th>Header <code>{{ k }}</code></th>
<td><code>{{ v }}</code></td>
</tr>
{% endfor %}
</table>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<form
action="{% url 'hc-channel-name' ch.code %}"
class="form-horizontal"
method="post">
{% csrf_token %}
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">&times;</button>
<h4 class="update-timeout-title">Integration Details</h4>
</div>
<div class="modal-body">
<div class="form-group">
<label for="update-name-input" class="col-sm-2 control-label">
Name
</label>
<div class="col-sm-10">
<input
name="name"
type="text"
value="{{ ch.name }}"
placeholder="{{ ch }}"
class="input-name form-control" />
<span class="help-block">
Give this integration a human-friendly name,
so you can easily recognize it later.
</span>
</div>
</div>
{% if ch.kind == "webhook" %}
{% if ch.url_down %}
<p><strong>URL for "down" events</strong></p>
<pre>{{ ch.url_down }}</pre>
{% endif %}
{% if ch.url_up %}
<p><strong>URL for "up" events</strong></p>
<pre>{{ ch.url_up }}</pre>
{% endif %}
{% if ch.post_data %}
<p><strong>POST data</strong></p>
<pre>{{ ch.post_data }}</pre>
{% endif %}
{% for k, v in ch.headers.items %}
<p><strong>Header <code>{{ k }}</code></strong></p>
<pre>{{ v }}</pre>
{% endfor %}
{% endif %}
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-primary">Save</button>
</div>
</div>
</div>
</form>
</div>
</div>
{% endif %}
{% endfor %}
{% endblock %}


+ 45
- 45
templates/front/update_name_modal.html View File

@ -12,58 +12,58 @@
<h4 class="update-timeout-title">Name and Tags</h4>
</div>
<div class="modal-body">
<div class="form-group">
<label for="update-name-input" class="col-sm-2 control-label">
Name
</label>
<div class="col-sm-10">
<input
id="update-name-input"
name="name"
type="text"
value="{{ check.name }}"
placeholder="unnamed"
class="input-name form-control" />
<div class="form-group">
<label for="update-name-input" class="col-sm-2 control-label">
Name
</label>
<div class="col-sm-10">
<input
id="update-name-input"
name="name"
type="text"
value="{{ check.name }}"
placeholder="unnamed"
class="input-name form-control" />
<span class="help-block">
Give this check a human-friendly name,
so you can easily recognize it later.
</span>
</div>
<span class="help-block">
Give this check a human-friendly name,
so you can easily recognize it later.
</span>
</div>
</div>
<div class="form-group">
<label for="update-tags-input" class="col-sm-2 control-label">
Tags
</label>
<div class="col-sm-10">
<input
id="update-tags-input"
name="tags"
type="text"
value="{{ check.tags }}"
placeholder="production www"
class="form-control" />
<div class="form-group">
<label for="update-tags-input" class="col-sm-2 control-label">
Tags
</label>
<div class="col-sm-10">
<input
id="update-tags-input"
name="tags"
type="text"
value="{{ check.tags }}"
placeholder="production www"
class="form-control" />
<span class="help-block">
Use tags for easy filtering and for status badges.
Separate multiple tags with spaces.
</span>
</div>
<span class="help-block">
Use tags for easy filtering and for status badges.
Separate multiple tags with spaces.
</span>
</div>
</div>
<div class="form-group">
<label for="update-desc-input" class="col-sm-2 control-label">
Description
</label>
<div class="col-sm-10">
<textarea
id="update-desc-input"
class="form-control"
rows="5"
name="desc">{{ check.desc }}</textarea>
</div>
<div class="form-group">
<label for="update-desc-input" class="col-sm-2 control-label">
Description
</label>
<div class="col-sm-10">
<textarea
id="update-desc-input"
class="form-control"
rows="5"
name="desc">{{ check.desc }}</textarea>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>


Loading…
Cancel
Save