@ -9,7 +9,7 @@ class BadgesTestCase(BaseTestCase): | |||||
Check.objects.create(project=self.bobs_project, tags="bobs-tag") | Check.objects.create(project=self.bobs_project, tags="bobs-tag") | ||||
self.client.login(username="[email protected]", password="password") | self.client.login(username="[email protected]", password="password") | ||||
r = self.client.get("/accounts/profile/badges/") | |||||
r = self.client.get("/projects/%s/badges/" % self.project.code) | |||||
self.assertContains(r, "foo.svg") | self.assertContains(r, "foo.svg") | ||||
self.assertContains(r, "a-B_1.svg") | self.assertContains(r, "a-B_1.svg") | ||||
@ -27,6 +27,6 @@ class BadgesTestCase(BaseTestCase): | |||||
self.project.save() | self.project.save() | ||||
self.client.login(username="[email protected]", password="password") | self.client.login(username="[email protected]", password="password") | ||||
r = self.client.get("/accounts/profile/badges/") | |||||
r = self.client.get("/projects/%s/badges/" % self.project.code) | |||||
self.assertContains(r, "badge/alices-badge-key/") | self.assertContains(r, "badge/alices-badge-key/") | ||||
self.assertContains(r, "badge/alices-badge-key/") | self.assertContains(r, "badge/alices-badge-key/") |
@ -5,17 +5,22 @@ from hc.test import BaseTestCase | |||||
from hc.accounts.models import Member | from hc.accounts.models import Member | ||||
class ProfileTestCase(BaseTestCase): | |||||
class ProjectTestCase(BaseTestCase): | |||||
def setUp(self): | def setUp(self): | ||||
super(ProfileTestCase, self).setUp() | |||||
super(ProjectTestCase, self).setUp() | |||||
self.url = "/projects/%s/settings/" % self.project.code | self.url = "/projects/%s/settings/" % self.project.code | ||||
def test_it_checks_access(self): | def test_it_checks_access(self): | ||||
self.client.login(username="bob@example.org", password="password") | |||||
self.client.login(username="charlie@example.org", password="password") | |||||
r = self.client.get(self.url) | r = self.client.get(self.url) | ||||
self.assertEqual(r.status_code, 404) | self.assertEqual(r.status_code, 404) | ||||
def test_it_allows_team_access(self): | |||||
self.client.login(username="[email protected]", password="password") | |||||
r = self.client.get(self.url) | |||||
self.assertContains(r, "Change Project Name") | |||||
def test_it_shows_api_keys(self): | def test_it_shows_api_keys(self): | ||||
self.project.api_key_readonly = "R" * 32 | self.project.api_key_readonly = "R" * 32 | ||||
self.project.save() | self.project.save() | ||||
@ -78,6 +83,13 @@ class ProfileTestCase(BaseTestCase): | |||||
" Alice's Project on %s" % settings.SITE_NAME) | " Alice's Project on %s" % settings.SITE_NAME) | ||||
self.assertEqual(mail.outbox[0].subject, subj) | self.assertEqual(mail.outbox[0].subject, subj) | ||||
def test_it_requires_owner_to_add_team_member(self): | |||||
self.client.login(username="[email protected]", password="password") | |||||
form = {"invite_team_member": "1", "email": "[email protected]"} | |||||
r = self.client.post(self.url, form) | |||||
self.assertEqual(r.status_code, 403) | |||||
def test_it_checks_team_size(self): | def test_it_checks_team_size(self): | ||||
self.profile.team_limit = 0 | self.profile.team_limit = 0 | ||||
self.profile.save() | self.profile.save() | ||||
@ -100,6 +112,13 @@ class ProfileTestCase(BaseTestCase): | |||||
self.bobs_profile.refresh_from_db() | self.bobs_profile.refresh_from_db() | ||||
self.assertEqual(self.bobs_profile.current_project, None) | self.assertEqual(self.bobs_profile.current_project, None) | ||||
def test_it_requires_owner_to_remove_team_member(self): | |||||
self.client.login(username="[email protected]", password="password") | |||||
form = {"remove_team_member": "1", "email": "[email protected]"} | |||||
r = self.client.post(self.url, form) | |||||
self.assertEqual(r.status_code, 403) | |||||
def test_it_checks_membership_when_removing_team_member(self): | def test_it_checks_membership_when_removing_team_member(self): | ||||
self.client.login(username="[email protected]", password="password") | self.client.login(username="[email protected]", password="password") | ||||
@ -1,93 +0,0 @@ | |||||
{% extends "base.html" %} | |||||
{% load compress static hc_extras %} | |||||
{% block title %}Account Settings - {% site_name %}{% endblock %} | |||||
{% block content %} | |||||
<div class="row"> | |||||
<div class="col-sm-12"> | |||||
<h1 class="settings-title">Settings <small>{{ request.user.email }}</small></h1> | |||||
</div> | |||||
</div> | |||||
<div class="row"> | |||||
<div class="col-sm-2"> | |||||
<ul class="nav nav-pills nav-stacked"> | |||||
<li><a href="{% url 'hc-profile' %}">Account</a></li> | |||||
{% if show_pricing %} | |||||
<li><a href="{% url 'hc-billing' %}">Billing</a></li> | |||||
{% endif %} | |||||
<li><a href="{% url 'hc-notifications' %}">Email Reports</a></li> | |||||
<li class="active"><a href="{% url 'hc-badges' %}">Badges</a></li> | |||||
</ul> | |||||
</div> | |||||
<div class="col-sm-10"> | |||||
<div class="panel panel-default"> | |||||
<div class="panel-body settings-block"> | |||||
<h2 class="settings-title">Status Badges</h2> | |||||
<p id="badges-description"> | |||||
{% site_name %} provides status badges for each of the tags | |||||
you have used. Additionally, the "{% site_name %}" | |||||
badge shows the overall status of all checks in a | |||||
project. The badges have public, but hard-to-guess | |||||
URLs. You can use them in your READMEs, | |||||
dashboards or status pages. | |||||
</p> | |||||
<div id="b-format" class="btn-group" data-toggle="buttons"> | |||||
<label id="show-svg" class="btn btn-default active"> | |||||
<input type="radio" autocomplete="off" checked> SVG | |||||
</label> | |||||
<label id="show-json" class="btn btn-default"> | |||||
<input type="radio" autocomplete="off"> JSON | |||||
</label> | |||||
</div> | |||||
<table id="badges-svg" class="badges table"> | |||||
{% for badge_set in badges %} | |||||
<tr> | |||||
<th colspan="2">{{ badge_set.project }}</th> | |||||
</tr> | |||||
{% for urldict in badge_set.urls %} | |||||
<tr> | |||||
<td> | |||||
<img src="{{ urldict.svg }}" alt="" /> | |||||
</td> | |||||
<td class="svg-url"> | |||||
<code>{{ urldict.svg }}</code> | |||||
</td> | |||||
</tr> | |||||
{% endfor %} | |||||
{% endfor %} | |||||
</table> | |||||
<table id="badges-json" class="badges table"> | |||||
{% for badge_set in badges %} | |||||
<tr> | |||||
<th colspan="2">{{ badge_set.project }}</th> | |||||
</tr> | |||||
{% for urldict in badge_set.urls %} | |||||
<tr> | |||||
<td class="json-response" data-url="{{ urldict.json }}"> | |||||
</td> | |||||
<td class="json-url"> | |||||
<code>{{ urldict.json }}</code> | |||||
</td> | |||||
</tr> | |||||
{% endfor %} | |||||
{% endfor %} | |||||
</table> | |||||
</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> | |||||
<script src="{% static 'js/badges.js' %}"></script> | |||||
{% endcompress %} | |||||
{% endblock %} |
@ -0,0 +1,169 @@ | |||||
<!DOCTYPE html>{% load compress staticfiles hc_extras %} | |||||
<html lang="en"> | |||||
<head> | |||||
<meta charset="utf-8"> | |||||
<title>{% block title %}{% site_name %} - Monitor Cron Jobs. Get Notified When Your Cron Jobs Fail{% endblock %}</title> | |||||
{% block description %} | |||||
<meta name="description" content="Monitor and Get Notified When Your Cron Jobs Fail. Free alternative to Cronitor and Dead Man's Snitch."> | |||||
{% endblock %} | |||||
{% block keywords %} | |||||
<meta name="keywords" content="healthchecks, monitor cron jobs, cron monitoring, cron job syntax, health checks, crontab cheat sheet, crontab monitoring, cronjob monitoring, cron dashboard"> | |||||
{% endblock %} | |||||
<meta name="viewport" content="width=device-width, initial-scale=1"> | |||||
<meta name="apple-mobile-web-app-title" content="{% site_name %}"> | |||||
<meta name="application-name" content="{% site_name %}"> | |||||
<link rel="icon" type="image/x-icon" href="{% static 'img/favicon.ico' %}"> | |||||
<link rel="apple-touch-icon" sizes="180x180" href="{% static 'img/apple-touch-180.png' %}"> | |||||
{% compress css %} | |||||
<link rel="stylesheet" href="{% static 'css/bootstrap.css' %}" type="text/css"> | |||||
<link rel="stylesheet" href="{% static 'css/icomoon.css' %}" type="text/css"> | |||||
<link rel="stylesheet" href="{% static 'css/nouislider.min.css' %}" type="text/css"> | |||||
<link rel="stylesheet" href="{% static 'css/nouislider.pips.css' %}" type="text/css"> | |||||
<link rel="stylesheet" href="{% static 'css/bootstrap-select.min.css' %}" type="text/css"> | |||||
<link rel="stylesheet" href="{% static 'css/snippet-copy.css' %}" type="text/css"> | |||||
<link rel="stylesheet" href="{% static 'css/base.css' %}" type="text/css"> | |||||
<link rel="stylesheet" href="{% static 'css/docs.css' %}" type="text/css"> | |||||
<link rel="stylesheet" href="{% static 'css/docs_cron.css' %}" type="text/css"> | |||||
<link rel="stylesheet" href="{% static 'css/welcome.css' %}" type="text/css"> | |||||
<link rel="stylesheet" href="{% static 'css/my_checks.css' %}" type="text/css"> | |||||
<link rel="stylesheet" href="{% static 'css/my_checks_desktop.css' %}" type="text/css"> | |||||
<link rel="stylesheet" href="{% static 'css/pricing.css' %}" type="text/css"> | |||||
<link rel="stylesheet" href="{% static 'css/syntax.css' %}" type="text/css"> | |||||
<link rel="stylesheet" href="{% static 'css/channels.css' %}" type="text/css"> | |||||
<link rel="stylesheet" href="{% static 'css/channel_checks.css' %}" type="text/css"> | |||||
<link rel="stylesheet" href="{% static 'css/details.css' %}" type="text/css"> | |||||
<link rel="stylesheet" href="{% static 'css/log.css' %}" type="text/css"> | |||||
<link rel="stylesheet" href="{% static 'css/add_pushover.css' %}" type="text/css"> | |||||
<link rel="stylesheet" href="{% static 'css/add_webhook.css' %}" type="text/css"> | |||||
<link rel="stylesheet" href="{% static 'css/settings.css' %}" type="text/css"> | |||||
<link rel="stylesheet" href="{% static 'css/ping_details.css' %}" type="text/css"> | |||||
<link rel="stylesheet" href="{% static 'css/profile.css' %}" type="text/css"> | |||||
<link rel="stylesheet" href="{% static 'css/checkbox.css' %}" type="text/css"> | |||||
<link rel="stylesheet" href="{% static 'css/radio.css' %}" type="text/css"> | |||||
<link rel="stylesheet" href="{% static 'css/billing.css' %}" type="text/css"> | |||||
<link rel="stylesheet" href="{% static 'css/login.css' %}" type="text/css"> | |||||
<link rel="stylesheet" href="{% static 'css/projects.css' %}" type="text/css"> | |||||
<link rel="stylesheet" href="{% static 'css/add_project_modal.css' %}" type="text/css"> | |||||
{% endcompress %} | |||||
</head> | |||||
<body class="page-{{ page }}"> | |||||
{% debug_warning %} | |||||
<nav class="navbar navbar-default"> | |||||
<div class="container{% if page == "checks" or page == "details" %}-fluid{% endif %}"> | |||||
<div class="navbar-header"> | |||||
<button | |||||
type="button" | |||||
class="navbar-toggle collapsed" | |||||
data-toggle="collapse" | |||||
data-target="#navbar" | |||||
aria-expanded="false" | |||||
aria-controls="navbar"> | |||||
<span class="sr-only">Toggle navigation</span> | |||||
<span class="icon-bar"></span> | |||||
<span class="icon-bar"></span> | |||||
<span class="icon-bar"></span> | |||||
</button> | |||||
<a class="navbar-brand" href="/"> | |||||
{{ project }} | |||||
<span class="caret"></span> | |||||
</a> | |||||
</div> | |||||
<div id="navbar" class="navbar-collapse collapse"> | |||||
<ul id="nav-main-sections" class="nav navbar-nav"> | |||||
{% if project %} | |||||
<li {% if page == 'checks' %} class="active" {% endif %}> | |||||
<a href="{% url 'hc-checks' project.code %}">Checks</a> | |||||
</li> | |||||
<li {% if page == 'channels' %} class="active" {% endif %}> | |||||
<a href="{% url 'hc-channels' %}">Integrations</a> | |||||
</li> | |||||
<li {% if page == 'badges' %} class="active" {% endif %}> | |||||
<a href="{% url 'hc-badges' project.code %}">Badges</a> | |||||
</li> | |||||
<li {% if page == 'project' %} class="active" {% endif %}> | |||||
<a href="{% url 'hc-project-settings' project.code %}">Settings</a> | |||||
</li> | |||||
{% endif %} | |||||
</ul> | |||||
{% if request.user.is_authenticated %} | |||||
<ul id="global-links" class="nav navbar-nav navbar-right"> | |||||
{% if show_pricing %} | |||||
<li {% if page == 'pricing' %} class="active" {% endif %}> | |||||
<a href="{% url 'hc-pricing' %}">Pricing</a> | |||||
</li> | |||||
{% endif %} | |||||
<li {% if page == 'docs' %} class="active" {% endif %}> | |||||
<a href="{% url 'hc-docs' %}">Docs</a> | |||||
</li> | |||||
<li class="dropdown"> | |||||
<a id="nav-email" href="#" class="dropdown-toggle" data-toggle="dropdown" role="button"> | |||||
Account | |||||
<span class="caret"></span> | |||||
</a> | |||||
<ul class="dropdown-menu"> | |||||
<li class="dropdown-header">Projects</li> | |||||
{% for project in request.profile.annotated_projects %} | |||||
<li class="project-item"> | |||||
<a href="{% url 'hc-checks' project.code %}"> | |||||
<span class="name">{{ project }}</span> | |||||
{% if project.n_down %} | |||||
<span class="badge badge-down pull-right"> | |||||
{{ project.n_down }} | |||||
</span> | |||||
{% endif %} | |||||
</a> | |||||
</li> | |||||
{% endfor %} | |||||
<li role="separator" class="divider"></li> | |||||
<li><a href="{% url 'hc-profile' %}">Account Settings</a></li> | |||||
<li><a href="{% url 'hc-logout' %}">Log Out</a></li> | |||||
</ul> | |||||
</li> | |||||
</ul> | |||||
{% elif page != "login" %} | |||||
<ul class="nav navbar-nav navbar-right"> | |||||
<li><a href="{% url 'hc-login' %}">Sign In</a></li> | |||||
</ul> | |||||
{% endif %} | |||||
</div> | |||||
</div> | |||||
</nav> | |||||
{% block containers %} | |||||
<div class="container{% if page == "checks" or page == "details" %}-fluid{% endif %}"> | |||||
{% block content %}{% endblock %} | |||||
</div> | |||||
{% endblock %} | |||||
<footer class="footer"> | |||||
<div class="container{% if page == "checks" or page == "details" %}-fluid{% endif %}"> | |||||
<ul> | |||||
<li> | |||||
Powered by Healthchecks open-source project | |||||
(<a href="https://github.com/healthchecks/healthchecks">github</a>, | |||||
<a href="https://healthchecks.io">healthchecks.io</a>) | |||||
</li> | |||||
</ul> | |||||
</div> | |||||
</footer> | |||||
{% 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 %} | |||||
</body> | |||||
</html> |
@ -0,0 +1,86 @@ | |||||
{% extends "base_project.html" %} | |||||
{% load compress static hc_extras %} | |||||
{% block title %}Account Settings - {% site_name %}{% endblock %} | |||||
{% block content %} | |||||
<div class="row"> | |||||
<div class="col-sm-10"> | |||||
<h1>Status Badges</h1> | |||||
<p id="badges-description"> | |||||
{% site_name %} provides status badges for each of the tags | |||||
you have used. Additionally, the "{% site_name %}" | |||||
badge shows the overall status of all checks in a | |||||
project. The badges have public, but hard-to-guess | |||||
URLs. You can use them in your READMEs, | |||||
dashboards or status pages. | |||||
</p> | |||||
<div id="b-format" class="btn-group" data-toggle="buttons"> | |||||
<label id="show-svg" class="btn btn-default active"> | |||||
<input type="radio" autocomplete="off" checked> SVG | |||||
</label> | |||||
<label id="show-json" class="btn btn-default"> | |||||
<input type="radio" autocomplete="off"> JSON | |||||
</label> | |||||
</div> | |||||
<table id="badges-svg" class="badges table"> | |||||
{% if have_tags %} | |||||
<tr> | |||||
<th colspan="2">Tags</th> | |||||
</tr> | |||||
{% endif %} | |||||
{% for urldict in badges %} | |||||
{% if urldict.tag == "*" %} | |||||
<tr> | |||||
<th colspan="2">Overall Status</th> | |||||
</tr> | |||||
{% endif %} | |||||
<tr> | |||||
<td> | |||||
<img src="{{ urldict.svg }}" alt="" /> | |||||
</td> | |||||
<td class="svg-url"> | |||||
<code>{{ urldict.svg }}</code> | |||||
</td> | |||||
</tr> | |||||
{% endfor %} | |||||
</table> | |||||
<table id="badges-json" class="badges table"> | |||||
{% if have_tags %} | |||||
<tr> | |||||
<th colspan="2">Tags</th> | |||||
</tr> | |||||
{% endif %} | |||||
{% for urldict in badges %} | |||||
{% if urldict.tag == "*" %} | |||||
<tr> | |||||
<th colspan="2">Overall Status</th> | |||||
</tr> | |||||
{% endif %} | |||||
<tr> | |||||
<td class="json-response" data-url="{{ urldict.json }}"> | |||||
</td> | |||||
<td class="json-url"> | |||||
<code>{{ urldict.json }}</code> | |||||
</td> | |||||
</tr> | |||||
{% endfor %} | |||||
</table> | |||||
</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> | |||||
<script src="{% static 'js/badges.js' %}"></script> | |||||
{% endcompress %} | |||||
{% endblock %} |