Browse Source

Show "Badges" and "Settings" in top navigation. Fixes #234

pull/241/head
Pēteris Caune 6 years ago
parent
commit
178b0ff95c
No known key found for this signature in database GPG Key ID: E28D7679E9A9EDE2
20 changed files with 382 additions and 186 deletions
  1. +1
    -0
      CHANGELOG.md
  2. +2
    -2
      hc/accounts/tests/test_badges.py
  3. +22
    -3
      hc/accounts/tests/test_project.py
  4. +0
    -1
      hc/accounts/urls.py
  5. +18
    -37
      hc/accounts/views.py
  6. +1
    -0
      hc/front/urls.py
  7. +37
    -0
      hc/front/views.py
  8. +22
    -0
      static/css/base.css
  9. +0
    -93
      templates/accounts/badges.html
  10. +0
    -1
      templates/accounts/billing.html
  11. +0
    -1
      templates/accounts/notifications.html
  12. +0
    -1
      templates/accounts/profile.html
  13. +8
    -3
      templates/accounts/project.html
  14. +8
    -37
      templates/base.html
  15. +169
    -0
      templates/base_project.html
  16. +86
    -0
      templates/front/badges.html
  17. +1
    -1
      templates/front/channels.html
  18. +1
    -1
      templates/front/details.html
  19. +1
    -1
      templates/front/log.html
  20. +5
    -4
      templates/front/my_checks.html

+ 1
- 0
CHANGELOG.md View File

