diff --git a/hc/accounts/admin.py b/hc/accounts/admin.py index 3f1fc81a..81491d59 100644 --- a/hc/accounts/admin.py +++ b/hc/accounts/admin.py @@ -3,6 +3,7 @@ from django.contrib.auth.admin import UserAdmin from django.contrib.auth.models import User from django.template.loader import render_to_string from django.urls import reverse +from django.utils.safestring import mark_safe from hc.accounts.models import Profile from hc.api.models import Channel, Check @@ -47,6 +48,7 @@ class ProfileAdmin(admin.ModelAdmin): fieldsets = (ProfileFieldset.tuple(), TeamFieldset.tuple()) + @mark_safe def users(self, obj): if obj.member_set.count() == 0: return obj.user.email @@ -55,6 +57,7 @@ class ProfileAdmin(admin.ModelAdmin): "profile": obj }) + @mark_safe def checks(self, obj): num_checks = Check.objects.filter(user=obj.user).count() pct = 100 * num_checks / max(obj.check_limit, 1) @@ -68,9 +71,6 @@ class ProfileAdmin(admin.ModelAdmin): def email(self, obj): return obj.user.email - users.allow_tags = True - checks.allow_tags = True - class HcUserAdmin(UserAdmin): actions = ["send_report"] diff --git a/hc/accounts/backends.py b/hc/accounts/backends.py index b0ba119d..b8c08717 100644 --- a/hc/accounts/backends.py +++ b/hc/accounts/backends.py @@ -15,7 +15,7 @@ class BasicBackend(object): # Authenticate against the token in user's profile. class ProfileBackend(BasicBackend): - def authenticate(self, username=None, token=None): + def authenticate(self, request=None, username=None, token=None): try: profiles = Profile.objects.select_related("user") profile = profiles.get(user__username=username) @@ -30,7 +30,7 @@ class ProfileBackend(BasicBackend): class EmailBackend(BasicBackend): - def authenticate(self, username=None, password=None): + def authenticate(self, request=None, username=None, password=None): try: user = User.objects.get(email=username) except User.DoesNotExist: diff --git a/hc/accounts/migrations/0001_initial.py b/hc/accounts/migrations/0001_initial.py index a71ce40b..1a85801e 100644 --- a/hc/accounts/migrations/0001_initial.py +++ b/hc/accounts/migrations/0001_initial.py @@ -18,7 +18,7 @@ class Migration(migrations.Migration): ('id', models.AutoField(auto_created=True, serialize=False, verbose_name='ID', primary_key=True)), ('next_report_date', models.DateTimeField(null=True, blank=True)), ('reports_allowed', models.BooleanField(default=True)), - ('user', models.OneToOneField(blank=True, to=settings.AUTH_USER_MODEL, null=True)), + ('user', models.OneToOneField(blank=True, to=settings.AUTH_USER_MODEL, null=True, on_delete=models.CASCADE)), ], ), ] diff --git a/hc/accounts/migrations/0006_profile_current_team.py b/hc/accounts/migrations/0006_profile_current_team.py index 2d93e974..40cf933f 100644 --- a/hc/accounts/migrations/0006_profile_current_team.py +++ b/hc/accounts/migrations/0006_profile_current_team.py @@ -16,6 +16,6 @@ class Migration(migrations.Migration): migrations.AddField( model_name='profile', name='current_team', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='accounts.Profile'), + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='accounts.Profile'), ), ] diff --git a/hc/accounts/models.py b/hc/accounts/models.py index 369fab24..da0f13fc 100644 --- a/hc/accounts/models.py +++ b/hc/accounts/models.py @@ -104,5 +104,5 @@ class Profile(models.Model): class Member(models.Model): - team = models.ForeignKey(Profile) - user = models.ForeignKey(User) + team = models.ForeignKey(Profile, models.CASCADE) + user = models.ForeignKey(User, models.CASCADE) diff --git a/hc/accounts/tests/test_admin.py b/hc/accounts/tests/test_admin.py new file mode 100644 index 00000000..48fb6691 --- /dev/null +++ b/hc/accounts/tests/test_admin.py @@ -0,0 +1,18 @@ +from hc.test import BaseTestCase + + +class AccountsAdminTestCase(BaseTestCase): + + def setUp(self): + super(AccountsAdminTestCase, self).setUp() + + self.alice.is_staff = True + self.alice.is_superuser = True + self.alice.save() + + def test_it_shows_profiles(self): + self.client.login(username="alice@example.org", password="password") + + r = self.client.get("/admin/accounts/profile/") + self.assertContains(r, "alice@example.org") + self.assertContains(r, "bob@example.org") diff --git a/hc/api/admin.py b/hc/api/admin.py index 8b4b40d4..f22ed320 100644 --- a/hc/api/admin.py +++ b/hc/api/admin.py @@ -1,6 +1,7 @@ from django.contrib import admin from django.core.paginator import Paginator from django.db import connection +from django.utils.safestring import mark_safe from hc.api.models import Channel, Check, Notification, Ping from hc.lib.date import format_duration @@ -164,14 +165,14 @@ class ChannelsAdmin(admin.ModelAdmin): def email(self, obj): return obj.user.email if obj.user else None + @mark_safe def formatted_kind(self, obj): if obj.kind == "email" and not obj.email_verified: - return "Email (unverified)" + return "Email (unconfirmed)" return obj.get_kind_display() formatted_kind.short_description = "Kind" - formatted_kind.allow_tags = True def num_notifications(self, obj): return Notification.objects.filter(channel=obj).count() diff --git a/hc/api/migrations/0001_initial.py b/hc/api/migrations/0001_initial.py index 77cd5512..084a1b40 100644 --- a/hc/api/migrations/0001_initial.py +++ b/hc/api/migrations/0001_initial.py @@ -19,7 +19,7 @@ class Migration(migrations.Migration): ('id', models.AutoField(auto_created=True, primary_key=True, verbose_name='ID', serialize=False)), ('code', models.UUIDField(default=uuid.uuid4, editable=False)), ('last_ping', models.DateTimeField(null=True, blank=True)), - ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)), + ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)), ], ), ] diff --git a/hc/api/migrations/0005_auto_20150630_2021.py b/hc/api/migrations/0005_auto_20150630_2021.py index 02620d22..40ec1533 100644 --- a/hc/api/migrations/0005_auto_20150630_2021.py +++ b/hc/api/migrations/0005_auto_20150630_2021.py @@ -15,6 +15,6 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='check', name='user', - field=models.ForeignKey(blank=True, to=settings.AUTH_USER_MODEL, null=True), + field=models.ForeignKey(blank=True, to=settings.AUTH_USER_MODEL, null=True, on_delete=models.CASCADE), ), ] diff --git a/hc/api/migrations/0007_ping.py b/hc/api/migrations/0007_ping.py index b70f4722..9ead6d13 100644 --- a/hc/api/migrations/0007_ping.py +++ b/hc/api/migrations/0007_ping.py @@ -20,7 +20,7 @@ class Migration(migrations.Migration): ('method', models.CharField(max_length=10)), ('ua', models.CharField(max_length=100, blank=True)), ('body', models.TextField(blank=True)), - ('owner', models.ForeignKey(to='api.Check')), + ('owner', models.ForeignKey(to='api.Check', on_delete=models.CASCADE)), ], ), ] diff --git a/hc/api/migrations/0010_channel.py b/hc/api/migrations/0010_channel.py index 280f2c81..4e03edb0 100644 --- a/hc/api/migrations/0010_channel.py +++ b/hc/api/migrations/0010_channel.py @@ -24,7 +24,7 @@ class Migration(migrations.Migration): ('value', models.CharField(max_length=200, blank=True)), ('email_verified', models.BooleanField(default=False)), ('checks', models.ManyToManyField(to='api.Check')), - ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)), + ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)), ], ), ] diff --git a/hc/api/migrations/0011_notification.py b/hc/api/migrations/0011_notification.py index 21a079c4..7557363f 100644 --- a/hc/api/migrations/0011_notification.py +++ b/hc/api/migrations/0011_notification.py @@ -18,8 +18,8 @@ class Migration(migrations.Migration): ('check_status', models.CharField(max_length=6)), ('created', models.DateTimeField(auto_now_add=True)), ('status', models.IntegerField(default=0)), - ('channel', models.ForeignKey(to='api.Channel')), - ('owner', models.ForeignKey(to='api.Check')), + ('channel', models.ForeignKey(to='api.Channel', on_delete=models.CASCADE)), + ('owner', models.ForeignKey(to='api.Check', on_delete=models.CASCADE)), ], ), ] diff --git a/hc/api/models.py b/hc/api/models.py index 4b69ad85..68555e9a 100644 --- a/hc/api/models.py +++ b/hc/api/models.py @@ -56,7 +56,7 @@ class Check(models.Model): name = models.CharField(max_length=100, blank=True) tags = models.CharField(max_length=500, blank=True) code = models.UUIDField(default=uuid.uuid4, editable=False, db_index=True) - user = models.ForeignKey(User, blank=True, null=True) + user = models.ForeignKey(User, models.CASCADE, blank=True, null=True) created = models.DateTimeField(auto_now_add=True) kind = models.CharField(max_length=10, default="simple", choices=CHECK_KINDS) @@ -204,7 +204,7 @@ class Check(models.Model): class Ping(models.Model): n = models.IntegerField(null=True) - owner = models.ForeignKey(Check) + owner = models.ForeignKey(Check, models.CASCADE) created = models.DateTimeField(auto_now_add=True) scheme = models.CharField(max_length=10, default="http") remote_addr = models.GenericIPAddressField(blank=True, null=True) @@ -214,7 +214,7 @@ class Ping(models.Model): class Channel(models.Model): code = models.UUIDField(default=uuid.uuid4, editable=False) - user = models.ForeignKey(User) + user = models.ForeignKey(User, models.CASCADE) created = models.DateTimeField(auto_now_add=True) kind = models.CharField(max_length=20, choices=CHANNEL_KINDS) value = models.TextField(blank=True) @@ -378,9 +378,9 @@ class Notification(models.Model): get_latest_by = "created" code = models.UUIDField(default=uuid.uuid4, null=True, editable=False) - owner = models.ForeignKey(Check) + owner = models.ForeignKey(Check, models.CASCADE) check_status = models.CharField(max_length=6) - channel = models.ForeignKey(Channel) + channel = models.ForeignKey(Channel, models.CASCADE) created = models.DateTimeField(auto_now_add=True) error = models.CharField(max_length=200, blank=True) diff --git a/hc/api/tests/test_admin.py b/hc/api/tests/test_admin.py index 7f55787f..20e0a075 100644 --- a/hc/api/tests/test_admin.py +++ b/hc/api/tests/test_admin.py @@ -28,4 +28,4 @@ class ApiAdminTestCase(BaseTestCase): value="foo@example.org") r = self.client.get("/admin/api/channel/") - self.assertContains(r, "Email (unverified)") + self.assertContains(r, "Email (unconfirmed)") diff --git a/hc/api/tests/test_notify.py b/hc/api/tests/test_notify.py index d9ab80d5..b56e3f4d 100644 --- a/hc/api/tests/test_notify.py +++ b/hc/api/tests/test_notify.py @@ -1,6 +1,8 @@ +from datetime import timedelta as td import json from django.core import mail +from django.utils.timezone import now from hc.api.models import Channel, Check, Notification from hc.test import BaseTestCase from mock import patch @@ -13,6 +15,7 @@ class NotifyTestCase(BaseTestCase): self.check = Check() self.check.status = status self.check.user = self.alice + self.check.last_ping = now() - td(minutes=61) self.check.save() self.channel = Channel(user=self.alice) @@ -172,7 +175,7 @@ class NotifyTestCase(BaseTestCase): payload = kwargs["json"] attachment = payload["attachments"][0] fields = {f["title"]: f["value"] for f in attachment["fields"]} - self.assertEqual(fields["Last Ping"], "Never") + self.assertEqual(fields["Last Ping"], "an hour ago") @patch("hc.api.transports.requests.request") def test_slack_with_complex_value(self, mock_post): @@ -280,7 +283,7 @@ class NotifyTestCase(BaseTestCase): payload = kwargs["json"] attachment = payload["attachments"][0] fields = {f["title"]: f["value"] for f in attachment["fields"]} - self.assertEqual(fields["Last Ping"], "Never") + self.assertEqual(fields["Last Ping"], "an hour ago") @patch("hc.api.transports.requests.request") def test_pushbullet(self, mock_post): diff --git a/hc/payments/migrations/0001_initial.py b/hc/payments/migrations/0001_initial.py index 0f87d3de..48b241a7 100644 --- a/hc/payments/migrations/0001_initial.py +++ b/hc/payments/migrations/0001_initial.py @@ -19,7 +19,7 @@ class Migration(migrations.Migration): ('customer_id', models.CharField(blank=True, max_length=36)), ('payment_method_token', models.CharField(blank=True, max_length=35)), ('subscription_id', models.CharField(blank=True, max_length=10)), - ('user', models.OneToOneField(blank=True, null=True, to=settings.AUTH_USER_MODEL)), + ('user', models.OneToOneField(blank=True, null=True, to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)), ], ), ] diff --git a/hc/payments/models.py b/hc/payments/models.py index 6c05ef4c..caf008bb 100644 --- a/hc/payments/models.py +++ b/hc/payments/models.py @@ -18,7 +18,7 @@ class SubscriptionManager(models.Manager): class Subscription(models.Model): - user = models.OneToOneField(User, blank=True, null=True) + user = models.OneToOneField(User, models.CASCADE, blank=True, null=True) customer_id = models.CharField(max_length=36, blank=True) payment_method_token = models.CharField(max_length=35, blank=True) subscription_id = models.CharField(max_length=10, blank=True) diff --git a/hc/urls.py b/hc/urls.py index 5b9b7d4e..090dc567 100644 --- a/hc/urls.py +++ b/hc/urls.py @@ -5,7 +5,7 @@ from hc.accounts.views import login as hc_login urlpatterns = [ url(r'^admin/login/', hc_login, {"show_password": True}), - url(r'^admin/', include(admin.site.urls)), + url(r'^admin/', admin.site.urls), url(r'^accounts/', include('hc.accounts.urls')), url(r'^', include('hc.api.urls')), url(r'^', include('hc.front.urls')),