forked from GithubBackups/healthchecks
Split "Account Settings" page into subpages.
This commit is contained in:
parent
ec7fa1124f
commit
4906a5247c
20
hc/accounts/tests/test_badges.py
Normal file
20
hc/accounts/tests/test_badges.py
Normal file
@ -0,0 +1,20 @@
|
||||
from hc.test import BaseTestCase
|
||||
from hc.api.models import Check
|
||||
|
||||
|
||||
class BadgesTestCase(BaseTestCase):
|
||||
|
||||
def test_it_shows_badges(self):
|
||||
self.client.login(username="alice@example.org", password="password")
|
||||
Check.objects.create(user=self.alice, tags="foo a-B_1 baz@")
|
||||
Check.objects.create(user=self.bob, tags="bobs-tag")
|
||||
|
||||
r = self.client.get("/accounts/profile/badges/")
|
||||
self.assertContains(r, "foo.svg")
|
||||
self.assertContains(r, "a-B_1.svg")
|
||||
|
||||
# Expect badge URLs only for tags that match \w+
|
||||
self.assertNotContains(r, "baz@.svg")
|
||||
|
||||
# Expect only Alice's tags
|
||||
self.assertNotContains(r, "bobs-tag.svg")
|
24
hc/accounts/tests/test_notifications.py
Normal file
24
hc/accounts/tests/test_notifications.py
Normal file
@ -0,0 +1,24 @@
|
||||
from hc.test import BaseTestCase
|
||||
|
||||
|
||||
class NotificationsTestCase(BaseTestCase):
|
||||
|
||||
def test_it_saves_reports_allowed_true(self):
|
||||
self.client.login(username="alice@example.org", password="password")
|
||||
|
||||
form = {"reports_allowed": "on"}
|
||||
r = self.client.post("/accounts/profile/notifications/", form)
|
||||
assert r.status_code == 200
|
||||
|
||||
self.alice.profile.refresh_from_db()
|
||||
self.assertTrue(self.alice.profile.reports_allowed)
|
||||
|
||||
def test_it_saves_reports_allowed_false(self):
|
||||
self.client.login(username="alice@example.org", password="password")
|
||||
|
||||
form = {}
|
||||
r = self.client.post("/accounts/profile/notifications/", form)
|
||||
assert r.status_code == 200
|
||||
|
||||
self.alice.profile.refresh_from_db()
|
||||
self.assertFalse(self.alice.profile.reports_allowed)
|
@ -123,18 +123,3 @@ class ProfileTestCase(BaseTestCase):
|
||||
# to user's default team.
|
||||
self.bobs_profile.refresh_from_db()
|
||||
self.assertEqual(self.bobs_profile.current_team, self.bobs_profile)
|
||||
|
||||
def test_it_shows_badges(self):
|
||||
self.client.login(username="alice@example.org", password="password")
|
||||
Check.objects.create(user=self.alice, tags="foo a-B_1 baz@")
|
||||
Check.objects.create(user=self.bob, tags="bobs-tag")
|
||||
|
||||
r = self.client.get("/accounts/profile/")
|
||||
self.assertContains(r, "foo.svg")
|
||||
self.assertContains(r, "a-B_1.svg")
|
||||
|
||||
# Expect badge URLs only for tags that match \w+
|
||||
self.assertNotContains(r, "baz@.svg")
|
||||
|
||||
# Expect only Alice's tags
|
||||
self.assertNotContains(r, "bobs-tag.svg")
|
||||
|
@ -14,6 +14,8 @@ urlpatterns = [
|
||||
views.check_token, name="hc-check-token"),
|
||||
|
||||
url(r'^profile/$', views.profile, name="hc-profile"),
|
||||
url(r'^profile/notifications/$', views.notifications, name="hc-notifications"),
|
||||
url(r'^profile/badges/$', views.badges, name="hc-badges"),
|
||||
|
||||
url(r'^unsubscribe_reports/([\w-]+)/$',
|
||||
views.unsubscribe_reports, name="hc-unsubscribe-reports"),
|
||||
|
@ -169,12 +169,6 @@ def profile(request):
|
||||
messages.info(request, "The API key has been revoked!")
|
||||
elif "show_api_key" in request.POST:
|
||||
show_api_key = True
|
||||
elif "update_reports_allowed" in request.POST:
|
||||
form = ReportSettingsForm(request.POST)
|
||||
if form.is_valid():
|
||||
profile.reports_allowed = form.cleaned_data["reports_allowed"]
|
||||
profile.save()
|
||||
messages.success(request, "Your settings have been updated!")
|
||||
elif "invite_team_member" in request.POST:
|
||||
if not profile.team_access_allowed:
|
||||
return HttpResponseForbidden()
|
||||
@ -213,6 +207,48 @@ def profile(request):
|
||||
profile.save()
|
||||
messages.success(request, "Team Name updated!")
|
||||
|
||||
ctx = {
|
||||
"page": "profile",
|
||||
"profile": profile,
|
||||
"show_api_key": show_api_key
|
||||
}
|
||||
|
||||
return render(request, "accounts/profile.html", ctx)
|
||||
|
||||
|
||||
@login_required
|
||||
def notifications(request):
|
||||
profile = request.user.profile
|
||||
# Switch user back to its default team
|
||||
if profile.current_team_id != profile.id:
|
||||
request.team = profile
|
||||
profile.current_team_id = profile.id
|
||||
profile.save()
|
||||
|
||||
if request.method == "POST":
|
||||
form = ReportSettingsForm(request.POST)
|
||||
if form.is_valid():
|
||||
profile.reports_allowed = form.cleaned_data["reports_allowed"]
|
||||
profile.save()
|
||||
messages.success(request, "Your settings have been updated!")
|
||||
|
||||
ctx = {
|
||||
"page": "profile",
|
||||
"profile": profile,
|
||||
}
|
||||
|
||||
return render(request, "accounts/notifications.html", ctx)
|
||||
|
||||
|
||||
@login_required
|
||||
def badges(request):
|
||||
profile = request.user.profile
|
||||
# Switch user back to its default team
|
||||
if profile.current_team_id != profile.id:
|
||||
request.team = profile
|
||||
profile.current_team_id = profile.id
|
||||
profile.save()
|
||||
|
||||
tags = set()
|
||||
for check in Check.objects.filter(user=request.team.user):
|
||||
tags.update(check.tags_list())
|
||||
@ -228,11 +264,9 @@ def profile(request):
|
||||
ctx = {
|
||||
"page": "profile",
|
||||
"badge_urls": badge_urls,
|
||||
"profile": profile,
|
||||
"show_api_key": show_api_key
|
||||
}
|
||||
|
||||
return render(request, "accounts/profile.html", ctx)
|
||||
return render(request, "accounts/badges.html", ctx)
|
||||
|
||||
|
||||
@login_required
|
||||
@ -286,11 +320,11 @@ def switch_team(request, target_username):
|
||||
# Superuser can switch to any team.
|
||||
access_ok = request.user.is_superuser
|
||||
|
||||
# Users can switch to teams they are members of.
|
||||
# Users can switch to their own teams.
|
||||
if not access_ok and other_user.id == request.user.id:
|
||||
access_ok = True
|
||||
|
||||
# Users can switch to their own teams.
|
||||
# Users can switch to teams they are members of.
|
||||
if not access_ok:
|
||||
for membership in request.user.member_set.all():
|
||||
if membership.team.user.id == other_user.id:
|
||||
|
@ -79,3 +79,7 @@ body {
|
||||
pre {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.nav-pills > li > a {
|
||||
color: #888;
|
||||
}
|
||||
|
4
static/css/bootstrap.css
vendored
4
static/css/bootstrap.css
vendored
@ -3274,8 +3274,8 @@ select[multiple].input-group-sm > .input-group-btn > .btn {
|
||||
.nav-pills > li.active > a,
|
||||
.nav-pills > li.active > a:hover,
|
||||
.nav-pills > li.active > a:focus {
|
||||
color: #ffffff;
|
||||
background-color: #22bc66;
|
||||
color: #888888;
|
||||
background-color: #eeeeee;
|
||||
}
|
||||
.nav-stacked > li {
|
||||
float: none;
|
||||
|
@ -440,8 +440,8 @@
|
||||
|
||||
//== Pills
|
||||
@nav-pills-border-radius: @border-radius-base;
|
||||
@nav-pills-active-link-hover-bg: @component-active-bg;
|
||||
@nav-pills-active-link-hover-color: @component-active-color;
|
||||
@nav-pills-active-link-hover-bg: #EEE;
|
||||
@nav-pills-active-link-hover-color: #888;
|
||||
|
||||
|
||||
//== Pagination
|
||||
|
63
templates/accounts/badges.html
Normal file
63
templates/accounts/badges.html
Normal file
@ -0,0 +1,63 @@
|
||||
{% extends "base.html" %}
|
||||
{% load compress staticfiles hc_extras %}
|
||||
|
||||
{% block title %}Account Settings - {% site_name %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<h1 class="settings-title">Settings</h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-3">
|
||||
<ul class="nav nav-pills nav-stacked">
|
||||
<li><a href="{% url 'hc-profile' %}">Security</a></li>
|
||||
<li><a href="{% url 'hc-notifications' %}">Notifications</a></li>
|
||||
<li class="active"><a href="{% url 'hc-badges' %}">Badges</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-9">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-body settings-block">
|
||||
<h2 class="settings-title">Status Badges</h2>
|
||||
<p id="badges-description">
|
||||
healthchecks.io provides status badges for each of the tags
|
||||
you have used. The badges have public, but hard-to-guess
|
||||
URLs. If you wish, you can add them to your READMEs,
|
||||
dashboards or status pages.
|
||||
</p>
|
||||
{% if badge_urls %}
|
||||
<table class="badges table">
|
||||
{% for badge_url in badge_urls %}
|
||||
<tr>
|
||||
<td>
|
||||
<img src="{{ badge_url }}" alt="" />
|
||||
</td>
|
||||
<td>
|
||||
<code>{{ badge_url }}</code>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% else %}
|
||||
<p>
|
||||
To get started with status badges, add some tags to
|
||||
your checks!
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
{% compress js %}
|
||||
<script src="{% static 'js/jquery-2.1.4.min.js' %}"></script>
|
||||
<script src="{% static 'js/bootstrap.min.js' %}"></script>
|
||||
{% endcompress %}
|
||||
{% endblock %}
|
58
templates/accounts/notifications.html
Normal file
58
templates/accounts/notifications.html
Normal file
@ -0,0 +1,58 @@
|
||||
{% extends "base.html" %}
|
||||
{% load compress staticfiles hc_extras %}
|
||||
|
||||
{% block title %}Account Settings - {% site_name %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<h1 class="settings-title">Settings</h1>
|
||||
</div>
|
||||
{% if messages %}
|
||||
<div class="col-sm-12">
|
||||
{% for message in messages %}
|
||||
<p class="alert alert-{{ message.tags }}">{{ message }}</p>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-3">
|
||||
<ul class="nav nav-pills nav-stacked">
|
||||
<li><a href="{% url 'hc-profile' %}">Account</a></li>
|
||||
<li class="active"><a href="{% url 'hc-notifications' %}">Notifications</a></li>
|
||||
<li><a href="{% url 'hc-badges' %}">Badges</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-6">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-body settings-block">
|
||||
<h2>Monthly Reports</h2>
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<label>
|
||||
<input
|
||||
name="reports_allowed"
|
||||
type="checkbox"
|
||||
{% if profile.reports_allowed %} checked {% endif %}>
|
||||
Each month send me a summary of my checks
|
||||
</label>
|
||||
<button
|
||||
name="update_reports_allowed"
|
||||
type="submit"
|
||||
class="btn btn-default pull-right">Save</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
{% compress js %}
|
||||
<script src="{% static 'js/jquery-2.1.4.min.js' %}"></script>
|
||||
<script src="{% static 'js/bootstrap.min.js' %}"></script>
|
||||
{% endcompress %}
|
||||
{% endblock %}
|
@ -19,26 +19,13 @@
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-6">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-body settings-block">
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<h2>Monthly Reports</h2>
|
||||
<label>
|
||||
<input
|
||||
name="reports_allowed"
|
||||
type="checkbox"
|
||||
{% if profile.reports_allowed %} checked {% endif %}>
|
||||
Each month send me a summary of my checks
|
||||
</label>
|
||||
<button
|
||||
name="update_reports_allowed"
|
||||
type="submit"
|
||||
class="btn btn-default pull-right">Save</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-3">
|
||||
<ul class="nav nav-pills nav-stacked">
|
||||
<li class="active"><a href="{% url 'hc-profile' %}">Account</a></li>
|
||||
<li><a href="{% url 'hc-notifications' %}">Notifications</a></li>
|
||||
<li><a href="{% url 'hc-badges' %}">Badges</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-6">
|
||||
@ -55,9 +42,44 @@
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-6">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-body settings-block">
|
||||
<h2>API Access</h2>
|
||||
{% if profile.api_key %}
|
||||
{% if show_api_key %}
|
||||
API key: <code>{{ profile.api_key }}</code>
|
||||
<button
|
||||
data-toggle="modal"
|
||||
data-target="#revoke-api-key-modal"
|
||||
class="btn btn-danger pull-right">Revoke</button>
|
||||
|
||||
{% else %}
|
||||
<form method="post">
|
||||
<span class="icon-ok"></span>
|
||||
API access is enabled.
|
||||
{% csrf_token %}
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
name="show_api_key"
|
||||
class="btn btn-default pull-right">Show API key</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<span class="icon-cancel"></span>
|
||||
API access is disabled.
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<button
|
||||
type="submit"
|
||||
name="create_api_key"
|
||||
class="btn btn-default pull-right">Create API key</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-body settings-block">
|
||||
<h2>Team Access</h2>
|
||||
@ -115,78 +137,8 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-6">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-body settings-block">
|
||||
<h2>API Access</h2>
|
||||
{% if profile.api_key %}
|
||||
{% if show_api_key %}
|
||||
API key: <code>{{ profile.api_key }}</code>
|
||||
<button
|
||||
data-toggle="modal"
|
||||
data-target="#revoke-api-key-modal"
|
||||
class="btn btn-danger pull-right">Revoke</button>
|
||||
|
||||
{% else %}
|
||||
<span class="icon-ok"></span>
|
||||
API access is enabled.
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
name="show_api_key"
|
||||
class="btn btn-default pull-right">Show API key</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<span class="icon-cancel"></span>
|
||||
API access is disabled.
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<button
|
||||
type="submit"
|
||||
name="create_api_key"
|
||||
class="btn btn-default pull-right">Create API key</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if badge_urls %}
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-body settings-block">
|
||||
<h2 class="settings-title">Status Badges</h2>
|
||||
<p id="badges-description">
|
||||
Here are status badges for each of the tags you have used. The
|
||||
badges have public, but hard-to-guess URLs. If you wish, you can
|
||||
add them to your READMEs, dashboards or status pages.
|
||||
</p>
|
||||
<table class="badges table">
|
||||
{% for badge_url in badge_urls %}
|
||||
<tr>
|
||||
<td>
|
||||
<img src="{{ badge_url }}" alt="" />
|
||||
</td>
|
||||
<td>
|
||||
<code>{{ badge_url }}</code>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
|
||||
|
||||
<div id="revoke-api-key-modal" class="modal">
|
||||
<div class="modal-dialog">
|
||||
<form id="revoke-api-key-form" method="post">
|
||||
|
@ -8,8 +8,8 @@
|
||||
<meta name="keywords" content="monitor cron jobs daemon background worker service cronjob monitoring crontab alert notify cronitor deadmanssnitch webhook">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<link href='//fonts.googleapis.com/css?family=Open+Sans:400,300,600' rel='stylesheet' type='text/css'>
|
||||
{% load compress staticfiles %}
|
||||
<!-- <link href='//fonts.googleapis.com/css?family=Open+Sans:400,300,600' rel='stylesheet' type='text/css'>
|
||||
--> {% load compress staticfiles %}
|
||||
<link rel="icon" type="image/x-icon" href="{% static 'img/favicon.ico' %}">
|
||||
|
||||
{% compress css %}
|
||||
|
Loading…
x
Reference in New Issue
Block a user