From 2fcde919179bf786c3bf3678923679264e0bf6b1 Mon Sep 17 00:00:00 2001
From: "J.-S. Caux" <J.S.Caux@uva.nl>
Date: Sat, 26 Mar 2016 16:44:00 +0100
Subject: [PATCH] Introduce RegistrationInvitation facility, email message not
 yet cleaned

---
 scipost/admin.py                              |  4 +-
 scipost/forms.py                              |  5 ++
 scipost/models.py                             | 22 +++++++
 .../scipost/accept_invitation_error.html      | 14 +++++
 scipost/templates/scipost/personal_page.html  |  1 +
 .../scipost/registration_invitation_sent.html | 14 +++++
 .../scipost/registration_invitations.html     | 57 +++++++++++++++++++
 scipost/urls.py                               | 16 ++++--
 scipost/utils.py                              | 36 ++++++++++++
 scipost/views.py                              | 39 +++++++++++++
 10 files changed, 201 insertions(+), 7 deletions(-)
 create mode 100644 scipost/templates/scipost/accept_invitation_error.html
 create mode 100644 scipost/templates/scipost/registration_invitation_sent.html
 create mode 100644 scipost/templates/scipost/registration_invitations.html

diff --git a/scipost/admin.py b/scipost/admin.py
index f15a05577..29e7c8cce 100644
--- a/scipost/admin.py
+++ b/scipost/admin.py
@@ -3,7 +3,7 @@ from django.contrib import admin
 from django.contrib.auth.admin import UserAdmin
 from django.contrib.auth.models import User
 
-from scipost.models import Contributor, AuthorshipClaim#, Opinion
+from scipost.models import *
 
 class ContributorInline(admin.StackedInline):
 #class ContributorInline(admin.TabularInline):
@@ -18,6 +18,8 @@ class UserAdmin(UserAdmin):
 admin.site.unregister(User)
 admin.site.register(User, UserAdmin)
 
+admin.site.register(RegistrationInvitation)
+
 #admin.site.register(Contributor)
 
 admin.site.register(AuthorshipClaim)
diff --git a/scipost/forms.py b/scipost/forms.py
index 4fb1de02f..d97a7ae13 100644
--- a/scipost/forms.py
+++ b/scipost/forms.py
@@ -34,6 +34,11 @@ class RegistrationForm(forms.Form):
     captcha = CaptchaField(label='* I am not a robot')
 
 
+class RegistrationInvitationForm(forms.ModelForm):
+    class Meta:
+        model = RegistrationInvitation
+        fields = ['title', 'first_name', 'last_name', 'email_address', 'invitation_type']
+
 class UpdateUserDataForm(forms.ModelForm):
     class Meta:
         model = User
diff --git a/scipost/models.py b/scipost/models.py
index fefa59e3a..2343c113e 100644
--- a/scipost/models.py
+++ b/scipost/models.py
@@ -94,6 +94,28 @@ class Contributor(models.Model):
         output += '</table>'
         return output
 
