--- /dev/null
+from django.apps import AppConfig
+
+
+class CFAppConfig(AppConfig):
+ name = 'pgcommitfest.commitfest'
+
+ def ready(self):
+ from pgcommitfest.auth import auth_user_data_received
+ from pgcommitfest.userprofile.util import handle_user_data
+
+ auth_user_data_received.connect(handle_user_data)
# Uncomment the next line to enable admin documentation:
# 'django.contrib.admindocs',
'pgcommitfest.selectable',
- 'pgcommitfest.commitfest',
+ 'pgcommitfest.commitfest.apps.CFAppConfig',
'pgcommitfest.mailqueue',
'pgcommitfest.userprofile',
)
# Account management
url(r'^account/profile/$', pgcommitfest.userprofile.views.userprofile),
- url(r'^account/profile/delmail/$', pgcommitfest.userprofile.views.deletemail),
- url(r'^account/profile/confirm/([0-9a-f]+)/$', pgcommitfest.userprofile.views.confirmemail),
# Examples:
# url(r'^$', 'pgpgcommitfest.commitfest.views.home', name='home),
from django import forms
-from django.contrib.auth.models import User
from .models import UserProfile, UserExtraEmail
super(UserProfileForm, self).__init__(*args, **kwargs)
self.user = user
+ mailhelp = "To add a new address to choose from, update your user profile on <a href=\"https://www.postgresql.org/account/profile/\">postgresql.org</a>."
+
self.fields['selectedemail'].empty_label = self.user.email
- self.fields['selectedemail'].queryset = UserExtraEmail.objects.filter(user=self.user, confirmed=True)
+ self.fields['selectedemail'].queryset = UserExtraEmail.objects.filter(user=self.user)
+ self.fields['selectedemail'].help_text = mailhelp
self.fields['notifyemail'].empty_label = self.user.email
- self.fields['notifyemail'].queryset = UserExtraEmail.objects.filter(user=self.user, confirmed=True)
-
-
-class MailForm(forms.Form):
- email = forms.EmailField()
- email2 = forms.EmailField(label="Repeat email")
-
- def clean_email(self):
- email = self.cleaned_data['email']
-
- if User.objects.filter(email=email).exists():
- raise forms.ValidationError("This email is already in use by another account")
-
- return email
-
- def clean_email2(self):
- # If the primary email checker had an exception, the data will be gone
- # from the cleaned_data structure
- if 'email' not in self.cleaned_data:
- return self.cleaned_data['email2']
- email1 = self.cleaned_data['email']
- email2 = self.cleaned_data['email2']
-
- if email1 != email2:
- raise forms.ValidationError("Email addresses don't match")
-
- return email2
+ self.fields['notifyemail'].queryset = UserExtraEmail.objects.filter(user=self.user)
+ self.fields['notifyemail'].help_text = mailhelp
--- /dev/null
+# Generated by Django 2.2.11 on 2020-08-11 11:09
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('userprofile', '0002_notifications'),
+ ]
+
+ operations = [
+ migrations.RemoveField(
+ model_name='userextraemail',
+ name='confirmed',
+ ),
+ migrations.RemoveField(
+ model_name='userextraemail',
+ name='token',
+ ),
+ migrations.RemoveField(
+ model_name='userextraemail',
+ name='tokensent',
+ ),
+ migrations.AlterField(
+ model_name='userprofile',
+ name='notifyemail',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='notifier', to='userprofile.UserExtraEmail', verbose_name='Notifications sent to'),
+ ),
+ migrations.AlterField(
+ model_name='userprofile',
+ name='selectedemail',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='userprofile.UserExtraEmail', verbose_name='Sender email'),
+ ),
+ ]
class UserExtraEmail(models.Model):
user = models.ForeignKey(User, null=False, blank=False, db_index=True, on_delete=models.CASCADE)
email = models.EmailField(max_length=100, null=False, blank=False, unique=True)
- confirmed = models.BooleanField(null=False, blank=False, default=False)
- token = models.CharField(max_length=100, null=False, blank=True)
- tokensent = models.DateTimeField(null=False, blank=False)
def __str__(self):
return self.email
class UserProfile(models.Model):
user = models.OneToOneField(User, null=False, blank=False, on_delete=models.CASCADE)
selectedemail = models.ForeignKey(UserExtraEmail, null=True, blank=True,
- verbose_name='Sender email', on_delete=models.CASCADE)
+ verbose_name='Sender email', on_delete=models.SET_NULL)
notifyemail = models.ForeignKey(UserExtraEmail, null=True, blank=True,
verbose_name='Notifications sent to',
- related_name='notifier', on_delete=models.CASCADE)
+ related_name='notifier', on_delete=models.SET_NULL)
notify_all_author = models.BooleanField(null=False, blank=False, default=False, verbose_name="Notify on all where author")
notify_all_reviewer = models.BooleanField(null=False, blank=False, default=False, verbose_name="Notify on all where reviewer")
notify_all_committer = models.BooleanField(null=False, blank=False, default=False, verbose_name="Notify on all where committer")
+++ /dev/null
-Somebody, probably you, has registered this email address as a secondary
-address for the account {{user.username}} on commitfest.postgresql.org.
-
-To confirm this addition, please click on the following link:
-
-https://commitfest.postgresql.org/account/profile/confirm/{{token}}/
-
</div>
</form>
-<h2>Extra email addresses</h2>
-<p>
-The following extra email addresses are registered for your account:
-</p>
-<ul>
-{%for e in extramails%}
- <li>{{e.email}}{%if not e.confirmed%} (<i>Pending confirmation</i>){%endif%} <a href="delmail/?{{e.id}}">delete</a></li>
-{%endfor%}
-</ul>
-
-<h3>Add email</h3>
-<form class="form-horizontal" method="post" action=".">{%csrf_token%}
-{%if mailform.errors%}
- <div class="alert">Please correct the errors below, and re-submit the form.</div>
-{%endif%}
-{%if mailform.non_field_errors%}
- <div class="alert alert-danger">{{mailform.non_field_errors}}</div>
-{%endif%}
- {%for field in mailform%}
- <div class="form-group">
- {{field|label_class:"control-label col-lg-1"}}
- <div class="col-lg-11 controls">
- {%if field.errors %}
- {%for e in field.errors%}
- <div class="alert alert-danger">{{e}}</div>
- {%endfor%}
- {%endif%}
-{{field|field_class:"form-control"}}
-{%if field.help_text%}<br/>{{field.help_text|safe}}{%endif%}</div>
- </div>
-{%endfor%}
-
- <div class="form-group">
- <div class="col-lg-12">
- <div class="control"><input type="submit" class="btn btn-default" name="submit" value="Add email"></div>
- </div>
- </div>
-</form>
{%endblock%}
-from Crypto.Hash import SHA256
-from Crypto import Random
from email.utils import formataddr
from email.header import Header
-from .models import UserProfile
-
-
-def generate_random_token():
- """
- Generate a random token of 64 characters. This token will be
- generated using a strong random number, and then hex encoded to make
- sure all characters are safe to put in emails and URLs.
- """
- s = SHA256.new()
- r = Random.new()
- s.update(r.read(250))
- return s.hexdigest()
+from .models import UserProfile, UserExtraEmail
class UserWrapper(object):
def email(self):
try:
up = UserProfile.objects.get(user=self.user)
- if up.selectedemail and up.selectedemail.confirmed:
+ if up.selectedemail:
return up.selectedemail.email
else:
return self.user.email
return formataddr((
str(Header("%s %s" % (self.user.first_name, self.user.last_name), 'utf-8')),
self.email))
+
+
+def handle_user_data(sender, **kwargs):
+ user = kwargs.pop('user')
+ userdata = kwargs.pop('userdata')
+
+ secondary = userdata.get('secondaryemails', [])
+
+ # Remove any email attached to this user that are not upstream. Since the foreign keys
+ # are set to SET_NULL, they will all revert to being the users default in this case.
+ UserExtraEmail.objects.filter(user=user).exclude(email__in=secondary).delete()
+
+ # Then add back any of the ones that aren't there
+ current = set([e.email for e in UserExtraEmail.objects.filter(user=user)])
+ for e in set(secondary).difference(current):
+ UserExtraEmail(user=user, email=e).save()
from django.shortcuts import render
from django.http import HttpResponseRedirect
-from django.template import RequestContext
from django.db import transaction
from django.contrib import messages
from django.contrib.auth.decorators import login_required
-from django.conf import settings
-from datetime import datetime
-
-from pgcommitfest.mailqueue.util import send_template_mail
-
-from .models import UserProfile, UserExtraEmail
-from .forms import UserProfileForm, MailForm
-from .util import generate_random_token
+from .models import UserProfile
+from .forms import UserProfileForm
@login_required
@transaction.atomic
def userprofile(request):
(profile, created) = UserProfile.objects.get_or_create(user=request.user)
- form = mailform = None
if request.method == 'POST':
- if request.POST['submit'] == 'Save':
- form = UserProfileForm(request.user, request.POST, instance=profile)
- if form.is_valid():
- form.save()
- messages.add_message(request, messages.INFO, "User profile saved.")
- return HttpResponseRedirect('.')
- elif request.POST['submit'] == 'Add email':
- mailform = MailForm(request.POST)
- if mailform.is_valid():
- m = UserExtraEmail(user=request.user,
- email=mailform.cleaned_data['email'],
- confirmed=False,
- token=generate_random_token(),
- tokensent=datetime.now())
- m.save()
- send_template_mail(settings.NOTIFICATION_FROM,
- request.user.username,
- m.email,
- 'Your email address for commitfest.postgresql.org',
- 'extra_email_mail.txt',
- {'token': m.token, 'user': m.user})
- messages.info(request, "A confirmation token has been sent to %s" % m.email)
- return HttpResponseRedirect('.')
- else:
- messages.error(request, "Invalid submit button pressed! Nothing saved.")
+ form = UserProfileForm(request.user, request.POST, instance=profile)
+ if form.is_valid():
+ form.save()
+ messages.add_message(request, messages.INFO, "User profile saved.")
return HttpResponseRedirect('.')
-
- if not form:
+ else:
form = UserProfileForm(request.user, instance=profile)
- if not mailform:
- mailform = MailForm()
-
- extramails = UserExtraEmail.objects.filter(user=request.user)
return render(request, 'userprofileform.html', {
'form': form,
- 'extramails': extramails,
- 'mailform': mailform,
})
-
-
-@login_required
-@transaction.atomic
-def deletemail(request):
- try:
- id = int(request.META['QUERY_STRING'])
- except ValueError:
- messages.error(request, "Invalid format of id in query string")
- return HttpResponseRedirect('../')
-
- try:
- e = UserExtraEmail.objects.get(user=request.user, id=id)
- except UserExtraEmail.DoesNotExist:
- messages.error(request, "Specified email address does not exist on this user")
- return HttpResponseRedirect('../')
-
- messages.info(request, "Email address %s deleted." % e.email)
- e.delete()
- return HttpResponseRedirect('../')
-
-
-@login_required
-@transaction.atomic
-def confirmemail(request, tokenhash):
- try:
- e = UserExtraEmail.objects.get(user=request.user, token=tokenhash)
- if e.confirmed:
- messages.warning(request, "This email address has already been confirmed.")
- else:
- # Ok, it's not confirmed. So let's do that now
- e.confirmed = True
- e.token = ''
- e.save()
- messages.info(request, "Email address %s added to profile." % e.email)
- except UserExtraEmail.DoesNotExist:
- messages.error(request, "Token %s was not found for your user. It may be because it has already been used?" % tokenhash)
-
- return HttpResponseRedirect("../../")