Notification emails #80
@ -26,7 +26,15 @@ class UserAdmin(auth_admin.UserAdmin):
|
|||||||
(None, {'fields': ('username', 'password')}),
|
(None, {'fields': ('username', 'password')}),
|
||||||
(
|
(
|
||||||
_('Personal info'),
|
_('Personal info'),
|
||||||
{'fields': ('full_name', 'image', 'email', 'badges')},
|
{
|
||||||
|
'fields': (
|
||||||
|
'full_name',
|
||||||
|
'image',
|
||||||
|
'email',
|
||||||
|
'badges',
|
||||||
|
'is_subscribed_to_notification_emails',
|
||||||
|
)
|
||||||
|
},
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
_('Permissions'),
|
_('Permissions'),
|
||||||
|
5
users/forms.py
Normal file
5
users/forms.py
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
from django import forms
|
||||||
|
|
||||||
|
|
||||||
|
class SubscribeNotificationEmailsForm(forms.Form):
|
||||||
|
subscribe = forms.BooleanField(widget=forms.HiddenInput(), required=False)
|
@ -25,7 +25,11 @@ class Command(BaseCommand):
|
|||||||
to_send = []
|
to_send = []
|
||||||
for n in batch:
|
for n in batch:
|
||||||
n.processed_by_mailer_at = timezone.now()
|
n.processed_by_mailer_at = timezone.now()
|
||||||
# TODO check some form of recipient.is_subscribed(n):
|
if not recipient.is_subscribed_to_notification_emails:
|
||||||
|
continue
|
||||||
|
# check that email is confirmed to avoid spamming unsuspecting email owners
|
||||||
|
if recipient.confirmed_email_at is None:
|
||||||
|
continue
|
||||||
n.sent = True
|
n.sent = True
|
||||||
to_send.append(n)
|
to_send.append(n)
|
||||||
# first mark as processed, then send: avoid spamming in case of a crash-loop
|
# first mark as processed, then send: avoid spamming in case of a crash-loop
|
||||||
|
@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 4.2.11 on 2024-04-15 12:53
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('users', '0002_moderators_group'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='user',
|
||||||
|
name='is_subscribed_to_notification_emails',
|
||||||
|
field=models.BooleanField(default=True),
|
||||||
|
),
|
||||||
|
]
|
@ -36,6 +36,7 @@ class User(TrackChangesMixin, AbstractUser):
|
|||||||
'confirmed_email_at',
|
'confirmed_email_at',
|
||||||
'full_name',
|
'full_name',
|
||||||
'email',
|
'email',
|
||||||
|
'is_subscribed_to_notification_emails',
|
||||||
}
|
}
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -49,6 +50,8 @@ class User(TrackChangesMixin, AbstractUser):
|
|||||||
date_deletion_requested = models.DateTimeField(null=True, blank=True)
|
date_deletion_requested = models.DateTimeField(null=True, blank=True)
|
||||||
confirmed_email_at = models.DateTimeField(null=True, blank=True)
|
confirmed_email_at = models.DateTimeField(null=True, blank=True)
|
||||||
|
|
||||||
|
is_subscribed_to_notification_emails = models.BooleanField(null=False, default=True)
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return f'{self.full_name or self.username}'
|
return f'{self.full_name or self.username}'
|
||||||
|
|
||||||
|
@ -117,4 +117,25 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<h1 class="mb-3 mt-5">Notifications</h1>
|
||||||
|
<div class="box settings">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6 mb-4">
|
||||||
|
<form action="{% url 'users:subscribe-notification-emails' %}" method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
{{ subscribe_notification_emails_form }}
|
||||||
|
{% if user.is_subscribed_to_notification_emails %}
|
||||||
|
You are subscribed to notification emails.
|
||||||
|
<button class="btn" type="submit">Unsubscribe</button>
|
||||||
|
{% if not user.confirmed_email_at %}
|
||||||
|
<p class="helptext text-warning">Your need to confirm your email to receive notification emails.</p>
|
||||||
|
{% endif %}
|
||||||
|
{% else %}
|
||||||
|
You are not subscribed to notification emails.
|
||||||
|
<button class="btn" type="submit">Subscribe</button>
|
||||||
|
{% endif %}
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{% endblock settings %}
|
{% endblock settings %}
|
||||||
|
@ -11,6 +11,11 @@ urlpatterns = [
|
|||||||
include(
|
include(
|
||||||
[
|
[
|
||||||
path('profile/', settings.ProfileView.as_view(), name='my-profile'),
|
path('profile/', settings.ProfileView.as_view(), name='my-profile'),
|
||||||
|
path(
|
||||||
|
'profile/subscribe-notification-emails/',
|
||||||
|
settings.SubscribeNotificationEmailsView.as_view(),
|
||||||
|
name='subscribe-notification-emails',
|
||||||
|
),
|
||||||
path('delete/', settings.DeleteView.as_view(), name='my-profile-delete'),
|
path('delete/', settings.DeleteView.as_view(), name='my-profile-delete'),
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
|
@ -1,7 +1,11 @@
|
|||||||
"""User profile pages."""
|
"""User profile pages."""
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
|
from django.urls import reverse_lazy
|
||||||
from django.views.generic import TemplateView
|
from django.views.generic import TemplateView
|
||||||
|
from django.views.generic.edit import FormView
|
||||||
|
|
||||||
|
from users.forms import SubscribeNotificationEmailsForm
|
||||||
|
|
||||||
User = get_user_model()
|
User = get_user_model()
|
||||||
|
|
||||||
@ -11,8 +15,25 @@ class ProfileView(LoginRequiredMixin, TemplateView):
|
|||||||
|
|
||||||
template_name = 'users/settings/profile.html'
|
template_name = 'users/settings/profile.html'
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super().get_context_data(**kwargs)
|
||||||
|
context['subscribe_notification_emails_form'] = SubscribeNotificationEmailsForm(
|
||||||
|
{'subscribe': not self.request.user.is_subscribed_to_notification_emails},
|
||||||
|
)
|
||||||
|
return context
|
||||||
|
|
||||||
|
|
||||||
class DeleteView(LoginRequiredMixin, TemplateView):
|
class DeleteView(LoginRequiredMixin, TemplateView):
|
||||||
"""Template view where account deletion can be requested."""
|
"""Template view where account deletion can be requested."""
|
||||||
|
|
||||||
template_name = 'users/settings/delete.html'
|
template_name = 'users/settings/delete.html'
|
||||||
|
|
||||||
|
|
||||||
|
class SubscribeNotificationEmailsView(LoginRequiredMixin, FormView):
|
||||||
|
form_class = SubscribeNotificationEmailsForm
|
||||||
|
success_url = reverse_lazy('users:my-profile')
|
||||||
|
|
||||||
|
def form_valid(self, form):
|
||||||
|
self.request.user.is_subscribed_to_notification_emails = form.cleaned_data['subscribe']
|
||||||
|
self.request.user.save(update_fields={'is_subscribed_to_notification_emails'})
|
||||||
|
return super().form_valid(form)
|
||||||
|
Loading…
Reference in New Issue
Block a user