-
Notifications
You must be signed in to change notification settings - Fork 173
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #85 from vitorfs/dev
Release v2.1
- Loading branch information
Showing
48 changed files
with
1,428 additions
and
835 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
from parsifal.utils.version import get_version | ||
|
||
VERSION = (2, 0, 5, "final", 0) | ||
VERSION = (2, 1, 0, "final", 0) | ||
|
||
__version__ = get_version(VERSION) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
default_app_config = "parsifal.apps.invites.apps.InvitesConfig" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
from django.contrib import admin | ||
from django.utils.translation import gettext_lazy as _ | ||
|
||
from parsifal.apps.invites.models import Invite | ||
|
||
|
||
@admin.register(Invite) | ||
class InviteAdmin(admin.ModelAdmin): | ||
date_hierarchy = "date_sent" | ||
list_display = ("get_invitee_email", "invited_by", "review", "status", "date_sent", "date_answered") | ||
list_select_related = ("invitee", "invited_by__profile", "review") | ||
list_filter = ("status",) | ||
raw_id_fields = ("invitee", "invited_by", "review") | ||
search_fields = ( | ||
"invitee_email", | ||
"invitee__email", | ||
"invitee__username", | ||
"invited_by__email", | ||
"invited_by__username", | ||
) | ||
readonly_fields = ("status", "date_sent", "code") | ||
fieldsets = ( | ||
(None, {"fields": ("review", "invited_by", "status", "code")}), | ||
(_("Invitee"), {"fields": ("invitee", "invitee_email")}), | ||
(_("Important dates"), {"fields": ("date_sent", "date_answered")}), | ||
) | ||
|
||
def get_invitee_email(self, obj): | ||
return obj.get_invitee_email() | ||
|
||
get_invitee_email.short_description = _("Invitee") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
from django.apps import AppConfig | ||
|
||
|
||
class InvitesConfig(AppConfig): | ||
default_auto_field = "django.db.models.BigAutoField" | ||
name = "parsifal.apps.invites" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
from django.utils.translation import gettext_lazy as _ | ||
|
||
|
||
class InviteStatus: | ||
PENDING = "pending" | ||
ACCEPTED = "accepted" | ||
REJECTED = "rejected" | ||
|
||
CHOICES = ( | ||
(PENDING, _("Pending")), | ||
(ACCEPTED, _("Accepted")), | ||
(REJECTED, _("Rejected")), | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
from django import forms | ||
from django.contrib.auth.models import User | ||
from django.contrib.sites.shortcuts import get_current_site | ||
from django.core.mail import EmailMultiAlternatives | ||
from django.db.models.functions import Lower | ||
from django.template.loader import render_to_string | ||
from django.utils.translation import gettext as _ | ||
|
||
from parsifal.apps.invites.constants import InviteStatus | ||
from parsifal.apps.invites.models import Invite | ||
|
||
|
||
class SendInviteForm(forms.ModelForm): | ||
class Meta: | ||
model = Invite | ||
fields = ("invitee", "invitee_email") | ||
|
||
def __init__(self, *args, request, review, **kwargs): | ||
self.request = request | ||
self.review = review | ||
super().__init__(*args, **kwargs) | ||
user_ids = {user.pk for user in self.request.user.profile.get_following()} | ||
self.fields["invitee"].queryset = ( | ||
User.objects.filter(pk__in=user_ids) | ||
.exclude(pk__in=self.review.co_authors.all()) | ||
.annotate(lower_username=Lower("username")) | ||
.order_by("lower_username") | ||
) | ||
self.fields["invitee"].label = _("Contacts") | ||
self.fields["invitee"].help_text = _("List of people that you are currently following on Parsifal.") | ||
|
||
self.fields["invitee_email"].label = _("Email address of the person you want to invite") | ||
self.fields["invitee_email"].help_text = _( | ||
"If the person you want to invite is not on Parsifal, you can inform their email address and we will send " | ||
"an invitation link to their inbox." | ||
) | ||
self.fields["invitee_email"].required = False | ||
|
||
def clean(self): | ||
cleaned_data = super().clean() | ||
if cleaned_data.get("invitee") and cleaned_data.get("invitee_email"): | ||
self.add_error( | ||
None, _("You must inform either a contact or an email address, but not both at the same time.") | ||
) | ||
if not cleaned_data.get("invitee") and not cleaned_data.get("invitee_email"): | ||
self.add_error(None, _("You must inform either a contact or an email address.")) | ||
return cleaned_data | ||
|
||
def clean_invitee(self): | ||
invitee = self.cleaned_data.get("invitee") | ||
if invitee: | ||
if self.review.is_author_or_coauthor(invitee): | ||
self.add_error("invitee", _("This person is already a co-author of this review.")) | ||
if Invite.objects.filter( | ||
review=self.review, invitee_email__iexact=invitee.email, status=InviteStatus.PENDING | ||
).exists(): | ||
self.add_error("invitee", _("This person already has a pending invite.")) | ||
return invitee | ||
|
||
def clean_invitee_email(self): | ||
invitee_email = self.cleaned_data.get("invitee_email") | ||
if invitee_email: | ||
invitee_email = User.objects.normalize_email(invitee_email) | ||
if invitee_email.lower() == self.request.user.email.lower(): | ||
self.add_error("invitee_email", _("You cannot invite yourself.")) | ||
try: | ||
user = User.objects.get(email__iexact=invitee_email) | ||
if self.review.is_author_or_coauthor(user): | ||
self.add_error("invitee_email", _("This person is already a co-author of this review.")) | ||
except User.DoesNotExist: | ||
pass | ||
if Invite.objects.filter( | ||
review=self.review, invitee_email__iexact=invitee_email, status=InviteStatus.PENDING | ||
).exists(): | ||
self.add_error("invitee_email", _("This person already has a pending invite.")) | ||
return invitee_email | ||
|
||
def send_mail(self): | ||
""" | ||
Send a django.core.mail.EmailMultiAlternatives to `to_email`. | ||
""" | ||
subject = render_to_string("invites/invite_subject.txt", {"invite": self.instance}) | ||
# Email subject *must not* contain newlines | ||
subject = "".join(subject.splitlines()) | ||
|
||
current_site = get_current_site(self.request) | ||
site_name = current_site.name | ||
domain = current_site.domain | ||
invited_by_name = self.instance.invited_by.profile.get_screen_name() | ||
from_email = f"{invited_by_name} via Parsifal <noreply@parsif.al>" | ||
to_email = self.instance.get_invitee_email() | ||
body = render_to_string( | ||
"invites/invite_email.html", | ||
{ | ||
"invite": self.instance, | ||
"site_name": site_name, | ||
"domain": domain, | ||
"protocol": "https" if self.request.is_secure() else "http", | ||
}, | ||
) | ||
|
||
email_message = EmailMultiAlternatives(subject, body, from_email, [to_email]) | ||
email_message.send() | ||
|
||
def save(self, commit=True): | ||
self.instance = super().save(commit=False) | ||
if self.instance.invitee: | ||
self.instance.invitee_email = self.instance.invitee.email | ||
else: | ||
self.instance.invitee = User.objects.filter(email__iexact=self.instance.invitee_email).first() | ||
self.instance.review = self.review | ||
self.instance.invited_by = self.request.user | ||
if commit: | ||
self.instance.save() | ||
self.send_mail() | ||
return self.instance |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
# Generated by Django 3.2.7 on 2021-09-07 17:05 | ||
|
||
from django.conf import settings | ||
from django.db import migrations, models | ||
import django.db.models.deletion | ||
import uuid | ||
|
||
|
||
class Migration(migrations.Migration): | ||
|
||
initial = True | ||
|
||
dependencies = [ | ||
('reviews', '0036_auto_20210906_2320'), | ||
migrations.swappable_dependency(settings.AUTH_USER_MODEL), | ||
] | ||
|
||
operations = [ | ||
migrations.CreateModel( | ||
name='Invite', | ||
fields=[ | ||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), | ||
('invitee_email', models.EmailField(blank=True, max_length=254, verbose_name='invitee email')), | ||
('status', models.CharField(choices=[('pending', 'Pending'), ('accepted', 'Accepted'), ('rejected', 'Rejected')], default='pending', max_length=32, verbose_name='status')), | ||
('code', models.UUIDField(default=uuid.uuid4, editable=False, verbose_name='code')), | ||
('date_sent', models.DateTimeField(auto_now_add=True, verbose_name='date sent')), | ||
('date_answered', models.DateTimeField(blank=True, null=True, verbose_name='date answered')), | ||
('invited_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='invites_sent', to=settings.AUTH_USER_MODEL, verbose_name='invited by')), | ||
('invitee', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='invites_received', to=settings.AUTH_USER_MODEL, verbose_name='invitee')), | ||
('review', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='invites', to='reviews.review', verbose_name='review')), | ||
], | ||
options={ | ||
'verbose_name': 'invite', | ||
'verbose_name_plural': 'invites', | ||
}, | ||
), | ||
] |
18 changes: 18 additions & 0 deletions
18
parsifal/apps/invites/migrations/0002_alter_invite_invitee_email.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
# Generated by Django 3.2.7 on 2021-09-08 00:45 | ||
|
||
from django.db import migrations, models | ||
|
||
|
||
class Migration(migrations.Migration): | ||
|
||
dependencies = [ | ||
('invites', '0001_initial'), | ||
] | ||
|
||
operations = [ | ||
migrations.AlterField( | ||
model_name='invite', | ||
name='invitee_email', | ||
field=models.EmailField(db_index=True, max_length=254, verbose_name='invitee email'), | ||
), | ||
] |
Empty file.
Oops, something went wrong.