+
+INVITATION_TYPE = (
+    ('F', 'Editorial Fellow'),
+    ('C', 'Contributor'),
+    )
+
+class RegistrationInvitation(models.Model):
+    """ 
+    Invitation to particular persons for registration
+    """
+    title = models.CharField(max_length=4, choices=TITLE_CHOICES)
+    first_name = models.CharField(max_length=30, default='')
+    last_name = models.CharField(max_length=30, default='')
+    email_address = models.EmailField()
+    invitation_type = models.CharField(max_length=2, choices=INVITATION_TYPE, default='C')
+    invitation_key = models.CharField(max_length=40, default='')
+    key_expires = models.DateTimeField(default=timezone.now)
+    date_sent = models.DateTimeField(default=timezone.now)
+    responded = models.BooleanField(default=False)
+
+
+
 AUTHORSHIP_CLAIM_STATUS = (
     (1, 'accepted'),
     (0, 'not yet vetted (pending)'),
diff --git a/scipost/templates/scipost/accept_invitation_error.html b/scipost/templates/scipost/accept_invitation_error.html
new file mode 100644
index 000000000..22929ee0a
--- /dev/null
+++ b/scipost/templates/scipost/accept_invitation_error.html
@@ -0,0 +1,14 @@
+{% extends 'scipost/base.html' %}
+
+{% block pagetitle %}: accept invitation: error{% endblock pagetitle %}
+
+{% block bodysup %}
+
+<section>
+  <h1>Registration Invitation: error</h1>
+
+  <p>Error message: {{ errormessage }}</p>
+
+</section>
+
+{% endblock bodysup %}
diff --git a/scipost/templates/scipost/personal_page.html b/scipost/templates/scipost/personal_page.html
index adfd1c9a8..a2ff2f57c 100644
--- a/scipost/templates/scipost/personal_page.html
+++ b/scipost/templates/scipost/personal_page.html
@@ -66,6 +66,7 @@
     <ul>
       <li><a href="{% url 'scipost:vet_registration_requests' %}">Vet Registration requests</a> ({{ nr_reg_to_vet }})</li>
       <li>Awaiting validation ({{ nr_reg_awaiting_validation }}) (no action necessary)</li>
+      <li><a href="{% url 'scipost:registration_invitations' %}">Manage Registration invitations</a></li>
     </ul>
   </div>
   {% endif %}
diff --git a/scipost/templates/scipost/registration_invitation_sent.html b/scipost/templates/scipost/registration_invitation_sent.html
new file mode 100644
index 000000000..b58b41fbd
--- /dev/null
+++ b/scipost/templates/scipost/registration_invitation_sent.html
@@ -0,0 +1,14 @@
+{% extends 'scipost/base.html' %}
+
+{% block pagetitle %}: registration invitation sent{% endblock pagetitle %}
+
+{% block bodysup %}
+
+<section>
+  <h1>Registration Invitation sent</h1>
+
+  <p>Return to the <a href="{% url 'scipost:registration_invitations' %}">registration invitations page</a>.</p>
+
+</section>
+
+{% endblock bodysup %}
diff --git a/scipost/templates/scipost/registration_invitations.html b/scipost/templates/scipost/registration_invitations.html
new file mode 100644
index 000000000..196e75e65
--- /dev/null
+++ b/scipost/templates/scipost/registration_invitations.html
@@ -0,0 +1,57 @@
+{% extends 'scipost/base.html' %}
+
+{% block pagetitle %}: registration invitations{% endblock pagetitle %}
+
+{% block bodysup %}
+
+<section>
+  <div class="flex-greybox">
+    <h1>Registration Invitations</h1>
+  </div>
+
+  <div class="flex-greybox">
+    <h2>Send a new invitation:</h2>
+    <form action="{% url 'scipost:registration_invitations' %}" method="post">
+      {% csrf_token %}
+      <table>
+	{{ reg_inv_form.as_table }}
+      </table>
+      <input type="submit" value="Submit" />
+    </form>
+  </div>
+
+  <div class="flex-greybox">
+    <h2>Invitations sent:</h2>
+
+    <h3>Editorial Fellows</h3>
+    <table>
+      <tr><td>Last name</td><td>First name</td><td>Email</td><td>Date sent</td><td>Type (Fellow, Contrib)</td><td>Responded</td></tr>
+      {% for fellow in sent_reg_inv_fellows %}
+      <tr>
+	<td>{{ fellow.last_name }}</td>
+	<td>{{ fellow.first_name }}</td>
+	<td>{{ fellow.email_address }}</td>
+	<td>{{ fellow.date_sent }} </td>
+	<td>{{ fellow.invitation_type }}</td>
+	<td>{{ fellow.responded }}</td></tr>
+      {% endfor %}
+    </table>
+
+    <h3>Normal Contributors</h3>
+    <table>
+      <tr><td>Last name</td><td>First name</td><td>Email</td><td>Date sent</td><td>Type (Fellow, Contrib)</td><td>Responded</td></tr>
+      {% for fellow in sent_reg_inv_contrib %}
+      <tr>
+	<td>{{ fellow.last_name }}</td>
+	<td>{{ fellow.first_name }}</td>
+	<td>{{ fellow.email_address }}</td>
+	<td>{{ fellow.date_sent }} </td>
+	<td>{{ fellow.invitation_type }}</td>
+	<td>{{ fellow.responded }}</td></tr>
+      {% endfor %}
+    </table>
+
+  </div>
+</section>
+
+{% endblock bodysup %}
diff --git a/scipost/urls.py b/scipost/urls.py
index 2528779b0..dc8afb052 100644
--- a/scipost/urls.py
+++ b/scipost/urls.py
@@ -29,6 +29,10 @@ urlpatterns = [
     url(r'^already_activated$', TemplateView.as_view(template_name='scipost/already_activated.html'), name='already_activated'),
     url(r'^vet_registration_requests$', views.vet_registration_requests, name='vet_registration_requests'),
     url(r'^vet_registration_request_ack/(?P<contributor_id>[0-9]+)$', views.vet_registration_request_ack, name='vet_registration_request_ack'),
+    url(r'^registration_invitations$', views.registration_invitations, name="registration_invitations"),
+    url(r'^registration_invitation_sent$', TemplateView.as_view(template_name='scipost/registration_invitation_sent.html'), name='registration_invitation_sent'),
+    url(r'^invitation/(?P<key>.+)$', views.accept_invitation, name='accept_invitation'),
+    url(r'^accept_invitation_error$', TemplateView.as_view(template_name='scipost/accept_invitation_error.html'), name='accept_invitation_error'),
 
     ## Authentication
     url(r'^login$', views.login_view, name='login'),
@@ -45,11 +49,11 @@ urlpatterns = [
 
     # Authorship claims
     url(r'^claim_authorships$', views.claim_authorships, name="claim_authorships"),
-    url(r'^claim_sub_authorship/(?P<submission_id>[0-9]+)/(?P<claim>[0-1])$', views.claim_sub_authorship, name="claim_sub_authorship"),
-    url(r'^claim_com_authorship/(?P<commentary_id>[0-9]+)/(?P<claim>[0-1])$', views.claim_com_authorship, name="claim_com_authorship"),
-    url(r'^claim_thesis_authorship/(?P<thesis_id>[0-9]+)/(?P<claim>[0-1])$', views.claim_thesis_authorship, name="claim_thesis_authorship"),
+    url(r'^claim_sub_authorship/(?P<submission_id>[0-9]+)/(?P<claim>[0-1])$', views.claim_sub_authorship, name='claim_sub_authorship'),
+    url(r'^claim_com_authorship/(?P<commentary_id>[0-9]+)/(?P<claim>[0-1])$', views.claim_com_authorship, name='claim_com_authorship'),
+    url(r'^claim_thesis_authorship/(?P<thesis_id>[0-9]+)/(?P<claim>[0-1])$', views.claim_thesis_authorship, name='claim_thesis_authorship'),
     url(r'^vet_authorship_claims$', views.vet_authorship_claims, name="vet_authorship_claims"),
-    url(r'^vet_sub_authorship_claim/(?P<submission_id>[0-9]+)/(?P<claim>[0-1])$', views.vet_sub_authorship_claim, name="vet_sub_authorship_claim"),
-    url(r'^vet_com_authorship_claim/(?P<commentary_id>[0-9]+)/(?P<claim>[0-1])$', views.vet_com_authorship_claim, name="vet_com_authorship_claim"),
-    url(r'^vet_thesis_authorship_claim/(?P<thesis_id>[0-9]+)/(?P<claim>[0-1])$', views.vet_thesis_authorship_claim, name="vet_thesis_authorship_claim"),
+    url(r'^vet_sub_authorship_claim/(?P<submission_id>[0-9]+)/(?P<claim>[0-1])$', views.vet_sub_authorship_claim, name='vet_sub_authorship_claim'),
+    url(r'^vet_com_authorship_claim/(?P<commentary_id>[0-9]+)/(?P<claim>[0-1])$', views.vet_com_authorship_claim, name='vet_com_authorship_claim'),
+    url(r'^vet_thesis_authorship_claim/(?P<thesis_id>[0-9]+)/(?P<claim>[0-1])$', views.vet_thesis_authorship_claim, name='vet_thesis_authorship_claim'),
 ]
diff --git a/scipost/utils.py b/scipost/utils.py
index 20cf57c1e..40eeaaa38 100644
--- a/scipost/utils.py
+++ b/scipost/utils.py
@@ -88,4 +88,40 @@ class Utils(object):
         emailmessage.send(fail_silently=False)
             
 
+
+    @classmethod
+    def create_and_save_invitation(cls):
+        invitation = RegistrationInvitation (
+            title = cls.reg_inv_form.cleaned_data['title'],
+            first_name = cls.reg_inv_form.cleaned_data['first_name'],
+            last_name = cls.reg_inv_form.cleaned_data['last_name'],
+            email_address = cls.reg_inv_form.cleaned_data['email_address'],
+            invitation_type = cls.reg_inv_form.cleaned_data['invitation_type'],
+            )
+        Utils.load({'invitation': invitation})
+
+    @classmethod
+    def send_registration_invitation_email(cls):
+        # Generate email activation key and link
+        salt = ""
+        for i in range(5):
+            salt = salt + random.choice(string.ascii_letters)
+        salt = salt.encode('utf8')
+        invitationsalt = cls.invitation.last_name
+        invitationsalt = invitationsalt.encode('utf8')
+        cls.invitation.invitation_key = hashlib.sha1(salt+invitationsalt).hexdigest()
+        cls.invitation.key_expires = datetime.datetime.strftime(
+            datetime.datetime.now() + datetime.timedelta(days=14), "%Y-%m-%d %H:%M:%S")
+        cls.invitation.save()
+        email_text = ('Dear ' + title_dict[cls.invitation.title] + ' ' +
+                      cls.invitation.last_name +
+                      ', \n\nYou are invited to register to the SciPost publication portal.' +
+                      ' You can do this by visiting ' +
+                      'this link within the next 2 weeks: \n\n' + 'https://scipost.org/invitation/' +
+                      cls.invitation.invitation_key +
+                      '\n\nYour registration will thereafter be vetted. Many thanks for your interest.  \n\nThe SciPost Team.')
+        emailmessage = EmailMessage(
+            'SciPost registration invitation', email_text, 'jscaux@scipost.org',
+            [cls.invitation.email_address, 'registration@scipost.org'], reply_to=['registration@scipost.org'])
+        emailmessage.send(fail_silently=False)
     
diff --git a/scipost/views.py b/scipost/views.py
index de4b8482e..ad1f667a3 100644
--- a/scipost/views.py
+++ b/scipost/views.py
@@ -179,6 +179,45 @@ def vet_registration_request_ack(request, contributor_id):
     return render(request, 'scipost/vet_registration_request_ack.html', context)
 
 
+def registration_invitations(request):
+    # List invitations sent; send new ones
+    if request.method == 'POST':
+        # Send invitation from form information
+        reg_inv_form = RegistrationInvitationForm(request.POST)
+        Utils.load({'reg_inv_form': reg_inv_form})
+        if reg_inv_form.is_valid():
+            Utils.create_and_save_invitation()
+            Utils.send_registration_invitation_email()
+        return HttpResponseRedirect('registration_invitation_sent')
+    else:
+        reg_inv_form = RegistrationInvitationForm()
+    sent_reg_inv_fellows = RegistrationInvitation.objects.filter(invitation_type='F').order_by('last_name')
+    sent_reg_inv_contrib = RegistrationInvitation.objects.filter(invitation_type='C').order_by('last_name')
+    context = {'reg_inv_form': reg_inv_form,
+               'sent_reg_inv_fellows': sent_reg_inv_fellows,
+               'sent_reg_inv_contrib': sent_reg_inv_contrib}
+    return render(request, 'scipost/registration_invitations.html', context)
+
+
+def accept_invitation(request, key):
+    invitation = get_object_or_404(RegistrationInvitation, invitation_key=key)
+    if timezone.now() > invitation.key_expires:
+        invitation_expired = True
+        errormessage = 'The invitation key has expired.'
+    elif invitation.responded:
+        errormessage = 'This invitation token has already been used.'
+    else:
+        form = RegistrationForm()
+        form.fields['last_name'].initial = invitation.last_name
+        form.fields['first_name'].initial = invitation.first_name
+        form.fields['email'].initial = invitation.email_address
+        errormessage = ''
+        return render(request, 'scipost/register.html', {'form': form, 'errormessage': errormessage})
+
+    context = {'errormessage': errormessage}
+    return render(request, 'scipost/invitation_error.html', context)
+
+
 def login_view(request):
     if request.method == 'POST':
         username = request.POST['username']
-- 
GitLab