From 6b0d566922f590cab3b6031e5468bc675d39a7db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C4=93teris=20Caune?= Date: Tue, 29 Jan 2019 10:59:10 +0200 Subject: [PATCH] "My Projects" page. --- hc/accounts/admin.py | 2 +- hc/accounts/tests/test_check_token.py | 22 +++++--- hc/accounts/tests/test_login.py | 12 +++-- hc/accounts/tests/test_switch_project.py | 50 ----------------- hc/accounts/urls.py | 3 -- hc/accounts/views.py | 31 +---------- hc/front/tests/test_add_check.py | 24 ++++----- hc/front/tests/test_my_checks.py | 38 +++++++++---- hc/front/tests/test_pause.py | 5 +- hc/front/tests/test_remove_check.py | 5 +- hc/front/tests/test_update_name.py | 5 +- hc/front/tests/test_update_timeout.py | 7 +-- hc/front/urls.py | 4 +- hc/front/views.py | 62 +++++++++++++++------- static/css/projects.css | 15 ++++++ templates/accounts/profile.html | 2 +- templates/admin/profile_list_projects.html | 2 +- templates/base.html | 7 +-- templates/front/log.html | 2 +- templates/front/my_checks.html | 2 +- templates/front/projects.html | 58 ++++++++++++++++++++ 21 files changed, 205 insertions(+), 153 deletions(-) delete mode 100644 hc/accounts/tests/test_switch_project.py create mode 100644 static/css/projects.css create mode 100644 templates/front/projects.html diff --git a/hc/accounts/admin.py b/hc/accounts/admin.py index 43f384c6..e1b0d51e 100644 --- a/hc/accounts/admin.py +++ b/hc/accounts/admin.py @@ -163,7 +163,7 @@ class ProjectAdmin(admin.ModelAdmin): @mark_safe def switch(self, obj): - url = reverse("hc-switch-project", args=[obj.code]) + url = reverse("hc-checks", args=[obj.code]) return "Show Checks" % url diff --git a/hc/accounts/tests/test_check_token.py b/hc/accounts/tests/test_check_token.py index 36f9ab80..abc837fe 100644 --- a/hc/accounts/tests/test_check_token.py +++ b/hc/accounts/tests/test_check_token.py @@ -9,13 +9,17 @@ class CheckTokenTestCase(BaseTestCase): self.profile.token = make_password("secret-token", "login") self.profile.save() + self.checks_url = "/projects/%s/checks/" % self.project.code + def test_it_shows_form(self): r = self.client.get("/accounts/check_token/alice/secret-token/") self.assertContains(r, "You are about to log in") def test_it_redirects(self): - r = self.client.post("/accounts/check_token/alice/secret-token/") - self.assertRedirects(r, "/checks/") + r = self.client.post("/accounts/check_token/alice/secret-token/", + follow=True) + + self.assertRedirects(r, self.checks_url) # After login, token should be blank self.profile.refresh_from_db() @@ -26,8 +30,10 @@ class CheckTokenTestCase(BaseTestCase): self.client.login(username="alice@example.org", password="password") # Login again, when already authenticated - r = self.client.post("/accounts/check_token/alice/secret-token/") - self.assertRedirects(r, "/checks/") + r = self.client.post("/accounts/check_token/alice/secret-token/", + follow=True) + + self.assertRedirects(r, self.checks_url) def test_it_redirects_bad_login(self): # Login with a bad token @@ -37,9 +43,11 @@ class CheckTokenTestCase(BaseTestCase): self.assertContains(r, "incorrect or expired") def test_it_handles_next_parameter(self): - r = self.client.post("/accounts/check_token/alice/secret-token/?next=/integrations/add_slack/") + url = "/accounts/check_token/alice/secret-token/?next=/integrations/add_slack/" + r = self.client.post(url) self.assertRedirects(r, "/integrations/add_slack/") def test_it_ignores_bad_next_parameter(self): - r = self.client.post("/accounts/check_token/alice/secret-token/?next=/evil/") - self.assertRedirects(r, "/checks/") + url = "/accounts/check_token/alice/secret-token/?next=/evil/" + r = self.client.post(url, follow=True) + self.assertRedirects(r, self.checks_url) diff --git a/hc/accounts/tests/test_login.py b/hc/accounts/tests/test_login.py index 68e94469..37328557 100644 --- a/hc/accounts/tests/test_login.py +++ b/hc/accounts/tests/test_login.py @@ -6,6 +6,10 @@ from hc.test import BaseTestCase class LoginTestCase(BaseTestCase): + def setUp(self): + super(LoginTestCase, self).setUp() + self.checks_url = "/projects/%s/checks/" % self.project.code + def test_it_sends_link(self): form = {"identity": "alice@example.org"} @@ -49,8 +53,8 @@ class LoginTestCase(BaseTestCase): "password": "password" } - r = self.client.post("/accounts/login/", form) - self.assertRedirects(r, "/checks/") + r = self.client.post("/accounts/login/", form, follow=True) + self.assertRedirects(r, self.checks_url) def test_it_handles_password_login_with_redirect(self): check = Check.objects.create(project=self.project) @@ -77,8 +81,8 @@ class LoginTestCase(BaseTestCase): "password": "password" } - r = self.client.post("/accounts/login/?next=/evil/", form) - self.assertRedirects(r, "/checks/") + r = self.client.post("/accounts/login/?next=/evil/", form, follow=True) + self.assertRedirects(r, self.checks_url) def test_it_handles_wrong_password(self): form = { diff --git a/hc/accounts/tests/test_switch_project.py b/hc/accounts/tests/test_switch_project.py deleted file mode 100644 index 931ffa99..00000000 --- a/hc/accounts/tests/test_switch_project.py +++ /dev/null @@ -1,50 +0,0 @@ -from hc.test import BaseTestCase -from hc.api.models import Check - - -class SwitchTeamTestCase(BaseTestCase): - def setUp(self): - super(SwitchTeamTestCase, self).setUp() - - self.url = "/accounts/switch_project/%s/" % self.project.code - - def test_it_switches(self): - self.bobs_profile.current_project = None - self.bobs_profile.save() - - c = Check(project=self.project, name="This belongs to Alice") - c.save() - - self.client.login(username="bob@example.org", password="password") - - r = self.client.get(self.url, follow=True) - - self.assertContains(r, "This belongs to Alice") - - self.bobs_profile.refresh_from_db() - self.assertEqual(self.bobs_profile.current_project, self.project) - - def test_it_checks_team_membership(self): - self.client.login(username="charlie@example.org", password="password") - - r = self.client.get(self.url) - self.assertEqual(r.status_code, 403) - - def test_it_switches_to_own_team(self): - self.client.login(username="alice@example.org", password="password") - - r = self.client.get(self.url, follow=True) - self.assertEqual(r.status_code, 200) - - def test_it_handles_invalid_project_code(self): - self.client.login(username="bob@example.org", password="password") - - url = "/accounts/switch_project/6837d6ec-fc08-4da5-a67f-08a9ed1ccf62/" - r = self.client.get(url) - self.assertEqual(r.status_code, 404) - - def test_it_requires_login(self): - r = self.client.get(self.url) - - expected_url = "/accounts/login/?next=" + self.url - self.assertRedirects(r, expected_url) diff --git a/hc/accounts/urls.py b/hc/accounts/urls.py index 2591ba74..a85de966 100644 --- a/hc/accounts/urls.py +++ b/hc/accounts/urls.py @@ -31,7 +31,4 @@ urlpatterns = [ path('change_email//', views.change_email, name="hc-change-email"), - path('switch_project//', - views.switch_project, name="hc-switch-project"), - ] diff --git a/hc/accounts/views.py b/hc/accounts/views.py index ea4c8eb7..929fdcb1 100644 --- a/hc/accounts/views.py +++ b/hc/accounts/views.py @@ -16,7 +16,6 @@ from django.utils.timezone import now from django.urls import resolve, Resolver404 from django.views.decorators.csrf import csrf_exempt from django.views.decorators.http import require_POST -from django.shortcuts import get_object_or_404 from hc.accounts.forms import (ChangeEmailForm, EmailPasswordForm, InviteTeamMemberForm, RemoveTeamMemberForm, ReportSettingsForm, SetPasswordForm, @@ -81,7 +80,7 @@ def _redirect_after_login(request): if _is_whitelisted(redirect_url): return redirect(redirect_url) - return redirect("hc-checks") + return redirect("hc-index") def login(request): @@ -228,7 +227,7 @@ def add_project(request): project.name = form.cleaned_data["name"] project.save() - return redirect("hc-switch-project", project.code) + return redirect("hc-checks", project.code) @login_required @@ -453,32 +452,6 @@ def unsubscribe_reports(request, username): return render(request, "accounts/unsubscribed.html") -@login_required -def switch_project(request, code): - project = get_object_or_404(Project, code=code) - - # The rules: - # Superuser can switch to any team. - access_ok = request.user.is_superuser - - # Users can switch to their own projects. - if not access_ok and project.owner_id == request.user.id: - access_ok = True - - # Users can switch to projects they are members of. - if not access_ok: - q = project.member_set.filter(user=request.user) - access_ok = q.exists() - - if not access_ok: - return HttpResponseForbidden() - - request.profile.current_project = project - request.profile.save() - - return redirect("hc-checks") - - @require_POST @login_required def close(request): diff --git a/hc/front/tests/test_add_check.py b/hc/front/tests/test_add_check.py index 8ac1f136..cf9d8443 100644 --- a/hc/front/tests/test_add_check.py +++ b/hc/front/tests/test_add_check.py @@ -3,12 +3,16 @@ from hc.test import BaseTestCase class AddCheckTestCase(BaseTestCase): + def setUp(self): + super(AddCheckTestCase, self).setUp() + + self.url = "/projects/%s/checks/add/" % self.project.code + self.redirect_url = "/projects/%s/checks/" % self.project.code def test_it_works(self): - url = "/checks/add/" self.client.login(username="alice@example.org", password="password") - r = self.client.post(url) - self.assertRedirects(r, "/checks/") + r = self.client.post(self.url) + self.assertRedirects(r, self.redirect_url) check = Check.objects.get() self.assertEqual(check.project, self.project) @@ -16,33 +20,29 @@ class AddCheckTestCase(BaseTestCase): self.profile.current_project = None self.profile.save() - url = "/checks/add/" self.client.login(username="alice@example.org", password="password") - r = self.client.post(url) - self.assertRedirects(r, "/checks/") + r = self.client.post(self.url) + self.assertRedirects(r, self.redirect_url) check = Check.objects.get() self.assertEqual(check.project, self.project) def test_team_access_works(self): - url = "/checks/add/" self.client.login(username="bob@example.org", password="password") - self.client.post(url) + self.client.post(self.url) check = Check.objects.get() # Added by bob, but should belong to alice (bob has team access) self.assertEqual(check.project, self.project) def test_it_rejects_get(self): - url = "/checks/add/" self.client.login(username="alice@example.org", password="password") - r = self.client.get(url) + r = self.client.get(self.url) self.assertEqual(r.status_code, 405) def test_it_obeys_check_limit(self): self.profile.check_limit = 0 self.profile.save() - url = "/checks/add/" self.client.login(username="alice@example.org", password="password") - r = self.client.post(url) + r = self.client.post(self.url) self.assertEqual(r.status_code, 400) diff --git a/hc/front/tests/test_my_checks.py b/hc/front/tests/test_my_checks.py index 45daa5d5..1c5707ce 100644 --- a/hc/front/tests/test_my_checks.py +++ b/hc/front/tests/test_my_checks.py @@ -11,19 +11,37 @@ class MyChecksTestCase(BaseTestCase): self.check = Check(project=self.project, name="Alice Was Here") self.check.save() + self.url = "/projects/%s/checks/" % self.project.code + def test_it_works(self): for email in ("alice@example.org", "bob@example.org"): self.client.login(username=email, password="password") - r = self.client.get("/checks/") + r = self.client.get(self.url) self.assertContains(r, "Alice Was Here", status_code=200) + def test_it_updates_current_project(self): + self.profile.current_project = None + self.profile.save() + + self.client.login(username="alice@example.org", password="password") + r = self.client.get(self.url) + self.assertEqual(r.status_code, 200) + + self.profile.refresh_from_db() + self.assertEqual(self.profile.current_project, self.project) + + def test_it_checks_access(self): + self.client.login(username="charlie@example.org", password="password") + r = self.client.get(self.url) + self.assertEqual(r.status_code, 404) + def test_it_shows_green_check(self): self.check.last_ping = timezone.now() self.check.status = "up" self.check.save() self.client.login(username="alice@example.org", password="password") - r = self.client.get("/checks/") + r = self.client.get(self.url) self.assertContains(r, "icon-up") def test_it_shows_red_check(self): @@ -32,7 +50,7 @@ class MyChecksTestCase(BaseTestCase): self.check.save() self.client.login(username="alice@example.org", password="password") - r = self.client.get("/checks/") + r = self.client.get(self.url) self.assertContains(r, "icon-down") def test_it_shows_amber_check(self): @@ -41,7 +59,7 @@ class MyChecksTestCase(BaseTestCase): self.check.save() self.client.login(username="alice@example.org", password="password") - r = self.client.get("/checks/") + r = self.client.get(self.url) self.assertContains(r, "icon-grace") def test_it_hides_add_check_button(self): @@ -49,19 +67,19 @@ class MyChecksTestCase(BaseTestCase): self.profile.save() self.client.login(username="alice@example.org", password="password") - r = self.client.get("/checks/") + r = self.client.get(self.url) self.assertContains(r, "Check limit reached", status_code=200) def test_it_saves_sort_field(self): self.client.login(username="alice@example.org", password="password") - self.client.get("/checks/?sort=name") + self.client.get(self.url + "?sort=name") self.profile.refresh_from_db() self.assertEqual(self.profile.sort, "name") def test_it_ignores_bad_sort_value(self): self.client.login(username="alice@example.org", password="password") - self.client.get("/checks/?sort=invalid") + self.client.get(self.url + "?sort=invalid") self.profile.refresh_from_db() self.assertEqual(self.profile.sort, "created") @@ -73,7 +91,7 @@ class MyChecksTestCase(BaseTestCase): self.check.save() self.client.login(username="alice@example.org", password="password") - r = self.client.get("/checks/") + r = self.client.get(self.url) self.assertContains(r, """
foo
""") def test_it_shows_grace_badge(self): @@ -83,7 +101,7 @@ class MyChecksTestCase(BaseTestCase): self.check.save() self.client.login(username="alice@example.org", password="password") - r = self.client.get("/checks/") + r = self.client.get(self.url) self.assertContains(r, """
foo
""") def test_it_shows_grace_started_badge(self): @@ -94,5 +112,5 @@ class MyChecksTestCase(BaseTestCase): self.check.save() self.client.login(username="alice@example.org", password="password") - r = self.client.get("/checks/") + r = self.client.get(self.url) self.assertContains(r, """
foo
""") diff --git a/hc/front/tests/test_pause.py b/hc/front/tests/test_pause.py index 10413191..5d0fed24 100644 --- a/hc/front/tests/test_pause.py +++ b/hc/front/tests/test_pause.py @@ -11,11 +11,12 @@ class PauseTestCase(BaseTestCase): super(PauseTestCase, self).setUp() self.check = Check.objects.create(project=self.project, status="up") self.url = "/checks/%s/pause/" % self.check.code + self.redirect_url = "/projects/%s/checks/" % self.project.code def test_it_pauses(self): self.client.login(username="alice@example.org", password="password") r = self.client.post(self.url) - self.assertRedirects(r, "/checks/") + self.assertRedirects(r, self.redirect_url) self.check.refresh_from_db() self.assertEqual(self.check.status, "paused") @@ -31,7 +32,7 @@ class PauseTestCase(BaseTestCase): self.client.login(username="bob@example.org", password="password") r = self.client.post(self.url) - self.assertRedirects(r, "/checks/") + self.assertRedirects(r, self.redirect_url) def test_it_clears_last_start_alert_after(self): self.check.last_start = now() diff --git a/hc/front/tests/test_remove_check.py b/hc/front/tests/test_remove_check.py index 185fc764..67a65f98 100644 --- a/hc/front/tests/test_remove_check.py +++ b/hc/front/tests/test_remove_check.py @@ -8,11 +8,12 @@ class RemoveCheckTestCase(BaseTestCase): super(RemoveCheckTestCase, self).setUp() self.check = Check.objects.create(project=self.project) self.remove_url = "/checks/%s/remove/" % self.check.code + self.redirect_url = "/projects/%s/checks/" % self.project.code def test_it_works(self): self.client.login(username="alice@example.org", password="password") r = self.client.post(self.remove_url) - self.assertRedirects(r, "/checks/") + self.assertRedirects(r, self.redirect_url) self.assertEqual(Check.objects.count(), 0) @@ -53,4 +54,4 @@ class RemoveCheckTestCase(BaseTestCase): self.client.login(username="bob@example.org", password="password") r = self.client.post(self.remove_url) - self.assertRedirects(r, "/checks/") + self.assertRedirects(r, self.redirect_url) diff --git a/hc/front/tests/test_update_name.py b/hc/front/tests/test_update_name.py index 33fb7f8c..ee8f086a 100644 --- a/hc/front/tests/test_update_name.py +++ b/hc/front/tests/test_update_name.py @@ -9,11 +9,12 @@ class UpdateNameTestCase(BaseTestCase): self.check = Check.objects.create(project=self.project) self.url = "/checks/%s/name/" % self.check.code + self.redirect_url = "/projects/%s/checks/" % self.project.code def test_it_works(self): self.client.login(username="alice@example.org", password="password") r = self.client.post(self.url, data={"name": "Alice Was Here"}) - self.assertRedirects(r, "/checks/") + self.assertRedirects(r, self.redirect_url) self.check.refresh_from_db() self.assertEqual(self.check.name, "Alice Was Here") @@ -37,7 +38,7 @@ class UpdateNameTestCase(BaseTestCase): # But this should still work: self.client.login(username="bob@example.org", password="password") r = self.client.post(self.url, data={"name": "Bob Was Here"}) - self.assertRedirects(r, "/checks/") + self.assertRedirects(r, self.redirect_url) def test_it_checks_ownership(self): payload = {"name": "Charlie Sent This"} diff --git a/hc/front/tests/test_update_timeout.py b/hc/front/tests/test_update_timeout.py index a1ebbca1..861b0394 100644 --- a/hc/front/tests/test_update_timeout.py +++ b/hc/front/tests/test_update_timeout.py @@ -14,13 +14,14 @@ class UpdateTimeoutTestCase(BaseTestCase): self.check.save() self.url = "/checks/%s/timeout/" % self.check.code + self.redirect_url = "/projects/%s/checks/" % self.project.code def test_it_works(self): payload = {"kind": "simple", "timeout": 3600, "grace": 60} self.client.login(username="alice@example.org", password="password") r = self.client.post(self.url, data=payload) - self.assertRedirects(r, "/checks/") + self.assertRedirects(r, self.redirect_url) self.check.refresh_from_db() self.assertEqual(self.check.kind, "simple") @@ -55,7 +56,7 @@ class UpdateTimeoutTestCase(BaseTestCase): self.client.login(username="alice@example.org", password="password") r = self.client.post(self.url, data=payload) - self.assertRedirects(r, "/checks/") + self.assertRedirects(r, self.redirect_url) self.check.refresh_from_db() self.assertEqual(self.check.kind, "cron") @@ -184,4 +185,4 @@ class UpdateTimeoutTestCase(BaseTestCase): self.client.login(username="bob@example.org", password="password") r = self.client.post(self.url, data=payload) - self.assertRedirects(r, "/checks/") + self.assertRedirects(r, self.redirect_url) diff --git a/hc/front/urls.py b/hc/front/urls.py index 873fd084..b82f22af 100644 --- a/hc/front/urls.py +++ b/hc/front/urls.py @@ -48,8 +48,8 @@ channel_urls = [ urlpatterns = [ path('', views.index, name="hc-index"), - path('checks/', views.my_checks, name="hc-checks"), - path('checks/add/', views.add_check, name="hc-add-check"), + path('projects//checks/', views.my_checks, name="hc-checks"), + path('projects//checks/add/', views.add_check, name="hc-add-check"), path('checks/cron_preview/', views.cron_preview), path('projects//checks/status/', views.status, name="hc-status"), path('checks//', include(check_urls)), diff --git a/hc/front/views.py b/hc/front/views.py index 5394f0c1..f6774d61 100644 --- a/hc/front/views.py +++ b/hc/front/views.py @@ -17,6 +17,7 @@ from django.utils import timezone from django.utils.crypto import get_random_string from django.views.decorators.csrf import csrf_exempt from django.views.decorators.http import require_POST +from hc.accounts.models import Project from hc.api.models import (DEFAULT_GRACE, DEFAULT_TIMEOUT, Channel, Check, Ping, Notification) from hc.api.transports import Telegram @@ -78,30 +79,41 @@ def _get_check_for_user(request, code): raise Http404("not found") -def _has_access(request, project_code): +def _get_project_for_user(request, project_code): """ Return true if current user has access to the specified account. """ if request.user.is_superuser: - return True + q = Project.objects.all + else: + q = request.profile.projects() - projects = request.profile.projects() - return projects.filter(code=project_code).exists() + try: + return q.get(code=project_code) + except Project.DoesNotExist: + raise Http404("not found") @login_required -def my_checks(request): +def my_checks(request, code): + project = _get_project_for_user(request, code) + if request.GET.get("sort") in VALID_SORT_VALUES: request.profile.sort = request.GET["sort"] request.profile.save() - checks = list(Check.objects.filter(project=request.project).prefetch_related("channel_set")) + if request.profile.current_project_id != project.id: + request.profile.current_project = project + request.profile.save() + + q = Check.objects.filter(project=project) + checks = list(q.prefetch_related("channel_set")) sortchecks(checks, request.profile.sort) tags_statuses, num_down = _tags_statuses(checks) pairs = list(tags_statuses.items()) pairs.sort(key=lambda pair: pair[0].lower()) - channels = Channel.objects.filter(project=request.project) + channels = Channel.objects.filter(project=project) channels = list(channels.order_by("created")) hidden_checks = set() @@ -129,7 +141,8 @@ def my_checks(request): "tags": pairs, "ping_endpoint": settings.PING_ENDPOINT, "timezones": pytz.all_timezones, - "num_available": request.project.num_checks_available(), + "project": project, + "num_available": project.num_checks_available(), "sort": request.profile.sort, "selected_tags": selected_tags, "show_search": True, @@ -142,8 +155,7 @@ def my_checks(request): @login_required def status(request, code): - if not _has_access(request, code): - raise Http404("not found") + _get_project_for_user(request, code) checks = list(Check.objects.filter(project__code=code)) @@ -183,7 +195,17 @@ def switch_channel(request, code, channel_code): def index(request): if request.user.is_authenticated: - return redirect("hc-checks") + projects = list(request.profile.projects()) + + if len(projects) == 1: + return redirect("hc-checks", projects[0].code) + + ctx = { + "page": "projects", + "show_plans": settings.USE_PAYMENTS, + "projects": projects + } + return render(request, "front/projects.html", ctx) check = Check() @@ -242,16 +264,17 @@ def docs_resources(request): @require_POST @login_required -def add_check(request): - if request.project.num_checks_available() <= 0: +def add_check(request, code): + project = _get_project_for_user(request, code) + if project.num_checks_available() <= 0: return HttpResponseBadRequest() - check = Check(project=request.project) + check = Check(project=project) check.save() check.assign_all_channels() - return redirect("hc-checks") + return redirect("hc-checks", code) @require_POST @@ -268,7 +291,7 @@ def update_name(request, code): if "/details/" in request.META.get("HTTP_REFERER", ""): return redirect("hc-details", code) - return redirect("hc-checks") + return redirect("hc-checks", check.project.code) @require_POST @@ -313,7 +336,7 @@ def update_timeout(request, code): if "/details/" in request.META.get("HTTP_REFERER", ""): return redirect("hc-details", code) - return redirect("hc-checks") + return redirect("hc-checks", check.project.code) @require_POST @@ -369,15 +392,16 @@ def pause(request, code): if "/details/" in request.META.get("HTTP_REFERER", ""): return redirect("hc-details", code) - return redirect("hc-checks") + return redirect("hc-checks", check.project.code) @require_POST @login_required def remove_check(request, code): check = _get_check_for_user(request, code) + project = check.project check.delete() - return redirect("hc-checks") + return redirect("hc-checks", project.code) def _get_events(check, limit): diff --git a/static/css/projects.css b/static/css/projects.css new file mode 100644 index 00000000..bcfafc98 --- /dev/null +++ b/static/css/projects.css @@ -0,0 +1,15 @@ +#my-projects a { + display: block; + color: #333; +} + +#my-projects a:hover { + text-decoration: none; +} + + +#my-projects a:hover .panel { + border-color: #0091EA; +} + + diff --git a/templates/accounts/profile.html b/templates/accounts/profile.html index 1aa62c3a..d6059708 100644 --- a/templates/accounts/profile.html +++ b/templates/accounts/profile.html @@ -79,7 +79,7 @@
{{ project }}
- + {% with project.check_set.count as n %} {{ n }} check{{ n|pluralize }} {% endwith %} diff --git a/templates/admin/profile_list_projects.html b/templates/admin/profile_list_projects.html index 638d53de..fc63e6dc 100644 --- a/templates/admin/profile_list_projects.html +++ b/templates/admin/profile_list_projects.html @@ -1,5 +1,5 @@ \ No newline at end of file diff --git a/templates/base.html b/templates/base.html index ef30e5e8..d269ec28 100644 --- a/templates/base.html +++ b/templates/base.html @@ -43,6 +43,7 @@ + {% endcompress %} @@ -87,9 +88,9 @@