@ -5,6 +5,7 @@ All notable changes to this project will be documented in this file.
### Improvements ### Improvements
- Add the EMAIL_USE_VERIFICATION configuration setting (#232) - Add the EMAIL_USE_VERIFICATION configuration setting (#232)
- Show "Badges" and "Settings" in top navigation (#234)
## 1.6.0 - 2019-04-01 ## 1.6.0 - 2019-04-01


+ 2
- 2
hc/accounts/tests/test_badges.py View File

@ -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/")

+ 22
- 3
hc/accounts/tests/test_project.py View File

@ -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")


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

@ -16,7 +16,6 @@ urlpatterns = [
path('profile/', views.profile, name="hc-profile"), path('profile/', views.profile, name="hc-profile"),
path('profile/notifications/', views.notifications, name="hc-notifications"), path('profile/notifications/', views.notifications, name="hc-notifications"),
path('profile/badges/', views.badges, name="hc-badges"),
path('close/', views.close, name="hc-close"), path('close/', views.close, name="hc-close"),
path('unsubscribe_reports/<str:username>/', path('unsubscribe_reports/<str:username>/',


+ 18
- 37
hc/accounts/views.py View File

@ -1,6 +1,5 @@
from datetime import timedelta as td from datetime import timedelta as td
import uuid import uuid
import re
from django.conf import settings from django.conf import settings
from django.contrib import messages from django.contrib import messages
@ -10,7 +9,8 @@ from django.contrib.auth import authenticate
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.core import signing from django.core import signing
from django.http import HttpResponseForbidden, HttpResponseBadRequest
from django.http import (HttpResponseForbidden, HttpResponseBadRequest,
HttpResponseNotFound)
from django.shortcuts import get_object_or_404, redirect, render from django.shortcuts import get_object_or_404, redirect, render
from django.utils.timezone import now from django.utils.timezone import now
from django.urls import resolve, Resolver404 from django.urls import resolve, Resolver404
@ -23,7 +23,6 @@ from hc.accounts.forms import (ChangeEmailForm, EmailPasswordForm,
ExistingEmailForm) ExistingEmailForm)
from hc.accounts.models import Profile, Project, Member from hc.accounts.models import Profile, Project, Member
from hc.api.models import Channel, Check from hc.api.models import Channel, Check
from hc.lib.badges import get_badge_url
from hc.payments.models import Subscription from hc.payments.models import Subscription
NEXT_WHITELIST = ("hc-checks", NEXT_WHITELIST = ("hc-checks",
@ -238,12 +237,22 @@ def add_project(request):
@login_required @login_required
def project(request, code): def project(request, code):
project = get_object_or_404(Project, code=code, owner=request.user)
if request.user.is_superuser:
q = Project.objects
else:
q = request.profile.projects()
try:
project = q.get(code=code)
except Project.DoesNotExist:
return HttpResponseNotFound()
is_owner = project.owner_id == request.user.id
ctx = { ctx = {
"page": "project", "page": "project",
"project": project, "project": project,
"show_api_keys": False,
"is_owner": is_owner,
"show_api_keys": "show_api_keys" in request.GET,
"project_name_status": "default", "project_name_status": "default",
"api_status": "default", "api_status": "default",
"team_status": "default" "team_status": "default"
@ -267,7 +276,7 @@ def project(request, code):
elif "show_api_keys" in request.POST: elif "show_api_keys" in request.POST:
ctx["show_api_keys"] = True ctx["show_api_keys"] = True
elif "invite_team_member" in request.POST: elif "invite_team_member" in request.POST:
if not project.can_invite():
if not is_owner or not project.can_invite():
return HttpResponseForbidden() return HttpResponseForbidden()
form = InviteTeamMemberForm(request.POST) form = InviteTeamMemberForm(request.POST)
@ -284,6 +293,9 @@ def project(request, code):
ctx["team_status"] = "success" ctx["team_status"] = "success"
elif "remove_team_member" in request.POST: elif "remove_team_member" in request.POST:
if not is_owner:
return HttpResponseForbidden()
form = RemoveTeamMemberForm(request.POST) form = RemoveTeamMemberForm(request.POST)
if form.is_valid(): if form.is_valid():
q = User.objects q = User.objects
@ -354,37 +366,6 @@ def notifications(request):
return render(request, "accounts/notifications.html", ctx) return render(request, "accounts/notifications.html", ctx)
@login_required
def badges(request):
badge_sets = []
for project in request.profile.projects():
tags = set()
for check in Check.objects.filter(project=project):
tags.update(check.tags_list())
sorted_tags = sorted(tags, key=lambda s: s.lower())
sorted_tags.append("*") # For the "overall status" badge
urls = []
for tag in sorted_tags:
if not re.match("^[\w-]+$", tag) and tag != "*":
continue
urls.append({
"svg": get_badge_url(project.badge_key, tag),
"json": get_badge_url(project.badge_key, tag, format="json"),
})
badge_sets.append({"project": project, "urls": urls})
ctx = {
"page": "profile",
"badges": badge_sets
}
return render(request, "accounts/badges.html", ctx)
@login_required @login_required
def set_password(request, token): def set_password(request, token):
if not request.profile.check_token(token, "set-password"): if not request.profile.check_token(token, "set-password"):


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

@ -50,6 +50,7 @@ channel_urls = [
urlpatterns = [ urlpatterns = [
path('', views.index, name="hc-index"), path('', views.index, name="hc-index"),
path('projects/<uuid:code>/checks/', views.my_checks, name="hc-checks"), path('projects/<uuid:code>/checks/', views.my_checks, name="hc-checks"),
path('projects/<uuid:code>/badges/', views.badges, name="hc-badges"),
path('projects/<uuid:code>/checks/add/', views.add_check, name="hc-add-check"), path('projects/<uuid:code>/checks/add/', views.add_check, name="hc-add-check"),
path('checks/cron_preview/', views.cron_preview), path('checks/cron_preview/', views.cron_preview),
path('projects/<uuid:code>/checks/status/', views.status, name="hc-status"), path('projects/<uuid:code>/checks/status/', views.status, name="hc-status"),


+ 37
- 0
hc/front/views.py View File

@ -1,5 +1,6 @@
from datetime import datetime, timedelta as td from datetime import datetime, timedelta as td
import json import json
import re
from urllib.parse import urlencode from urllib.parse import urlencode
from croniter import croniter from croniter import croniter
@ -29,6 +30,7 @@ from hc.front.schemas import telegram_callback
from hc.front.templatetags.hc_extras import (num_down_title, down_title, from hc.front.templatetags.hc_extras import (num_down_title, down_title,
sortchecks) sortchecks)
from hc.lib import jsonschema from hc.lib import jsonschema
from hc.lib.badges import get_badge_url
import pytz import pytz
from pytz.exceptions import UnknownTimeZoneError from pytz.exceptions import UnknownTimeZoneError
import requests import requests
@ -441,6 +443,7 @@ def log(request, code):
limit = check.project.owner_profile.ping_log_limit limit = check.project.owner_profile.ping_log_limit
ctx = { ctx = {
"project": check.project,
"check": check, "check": check,
"events": _get_events(check, limit), "events": _get_events(check, limit),
"limit": limit, "limit": limit,
@ -459,6 +462,7 @@ def details(request, code):
ctx = { ctx = {
"page": "details", "page": "details",
"project": check.project,
"check": check, "check": check,
"channels": channels, "channels": channels,
"timezones": pytz.all_timezones "timezones": pytz.all_timezones
@ -515,6 +519,38 @@ def status_single(request, code):
return JsonResponse(doc) return JsonResponse(doc)
@login_required
def badges(request, code):
project = _get_project_for_user(request, code)
tags = set()
for check in Check.objects.filter(project=project):
tags.update(check.tags_list())
sorted_tags = sorted(tags, key=lambda s: s.lower())
sorted_tags.append("*") # For the "overall status" badge
urls = []
for tag in sorted_tags:
if not re.match("^[\w-]+$", tag) and tag != "*":
continue
urls.append({
"tag": tag,
"svg": get_badge_url(project.badge_key, tag),
"json": get_badge_url(project.badge_key, tag, format="json"),
})
ctx = {
"have_tags": len(urls) > 1,
"page": "badges",
"project": project,
"badges": urls
}
return render(request, "front/badges.html", ctx)
@login_required @login_required
def channels(request): def channels(request):
if request.method == "POST": if request.method == "POST":
@ -547,6 +583,7 @@ def channels(request):
ctx = { ctx = {
"page": "channels", "page": "channels",
"project": request.project,
"profile": request.project.owner_profile, "profile": request.project.owner_profile,
"channels": channels, "channels": channels,
"enable_pushbullet": settings.PUSHBULLET_CLIENT_ID is not None, "enable_pushbullet": settings.PUSHBULLET_CLIENT_ID is not None,


+ 22
- 0
static/css/base.css View File

@ -41,6 +41,10 @@ body {
font-size: small; font-size: small;
} }
#global-links > li > a {
font-size: small;
}
@media (min-width: 768px) { @media (min-width: 768px) {
.navbar-default .navbar-nav > li.active > a, .navbar-default .navbar-nav > li > a:hover { .navbar-default .navbar-nav > li.active > a, .navbar-default .navbar-nav > li > a:hover {
border-bottom: 5px solid #eee; border-bottom: 5px solid #eee;
@ -48,6 +52,11 @@ body {
} }
} }
.navbar-default .navbar-brand, .navbar-default .navbar-brand:hover {
color: #333;
font-weight: bold;
}
.navbar-text { .navbar-text {
font-size: small; font-size: small;
} }
@ -115,3 +124,16 @@ pre {
.badge-down { .badge-down {
background-color: #d9534f; background-color: #d9534f;
} }
.dropdown-menu > li.project-item a {
min-width: 200px;
padding: 6px 20px 3px 20px;
}
.project-item .name {
display: inline-block;
max-width: 130px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}

+ 0
- 93
templates/accounts/badges.html View File

@ -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
- 1
templates/accounts/billing.html View File

@ -19,7 +19,6 @@
<li><a href="{% url 'hc-profile' %}">Account</a></li> <li><a href="{% url 'hc-profile' %}">Account</a></li>
<li class="active"><a href="{% url 'hc-billing' %}">Billing</a></li> <li class="active"><a href="{% url 'hc-billing' %}">Billing</a></li>
<li><a href="{% url 'hc-notifications' %}">Email Reports</a></li> <li><a href="{% url 'hc-notifications' %}">Email Reports</a></li>
<li><a href="{% url 'hc-badges' %}">Badges</a></li>
</ul> </ul>
</div> </div>


+ 0
- 1
templates/accounts/notifications.html View File

@ -21,7 +21,6 @@
<li><a href="{% url 'hc-billing' %}">Billing</a></li> <li><a href="{% url 'hc-billing' %}">Billing</a></li>
{% endif %} {% endif %}
<li class="active"><a href="{% url 'hc-notifications' %}">Email Reports</a></li> <li class="active"><a href="{% url 'hc-notifications' %}">Email Reports</a></li>
<li><a href="{% url 'hc-badges' %}">Badges</a></li>
</ul> </ul>
</div> </div>


+ 0
- 1
templates/accounts/profile.html View File

@ -29,7 +29,6 @@
<li><a href="{% url 'hc-billing' %}">Billing</a></li> <li><a href="{% url 'hc-billing' %}">Billing</a></li>
{% endif %} {% endif %}
<li><a href="{% url 'hc-notifications' %}">Email Reports</a></li> <li><a href="{% url 'hc-notifications' %}">Email Reports</a></li>
<li><a href="{% url 'hc-badges' %}">Badges</a></li>
</ul> </ul>
</div> </div>


+ 8
- 3
templates/accounts/project.html View File

@ -1,4 +1,4 @@
{% extends "base.html" %}
{% extends "base_project.html" %}
{% load compress static hc_extras %} {% load compress static hc_extras %}
{% block title %}Project Settings - {{ project }}{% endblock %} {% block title %}Project Settings - {{ project }}{% endblock %}
@ -7,7 +7,6 @@
{% block content %} {% block content %}
<div class="row"> <div class="row">
<div class="col-sm-9 col-md-6"> <div class="col-sm-9 col-md-6">
<h1 class="settings-title">Project Settings</h1>
{% for message in messages %} {% for message in messages %}
<p class="alert alert-{{ message.tags }}">{{ message }}</p> <p class="alert alert-{{ message.tags }}">{{ message }}</p>
{% endfor %} {% endfor %}
@ -103,10 +102,12 @@
<td>{{ member.user.email }} </td> <td>{{ member.user.email }} </td>
<td>Member</td> <td>Member</td>
<td> <td>
{% if is_owner %}
<a <a
href="#" href="#"
data-email="{{ member.user.email }}" data-email="{{ member.user.email }}"
class="pull-right member-remove">Remove</a> class="pull-right member-remove">Remove</a>
{% endif %}
</td> </td>
</tr> </tr>
{% endfor %} {% endfor %}
@ -121,7 +122,8 @@
<br /> <br />
{% if project.can_invite %}
{% if is_owner %}
{% if project.can_invite%}
<a <a
href="#" href="#"
class="btn btn-primary pull-right" class="btn btn-primary pull-right"
@ -134,6 +136,7 @@
<a href="{% url 'hc-pricing' %}">upgrade your account!</a> <a href="{% url 'hc-pricing' %}">upgrade your account!</a>
</div> </div>
{% endif %} {% endif %}
{% endif %}
</div> </div>
{% if team_member_invited %} {% if team_member_invited %}
@ -149,6 +152,7 @@
{% endif %} {% endif %}
</div> </div>
{% if is_owner %}
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-body settings-block"> <div class="panel-body settings-block">
{% csrf_token %} {% csrf_token %}
@ -163,6 +167,7 @@
</form> </form>
</div> </div>
</div> </div>
{% endif %}
</div> </div>
</div> </div>


+ 8
- 37
templates/base.html View File

@ -88,18 +88,7 @@
</div> </div>
<div id="navbar" class="navbar-collapse collapse"> <div id="navbar" class="navbar-collapse collapse">
<ul id="nav-main-sections" class="nav navbar-nav">
{% if request.user.is_authenticated and request.profile.current_project %}
<li {% if page == 'checks' %} class="active" {% endif %}>
<a href="{% url 'hc-checks' request.profile.current_project.code %}">Checks</a>
</li>
<li {% if page == 'channels' %} class="active" {% endif %}>
<a href="{% url 'hc-channels' %}">Integrations</a>
</li>
{% endif %}
<ul id="global-links" class="nav navbar-nav navbar-right">
{% if show_pricing %} {% if show_pricing %}
<li {% if page == 'pricing' %} class="active" {% endif %}> <li {% if page == 'pricing' %} class="active" {% endif %}>
<a href="{% url 'hc-pricing' %}">Pricing</a> <a href="{% url 'hc-pricing' %}">Pricing</a>
@ -110,29 +99,18 @@
<a href="{% url 'hc-docs' %}">Docs</a> <a href="{% url 'hc-docs' %}">Docs</a>
</li> </li>
</ul>
{% if request.user.is_authenticated %}
<ul class="nav navbar-nav navbar-right">
{% if request.user.is_authenticated %}
<li class="dropdown"> <li class="dropdown">
<a id="nav-email" href="#" class="dropdown-toggle" data-toggle="dropdown" role="button"> <a id="nav-email" href="#" class="dropdown-toggle" data-toggle="dropdown" role="button">
{% if check %}
{{ check.project }}
{% elif request.profile.current_project %}
{{ request.profile.current_project }}
{% else %}
{{ request.user.email }}
{% endif %}
Account
<span class="caret"></span> <span class="caret"></span>
</a> </a>
<ul class="dropdown-menu"> <ul class="dropdown-menu">
<li class="dropdown-header">Projects</li>
{% for project in request.profile.annotated_projects %} {% for project in request.profile.annotated_projects %}
<li class="dropdown-header">{{ project }}</li>
<li> <li>
<a href="{% url 'hc-checks' project.code %}"> <a href="{% url 'hc-checks' project.code %}">
Checks
{{ project }}
{% if project.n_down %} {% if project.n_down %}
<span class="badge badge-down pull-right"> <span class="badge badge-down pull-right">
{{ project.n_down }} {{ project.n_down }}
@ -140,24 +118,17 @@
{% endif %} {% endif %}
</a> </a>
</li> </li>
{% if project.owner_id == request.user.id %}
<li>
<a href="{% url 'hc-project-settings' project.code %}">Project Settings</a>
</li>
{% endif %}
<li role="separator" class="divider"></li>
{% endfor %} {% endfor %}
<li role="separator" class="divider"></li>
<li><a href="{% url 'hc-profile' %}">Account Settings</a></li> <li><a href="{% url 'hc-profile' %}">Account Settings</a></li>
<li><a href="{% url 'hc-logout' %}">Log Out</a></li> <li><a href="{% url 'hc-logout' %}">Log Out</a></li>
</ul> </ul>
</li> </li>
</ul>
{% elif page != "login" %}
<ul class="nav navbar-nav navbar-right">
{% elif page != "login" %}
<li><a href="{% url 'hc-login' %}">Sign In</a></li> <li><a href="{% url 'hc-login' %}">Sign In</a></li>
{% endif %}
</ul> </ul>
{% endif %}
{% if show_search %} {% if show_search %}
<form class="navbar-form navbar-right hidden-xs hidden-sm"> <form class="navbar-form navbar-right hidden-xs hidden-sm">


+ 169
- 0
templates/base_project.html View File

@ -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>

+ 86
- 0
templates/front/badges.html View File

@ -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 %}

+ 1
- 1
templates/front/channels.html View File

@ -1,4 +1,4 @@
{% extends "base.html" %}
{% extends "base_project.html" %}
{% load compress humanize static hc_extras %} {% load compress humanize static hc_extras %}
{% block title %}Integrations - {% site_name %}{% endblock %} {% block title %}Integrations - {% site_name %}{% endblock %}


+ 1
- 1
templates/front/details.html View File

@ -1,4 +1,4 @@
{% extends "base.html" %}
{% extends "base_project.html" %}
{% load compress humanize static hc_extras %} {% load compress humanize static hc_extras %}
{% block title %}{{ check|down_title }}{% endblock %} {% block title %}{{ check|down_title }}{% endblock %}


+ 1
- 1
templates/front/log.html View File

@ -1,4 +1,4 @@
{% extends "base.html" %}
{% extends "base_project.html" %}
{% load compress humanize staticfiles hc_extras %} {% load compress humanize staticfiles hc_extras %}
{% block title %}My Checks - {% site_name %}{% endblock %} {% block title %}My Checks - {% site_name %}{% endblock %}


+ 5
- 4
templates/front/my_checks.html View File

@ -1,17 +1,18 @@
{% extends "base.html" %}
{% extends "base_project.html" %}
{% load compress static hc_extras %} {% load compress static hc_extras %}
{% block title %}{{ num_down|num_down_title }}{% endblock %} {% block title %}{{ num_down|num_down_title }}{% endblock %}
{% block content %} {% block content %}
<div class="row"> <div class="row">
{% if tags %}
<div id="my-checks-tags" class="col-sm-12">
<div id="my-checks-tags" class="col-sm-9">
{% for tag, status in tags %} {% for tag, status in tags %}
<div class="btn btn-xs {{ status }} {% if tag in selected_tags %}checked{% endif%}">{{ tag }}</div> <div class="btn btn-xs {{ status }} {% if tag in selected_tags %}checked{% endif%}">{{ tag }}</div>
{% endfor %} {% endfor %}
</div> </div>
{% endif %}
<div class="col-sm-3">
<input id="search" type="text" placeholder="Filter by check name&hellip;" class="form-control" value="{{ search }}">
</div>
</div> </div>
<div class="row"> <div class="row">


Loading…
Cancel
Save