SciPost Code Repository

Skip to content
Snippets Groups Projects
Commit 000ff5c3 authored by Jorran de Wit's avatar Jorran de Wit
Browse files

Update Profile/Mail construction

parent f9e02d9f
No related branches found
No related tags found
No related merge requests found
...@@ -4,10 +4,16 @@ __license__ = "AGPL v3" ...@@ -4,10 +4,16 @@ __license__ = "AGPL v3"
from django.contrib import admin from django.contrib import admin
from .models import Profile from .models import Profile, ProfileEmail
class ProfileEmailInline(admin.TabularInline):
model = ProfileEmail
extra = 0
class ProfileAdmin(admin.ModelAdmin): class ProfileAdmin(admin.ModelAdmin):
search_fields = ['first_name', 'last_name', 'email', 'orcid_id'] search_fields = ['first_name', 'last_name', 'email', 'orcid_id']
inlines = [ProfileEmailInline]
admin.site.register(Profile, ProfileAdmin) admin.site.register(Profile, ProfileAdmin)
...@@ -4,36 +4,37 @@ __license__ = "AGPL v3" ...@@ -4,36 +4,37 @@ __license__ = "AGPL v3"
from django import forms from django import forms
from .models import Profile, AlternativeEmail from .models import Profile, ProfileEmail
class ProfileForm(forms.ModelForm): class ProfileForm(forms.ModelForm):
email = forms.EmailField()
class Meta: class Meta:
model = Profile model = Profile
fields = ['title', 'first_name', 'last_name', 'email', fields = ['title', 'first_name', 'last_name',
'discipline', 'expertises', 'orcid_id', 'webpage', 'discipline', 'expertises', 'orcid_id', 'webpage',
'accepts_SciPost_emails', 'accepts_refereeing_requests'] 'accepts_SciPost_emails', 'accepts_refereeing_requests']
def clean_email(self): def clean_email(self):
""" """Check that the email isn't yet associated to an existing Profile."""
Check that the email isn't yet associated to an existing Profile
(via either the email field or the m2m-related alternativeemails).
"""
cleaned_email = self.cleaned_data['email'] cleaned_email = self.cleaned_data['email']
if Profile.objects.filter(email=cleaned_email).exists(): if self.instance.emails.filter(email=cleaned_email).exists():
raise forms.ValidationError( raise forms.ValidationError('A Profile with this email already exists.')
'A Profile with this email (as primary email) already exists.')
elif AlternativeEmail.objects.filter(email=cleaned_email).exists():
raise forms.ValidationError(
'A Profile with this email (as alternative email) already exists.')
return cleaned_email return cleaned_email
def save(self):
profile = super().save()
ProfileEmail.objects.create(
profile=profile,
email=self.cleaned_data['email'],
primary=True)
class AlternativeEmailForm(forms.ModelForm): class ProfileEmailForm(forms.ModelForm):
class Meta: class Meta:
model = AlternativeEmail model = ProfileEmail
fields = ['email', 'still_valid'] fields = ['email', 'still_valid']
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
......
# -*- coding: utf-8 -*-
# Generated by Django 1.11.4 on 2018-10-02 09:04
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('profiles', '0006_populate_profile_from_reginv_and_refinv'),
]
operations = [
migrations.RenameModel('AlternativeEmail', 'ProfileEmail')
]
# -*- coding: utf-8 -*-
# Generated by Django 1.11.4 on 2018-10-02 09:06
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('profiles', '0007_auto_20181002_1104'),
]
operations = [
migrations.AlterField(
model_name='profileemail',
name='profile',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='emails', to='profiles.Profile'),
),
]
# -*- coding: utf-8 -*-
# Generated by Django 1.11.4 on 2018-10-02 09:10
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('profiles', '0008_auto_20181002_1106'),
]
operations = [
migrations.AddField(
model_name='profileemail',
name='primary',
field=models.BooleanField(default=False),
),
]
# -*- coding: utf-8 -*-
# Generated by Django 1.11.4 on 2018-10-02 09:14
from __future__ import unicode_literals
from django.db import migrations
def add_primary_emails(apps, schema_editor):
Profile = apps.get_model('profiles', 'Profile')
ProfileEmail = apps.get_model('profiles', 'ProfileEmail')
for profile in Profile.objects.all():
ProfileEmail.objects.get_or_create(
profile=profile,
email=profile.email,
defaults={'primary': True})
class Migration(migrations.Migration):
dependencies = [
('profiles', '0009_profileemail_primary'),
]
operations = [
migrations.RunPython(add_primary_emails)
]
...@@ -38,7 +38,6 @@ class Profile(models.Model): ...@@ -38,7 +38,6 @@ class Profile(models.Model):
title = models.CharField(max_length=4, choices=TITLE_CHOICES) title = models.CharField(max_length=4, choices=TITLE_CHOICES)
first_name = models.CharField(max_length=64) first_name = models.CharField(max_length=64)
last_name = models.CharField(max_length=64) last_name = models.CharField(max_length=64)
email = models.EmailField()
discipline = models.CharField(max_length=20, choices=SCIPOST_DISCIPLINES, discipline = models.CharField(max_length=20, choices=SCIPOST_DISCIPLINES,
default=DISCIPLINE_PHYSICS, verbose_name='Main discipline') default=DISCIPLINE_PHYSICS, verbose_name='Main discipline')
expertises = ChoiceArrayField( expertises = ChoiceArrayField(
...@@ -59,19 +58,19 @@ class Profile(models.Model): ...@@ -59,19 +58,19 @@ class Profile(models.Model):
def __str__(self): def __str__(self):
return '%s, %s %s' % (self.last_name, self.get_title_display(), self.first_name) return '%s, %s %s' % (self.last_name, self.get_title_display(), self.first_name)
@property
def email(self):
return self.emails.filter(primary=True).first()
class AlternativeEmail(models.Model):
""" class ProfileEmail(models.Model):
It is often the case that somebody has multiple email addresses. """Any email related to a Profile instance."""
The 'email' field of a given Profile, Contributor or other person-based object
is then interpreted as representing the current address.
An AlternativeEmail is bound to a Profile and holds info on eventual
secondary email addresses.
"""
profile = models.ForeignKey(Profile, on_delete=models.CASCADE) profile = models.ForeignKey(Profile, on_delete=models.CASCADE)
email = models.EmailField() email = models.EmailField()
still_valid = models.BooleanField(default=True) still_valid = models.BooleanField(default=True)
primary = models.BooleanField(default=False)
class Meta: class Meta:
unique_together = ['profile', 'email'] unique_together = ['profile', 'email']
ordering = ['-primary', '-still_valid', 'email']
default_related_name = 'emails' default_related_name = 'emails'
...@@ -11,12 +11,29 @@ ...@@ -11,12 +11,29 @@
<tr> <tr>
<td>Email(s)</td> <td>Email(s)</td>
<td> <td>
<ul> <table class="table table-sm table-borderless">
<li>{{ profile.email }} (current)</li> <thead>
{% for altemail in profile.emails.all %} <tr>
<li>{{ altemail.email }} (alt, {{ altemail.still_valid|yesno:"still valid,deprecated" }})</li> <th colspan="2">Email</th>
{% endfor %} <th>Still valid</th>
</ul> <th></th>
</tr>
</thead>
{% for profile_mail in profile.emails.all %}
<tr>
<td>{% if profile_mail.primary %}<strong>{% endif %}{{ profile_mail.email }}{% if profile_mail.primary %}</strong>{% endif %}</td>
<td>{{ profile_mail.primary|yesno:'Primary,Alternative' }}</td>
<td>
<i class="fa {{ profile_mail.still_valid|yesno:'fa-check-circle text-success,fa-times-circle text-danger' }}"></i>
</td>
<td class="d-flex">
<form method="post" action="{% url 'profiles:toggle_email_status' profile_mail.id %}">{% csrf_token %}<button type="submit" class="btn btn-link p-0">{{ profile_mail.still_valid|yesno:'Deprecate,Mark valid' }}</button></form>
<form method="post" action="{% url 'profiles:delete_profile_email' profile_mail.id %}">{% csrf_token %}<button type="submit" class="btn btn-link text-danger p-0 ml-2" onclick="return confirm('Sure you want to delete {{ profile_mail.email }}?')"><i class="fa fa-trash"></i></button></form>
</td>
</tr>
{% endfor %}
</table>
</td> </td>
</tr> </tr>
<tr> <tr>
...@@ -43,10 +60,10 @@ ...@@ -43,10 +60,10 @@
<li><a href="{% url 'profiles:profile_delete' pk=profile.id %}">Delete</a> this Profile</li> <li><a href="{% url 'profiles:profile_delete' pk=profile.id %}">Delete</a> this Profile</li>
<li> <li>
<div> <div>
Add an alternative email to this Profile: Add an email to this Profile:
<form action="{% url 'profiles:add_alternative_email' profile_id=profile.id %}" method="post"> <form action="{% url 'profiles:add_profile_email' profile_id=profile.id %}" method="post">
{% csrf_token %} {% csrf_token %}
{{ alternative_email_form|bootstrap }} {{ email_form|bootstrap }}
<input class="btn btn-outline-secondary" type="submit" value="Add"> <input class="btn btn-outline-secondary" type="submit" value="Add">
</form> </form>
</div> </div>
......
...@@ -33,8 +33,18 @@ urlpatterns = [ ...@@ -33,8 +33,18 @@ urlpatterns = [
name='profiles' name='profiles'
), ),
url( url(
r'^(?P<profile_id>[0-9]+)/add_alternative_email/$', r'^(?P<profile_id>[0-9]+)/add_email$',
views.add_alternative_email, views.add_profile_email,
name='add_alternative_email' name='add_profile_email'
),
url(
r'^(?P<email_id>[0-9]+)/toggle$',
views.toggle_email_status,
name='toggle_email_status'
),
url(
r'^(?P<email_id>[0-9]+)/delete$',
views.delete_profile_email,
name='delete_profile_email'
), ),
] ]
...@@ -6,6 +6,7 @@ from django.contrib import messages ...@@ -6,6 +6,7 @@ from django.contrib import messages
from django.core.urlresolvers import reverse, reverse_lazy from django.core.urlresolvers import reverse, reverse_lazy
from django.db import IntegrityError from django.db import IntegrityError
from django.shortcuts import get_object_or_404, render, redirect from django.shortcuts import get_object_or_404, render, redirect
from django.views.decorators.http import require_POST
from django.views.generic.edit import CreateView, UpdateView, DeleteView from django.views.generic.edit import CreateView, UpdateView, DeleteView
from django.views.generic.list import ListView from django.views.generic.list import ListView
...@@ -18,8 +19,8 @@ from scipost.models import Contributor ...@@ -18,8 +19,8 @@ from scipost.models import Contributor
from invitations.models import RegistrationInvitation from invitations.models import RegistrationInvitation
from submissions.models import RefereeInvitation from submissions.models import RefereeInvitation
from .models import Profile, AlternativeEmail from .models import Profile, ProfileEmail
from .forms import ProfileForm, AlternativeEmailForm, SearchTextForm from .forms import ProfileForm, ProfileEmailForm, SearchTextForm
...@@ -138,22 +139,42 @@ class ProfileListView(PermissionsMixin, PaginationMixin, ListView): ...@@ -138,22 +139,42 @@ class ProfileListView(PermissionsMixin, PaginationMixin, ListView):
'next_refinv_wo_profile': refinv_wo_profile.first(), 'next_refinv_wo_profile': refinv_wo_profile.first(),
'nr_reginv_wo_profile': reginv_wo_profile.count(), 'nr_reginv_wo_profile': reginv_wo_profile.count(),
'next_reginv_wo_profile': reginv_wo_profile.first(), 'next_reginv_wo_profile': reginv_wo_profile.first(),
'alternative_email_form': AlternativeEmailForm(), 'email_form': ProfileEmailForm(),
}) })
return context return context
@permission_required('scipost.can_create_profiles') @permission_required('scipost.can_create_profiles')
def add_alternative_email(request, profile_id): def add_profile_email(request, profile_id):
""" """
Add an alternative email address to a Profile. Add an email address to a Profile.
""" """
profile = get_object_or_404(Profile, pk=profile_id) profile = get_object_or_404(Profile, pk=profile_id)
form = AlternativeEmailForm(request.POST or None, profile=profile) form = ProfileEmailForm(request.POST or None, profile=profile)
if form.is_valid(): if form.is_valid():
form.save() form.save()
messages.success(request, 'Alternative email successfully added.') messages.success(request, 'Email successfully added.')
else: else:
for field, err in form.errors.items(): for field, err in form.errors.items():
messages.warning(request, err[0]) messages.warning(request, err[0])
return redirect(reverse('profiles:profiles')) return redirect(reverse('profiles:profiles'))
@require_POST
@permission_required('scipost.can_create_profiles')
def toggle_email_status(request, email_id):
"""Toggle valid/deprecated status of ProfileEmail."""
profile_email = get_object_or_404(ProfileEmail, pk=email_id)
ProfileEmail.objects.filter(id=email_id).update(still_valid=not profile_email.still_valid)
messages.success(request, 'Email updated')
return redirect('profiles:profiles')
@require_POST
@permission_required('scipost.can_create_profiles')
def delete_profile_email(request, email_id):
"""Delete ProfileEmail."""
profile_email = get_object_or_404(ProfileEmail, pk=email_id)
profile_email.delete()
messages.success(request, 'Email deleted')
return redirect('profiles:profiles')
...@@ -3,7 +3,6 @@ __license__ = "AGPL v3" ...@@ -3,7 +3,6 @@ __license__ = "AGPL v3"
from django.contrib import admin from django.contrib import admin
# from django.contrib.contenttypes.admin import GenericTabularInline
from django import forms from django import forms
from guardian.admin import GuardedModelAdmin from guardian.admin import GuardedModelAdmin
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment