From ea9e79d0a814d3210222f0f62e4225435da233a2 Mon Sep 17 00:00:00 2001
From: "J.-S. Caux" <J.S.Caux@uva.nl>
Date: Tue, 20 Sep 2016 08:30:36 +0200
Subject: [PATCH] html version of contrib invitation email

---
 scipost/forms.py                              |   8 +-
 scipost/models.py                             |   6 +-
 .../scipost/registration_invitations.html     |  54 ++++-
 scipost/urls.py                               |  71 +++++--
 scipost/utils.py                              | 193 ++++++++++++++----
 scipost/views.py                              |  37 +++-
 6 files changed, 290 insertions(+), 79 deletions(-)

diff --git a/scipost/forms.py b/scipost/forms.py
index 0eb7cdc73..6e86d1d46 100644
--- a/scipost/forms.py
+++ b/scipost/forms.py
@@ -12,6 +12,7 @@ from crispy_forms.layout import Layout, Div, Field, Fieldset, HTML, Submit
 
 from .models import *
 
+from journals.models import Publication
 from submissions.models import SUBMISSION_STATUS_PUBLICLY_UNLISTED
 from submissions.models import Submission
 
@@ -56,7 +57,9 @@ class RegistrationInvitationForm(forms.ModelForm):
     class Meta:
         model = RegistrationInvitation
         fields = ['title', 'first_name', 'last_name', 'email', 
-                  'invitation_type', 'cited_in_submission', 'message_style', 'personal_message']
+                  'invitation_type', 
+                  'cited_in_submission', 'cited_in_publication', 
+                  'message_style', 'personal_message']
 
     def __init__(self, *args, **kwargs):
         super(RegistrationInvitationForm, self).__init__(*args, **kwargs)
@@ -65,6 +68,8 @@ class RegistrationInvitationForm(forms.ModelForm):
         self.fields['cited_in_submission'] = forms.ModelChoiceField(
             queryset=Submission.objects.all().exclude(
                 status__in=SUBMISSION_STATUS_PUBLICLY_UNLISTED).order_by('-submission_date'))
+        self.fields['cited_in_publication'] = forms.ModelChoiceField(
+            queryset=Publication.objects.all().order_by('-publication_date'))
         self.helper = FormHelper()
         self.helper.layout = Layout(
             Div(
@@ -79,6 +84,7 @@ class RegistrationInvitationForm(forms.ModelForm):
                     css_class="col-6"),
                 css_class="row"),
             Div(Field('cited_in_submission'),),
+            Div(Field('cited_in_publication'),),
             )
 
 class UpdateUserDataForm(forms.ModelForm):
diff --git a/scipost/models.py b/scipost/models.py
index ea94d2156..28caa4746 100644
--- a/scipost/models.py
+++ b/scipost/models.py
@@ -318,7 +318,8 @@ INVITATION_TYPE = (
     ('F', 'Editorial Fellow'),
     ('C', 'Contributor'),
     ('R', 'Refereeing'),
-    ('ci', 'cited'),
+    ('ci', 'cited in submission'),
+    ('cp', 'cited in publication'),
     )
 
 INVITATION_STYLE = (
@@ -336,12 +337,15 @@ class RegistrationInvitation(models.Model):
     email = models.EmailField()
     invitation_type = models.CharField(max_length=2, choices=INVITATION_TYPE, default='C')
     cited_in_submission = models.ForeignKey('submissions.Submission', blank=True, null=True)
+    cited_in_publication = models.ForeignKey('journals.Publication', blank=True, null=True)
     message_style = models.CharField(max_length=1, choices=INVITATION_STYLE, default='F')
     personal_message = models.TextField(blank=True, null=True)
     invitation_key = models.CharField(max_length=40, default='')
     key_expires = models.DateTimeField(default=timezone.now)
     date_sent = models.DateTimeField(default=timezone.now)
     invited_by = models.ForeignKey(Contributor, blank=True, null=True)
+    nr_reminders = models.PositiveSmallIntegerField(default=0)
+    date_last_reminded = models.DateTimeField(blank=True, null=True)
     responded = models.BooleanField(default=False)
 
     def __str__ (self):
diff --git a/scipost/templates/scipost/registration_invitations.html b/scipost/templates/scipost/registration_invitations.html
index 5197150b3..c1c3509dc 100644
--- a/scipost/templates/scipost/registration_invitations.html
+++ b/scipost/templates/scipost/registration_invitations.html
@@ -9,14 +9,21 @@
   $(document).ready(function(){
 
   $("#div_id_cited_in_submission").hide();
+  $("#div_id_cited_in_publication").hide();
 
   $('select#id_invitation_type').on('change', function() {
     switch ($('select#id_invitation_type').val()) {
       case "ci":
         $("#div_id_cited_in_submission").show();
+        $("#div_id_cited_in_publication").hide();
+        break;
+      case "cp":
+        $("#div_id_cited_in_submission").hide();
+        $("#div_id_cited_in_publication").show();
         break;
       default:
         $("#div_id_cited_in_submission").hide();
+        $("#div_id_cited_in_publication").hide();
     }
    });
   });
@@ -44,7 +51,7 @@
 <hr class="hr12"/>
 <section>
   <div class="flex-greybox">
-    <h2>Invitations sent (response pending) &nbsp;&nbsp; EF ({{ nr_sent_reg_inv_fellows }}), C ({{ nr_sent_reg_inv_contrib }}), R ({{ nr_sent_reg_inv_ref }}), cited ({{ nr_sent_reg_inv_cited }}):</h2>
+    <h2>Invitations sent (response pending) &nbsp;&nbsp; EF ({{ nr_sent_reg_inv_fellows }}), C ({{ nr_sent_reg_inv_contrib }}), R ({{ nr_sent_reg_inv_ref }}), cited in sub ({{ nr_sent_reg_inv_cited_sub }}), cited in pub ({{ nr_sent_reg_inv_cited_pub }}):</h2>
   </div>
   <hr class="hr6"/>  
   <h3>Editorial Fellows ({{ nr_sent_reg_inv_fellows }})</h3>
@@ -58,6 +65,7 @@
       <td>{{ fellow.date_sent }} </td>
       <td>{{ fellow.invitation_type }}</td>
       <td>{{ fellow.invited_by.user.last_name }}</td>
+      <td><a href="{% url 'scipost:renew_registration_invitation' invitation_id=fellow.id %}">Renew</a> ({{ fellow.nr_reminders }}) {% if fellow.date_last_reminded %}(last: {{ fellow.date_last_reminded|date:"Y-m-d" }}){% endif %}</td>
     </tr>
     {% endfor %}
   </table>
@@ -73,6 +81,7 @@
       <td>{{ fellow.date_sent }} </td>
       <td>{{ fellow.invitation_type }}</td>
       <td>{{ fellow.invited_by.user.last_name }}</td>
+      <td><a href="{% url 'scipost:renew_registration_invitation' invitation_id=fellow.id %}">Renew</a> ({{ fellow.nr_reminders }}) {% if fellow.date_last_reminded %}(last: {{ fellow.date_last_reminded|date:"Y-m-d" }}){% endif %}</td>
     </tr>
     {% endfor %}
   </table>
@@ -92,10 +101,26 @@
     {% endfor %}
   </table>
   <hr class="hr6"/>  
-  <h3>Cited ({{ nr_sent_reg_inv_cited }})</h3>
+  <h3>Cited in sub ({{ nr_sent_reg_inv_cited_sub }})</h3>
+  <table class="tableofInvitees">
+    <tr><td>Last name</td><td>First name</td><td>Email</td><td>Date sent</td><td>Type</td><td>Invited by</td></tr>
+    {% for fellow in sent_reg_inv_cited_sub %}
+    <tr>
+      <td>{{ fellow.last_name }}</td>
+      <td>{{ fellow.first_name }}</td>
+      <td>{{ fellow.email }}</td>
+      <td>{{ fellow.date_sent }} </td>
+      <td>{{ fellow.invitation_type }}</td>
+      <td>{{ fellow.invited_by.user.last_name }}</td>
+      <td><a href="{% url 'scipost:renew_registration_invitation' invitation_id=fellow.id %}">Renew</a> ({{ fellow.nr_reminders }}) {% if fellow.date_last_reminded %}(last: {{ fellow.date_last_reminded|date:"Y-m-d" }}){% endif %}</td>
+    </tr>
+    {% endfor %}
+  </table>
+  <hr class="hr6"/>  
+  <h3>Cited in pub ({{ nr_sent_reg_inv_cited_pub }})</h3>
   <table class="tableofInvitees">
     <tr><td>Last name</td><td>First name</td><td>Email</td><td>Date sent</td><td>Type</td><td>Invited by</td></tr>
-    {% for fellow in sent_reg_inv_cited %}
+    {% for fellow in sent_reg_inv_cited_pub %}
     <tr>
       <td>{{ fellow.last_name }}</td>
       <td>{{ fellow.first_name }}</td>
@@ -103,6 +128,7 @@
       <td>{{ fellow.date_sent }} </td>
       <td>{{ fellow.invitation_type }}</td>
       <td>{{ fellow.invited_by.user.last_name }}</td>
+      <td><a href="{% url 'scipost:renew_registration_invitation' invitation_id=fellow.id %}">Renew</a> ({{ fellow.nr_reminders }}) {% if fellow.date_last_reminded %}(last: {{ fellow.date_last_reminded|date:"Y-m-d" }}){% endif %}</td>
     </tr>
     {% endfor %}
   </table>
@@ -110,7 +136,7 @@
   <hr class="hr12"/>
 <section>
   <div class="flex-greybox">
-    <h2>Invitations sent (responded) &nbsp;&nbsp; EF ({{ nr_resp_reg_inv_fellows }}), C ({{ nr_resp_reg_inv_contrib }}), R ({{ nr_resp_reg_inv_ref }}), cited({{ nr_resp_reg_inv_cited }}):</h2>
+    <h2>Invitations sent (responded) &nbsp;&nbsp; EF ({{ nr_resp_reg_inv_fellows }}), C ({{ nr_resp_reg_inv_contrib }}), R ({{ nr_resp_reg_inv_ref }}), cited in sub ({{ nr_resp_reg_inv_cited_sub }}), cited in pub ({{ nr_resp_reg_inv_cited_pub }}):</h2>
   </div>
   <hr class="hr6"/>  
   <h3>Editorial Fellows ({{ nr_resp_reg_inv_fellows }})</h3>
@@ -160,10 +186,26 @@
   </table>
 
   <hr class="hr6"/>
-  <h3>Cited ({{ nr_resp_reg_inv_cited }})</h3>
+  <h3>Cited in sub ({{ nr_resp_reg_inv_cited_sub }})</h3>
+  <table class="tableofInviteesResponded">
+    <tr><td>Last name</td><td>First name</td><td>Email</td><td>Date sent</td><td>Type</td><td>Invited by</td></tr>
+    {% for fellow in resp_reg_inv_cited_sub %}
+    <tr>
+      <td>{{ fellow.last_name }}</td>
+      <td>{{ fellow.first_name }}</td>
+      <td>{{ fellow.email }}</td>
+      <td>{{ fellow.date_sent }} </td>
+      <td>{{ fellow.invitation_type }}</td>
+      <td>{{ fellow.invited_by.user.last_name }}</td>
+    </tr>
+    {% endfor %}
+  </table>
+
+  <hr class="hr6"/>
+  <h3>Cited in pub ({{ nr_resp_reg_inv_cited_pub }})</h3>
   <table class="tableofInviteesResponded">
     <tr><td>Last name</td><td>First name</td><td>Email</td><td>Date sent</td><td>Type</td><td>Invited by</td></tr>
-    {% for fellow in resp_reg_inv_cited %}
+    {% for fellow in resp_reg_inv_cited_pub %}
     <tr>
       <td>{{ fellow.last_name }}</td>
       <td>{{ fellow.first_name }}</td>
diff --git a/scipost/urls.py b/scipost/urls.py
index cabfd887a..f0f88c1c3 100644
--- a/scipost/urls.py
+++ b/scipost/urls.py
@@ -20,11 +20,15 @@ urlpatterns = [
     url(r'^news$', views.news, name='news'),
     url(r'^about$', TemplateView.as_view(template_name='scipost/about.html'), name='about'),
     url(r'^call$', TemplateView.as_view(template_name='scipost/call.html'), name='call'),
-    url(r'^foundation$', TemplateView.as_view(template_name='scipost/foundation.html'), name='foundation'),
+    url(r'^foundation$', TemplateView.as_view(template_name='scipost/foundation.html'), 
+        name='foundation'),
     url(r'^tour$', TemplateView.as_view(template_name='scipost/quick_tour.html'), name='quick_tour'),
     url(r'^FAQ$', TemplateView.as_view(template_name='scipost/FAQ.html'), name='FAQ'),
-    url(r'^terms_and_conditions$', TemplateView.as_view(template_name='scipost/terms_and_conditions.html'), name='terms_and_conditions'),
-    url(r'^privacy_policy$', TemplateView.as_view(template_name='scipost/privacy_policy.html'), name='privacy_policy'),
+    url(r'^terms_and_conditions$', 
+        TemplateView.as_view(template_name='scipost/terms_and_conditions.html'), 
+        name='terms_and_conditions'),
+    url(r'^privacy_policy$', TemplateView.as_view(template_name='scipost/privacy_policy.html'), 
+        name='privacy_policy'),
 
     # Feeds
     url(r'^feeds$', TemplateView.as_view(template_name='scipost/feeds.html'), name='feeds'),
@@ -42,24 +46,40 @@ urlpatterns = [
 
     ## Registration
     url(r'^register$', views.register, name='register'),
-    url(r'^thanks_for_registering$', TemplateView.as_view(template_name='scipost/thanks_for_registering.html'), name='thanks_for_registering'),
+    url(r'^thanks_for_registering$', 
+        TemplateView.as_view(template_name='scipost/thanks_for_registering.html'), 
+        name='thanks_for_registering'),
     url(r'^activation/(?P<key>.+)$', views.activation, name='activation'),
-    url(r'^request_new_activation_link/(?P<oldkey>.+)$', views.request_new_activation_link, name='request_new_activation_link'),
-    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'^request_new_activation_link/(?P<oldkey>.+)$', 
+        views.request_new_activation_link, 
+        name='request_new_activation_link'),
+    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'^renew_registration_invitation/(?P<invitation_id>[0-9]+)$', 
+        views.renew_registration_invitation, name="renew_registration_invitation"),
+    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'^invitation/(?P<key>.+)$', views.invitation, name='invitation'),
-    url(r'^accept_invitation_error$', TemplateView.as_view(template_name='scipost/accept_invitation_error.html'), name='accept_invitation_error'),
+    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'),
     url(r'^logout$', views.logout_view, name='logout'),
     url(r'^personal_page$', views.personal_page, name='personal_page'),
     url(r'^change_password$', views.change_password, name='change_password'),
-    url(r'^reset_password_confirm/(?P<uidb64>[0-9A-Za-z]+)-(?P<token>.+)/$', views.reset_password_confirm, name='reset_password_confirm'),
+    url(r'^reset_password_confirm/(?P<uidb64>[0-9A-Za-z]+)-(?P<token>.+)/$', 
+        views.reset_password_confirm, name='reset_password_confirm'),
     url(r'^reset_password/$', views.reset_password, name='reset_password'),
     url(r'^update_personal_data$', views.update_personal_data, name='update_personal_data'),
 
@@ -71,11 +91,15 @@ 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_authorship_claim/(?P<claim_id>[0-9]+)/(?P<claim>[0-1])$', views.vet_authorship_claim, name='vet_authorship_claim'),
+    url(r'^vet_authorship_claim/(?P<claim_id>[0-9]+)/(?P<claim>[0-1])$', 
+        views.vet_authorship_claim, name='vet_authorship_claim'),
 
 
     ####################
@@ -113,18 +137,23 @@ urlpatterns = [
 
     url(r'^create_list$', views.create_list, name='create_list'),
     url(r'^list/(?P<list_id>[0-9]+)$', views.list, name='list'),
-    url(r'^list_add_element/(?P<list_id>[0-9]+)/(?P<type>[SCTc])/(?P<element_id>[0-9]+)$', views.list_add_element, name='list_add_element'),
-    url(r'^list_remove_element/(?P<list_id>[0-9]+)/(?P<type>[SCTc])/(?P<element_id>[0-9]+)$', views.list_remove_element, name='list_remove_element'),
+    url(r'^list_add_element/(?P<list_id>[0-9]+)/(?P<type>[SCTc])/(?P<element_id>[0-9]+)$', 
+        views.list_add_element, name='list_add_element'),
+    url(r'^list_remove_element/(?P<list_id>[0-9]+)/(?P<type>[SCTc])/(?P<element_id>[0-9]+)$', 
+        views.list_remove_element, name='list_remove_element'),
 
     # Teams
     url(r'^create_team$', views.create_team, name='create_team'),
     url(r'^add_team_member/(?P<team_id>[0-9]+)$', views.add_team_member, name='add_team_member'),
-    url(r'^add_team_member/(?P<team_id>[0-9]+)/(?P<contributor_id>[0-9]+)$', views.add_team_member, name='add_team_member'),
+    url(r'^add_team_member/(?P<team_id>[0-9]+)/(?P<contributor_id>[0-9]+)$', 
+        views.add_team_member, name='add_team_member'),
 
     # Graphs
     url(r'^create_graph$', views.create_graph, name='create_graph'),
     url(r'^graph/(?P<graph_id>[0-9]+)$', views.graph, name='graph'),
-    url(r'^edit_graph_node/(?P<node_id>[0-9]+)$', views.edit_graph_node, name='edit_graph_node'),
-    url(r'^delete_graph_node/(?P<node_id>[0-9]+)$', views.delete_graph_node, name='delete_graph_node'),
+    url(r'^edit_graph_node/(?P<node_id>[0-9]+)$', 
+        views.edit_graph_node, name='edit_graph_node'),
+    url(r'^delete_graph_node/(?P<node_id>[0-9]+)$', 
+        views.delete_graph_node, name='delete_graph_node'),
     url(r'^api/graph/(?P<graph_id>[0-9]+)$', views.api_graph, name='api_graph'),
 ]
diff --git a/scipost/utils.py b/scipost/utils.py
index d3524aada..02d8505d7 100644
--- a/scipost/utils.py
+++ b/scipost/utils.py
@@ -4,7 +4,8 @@ import random
 import string
 
 from django.contrib.auth.models import User
-from django.core.mail import EmailMessage
+from django.core.mail import EmailMessage, EmailMultiAlternatives
+from django.template import Context, Template
 from django.utils import timezone
 
 from .models import *
@@ -143,61 +144,120 @@ class Utils(object):
         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()
+    def send_registration_invitation_email(cls, renew=False):
+        if not renew:
+            # 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=365), "%Y-%m-%d %H:%M:%S")
+        if renew:
+            cls.invitation.nr_reminders += 1
+            cls.invitation.date_last_reminded = timezone.now()
         cls.invitation.save()
         email_text = ''
+        email_text_html = ''
+        email_context = Context({})
+        if renew:
+            email_text += ('Reminder: Invitation to SciPost\n'
+                           '-------------------------------\n\n')
+            email_text_html += ('<strong>Reminder: Invitation to SciPost</strong>'
+                               '<br/><hr/><br/>')
         if cls.invitation.invitation_type == 'F':
             email_text += 'RE: Invitation to join the Editorial College of SciPost\n\n'
+            email_text_html += 'RE: Invitation to join the Editorial College of SciPost<br/><hr/><br/>'
         email_text += 'Dear '
+        email_text_html += 'Dear '
         if cls.invitation.message_style == 'F':
             email_text += title_dict[cls.invitation.title] + ' ' + cls.invitation.last_name
+            email_text_html += '{{ title }} {{ last_name }}'
+            email_context['title'] = title_dict[cls.invitation.title]
+            email_context['last_name'] = cls.invitation.last_name
         else:
             email_text += cls.invitation.first_name
+            email_text_html += '{{ first_name }}'
+            email_context['first_name'] = cls.invitation.first_name
         email_text +=  ',\n\n'
+        email_text_html += ',<br/><br/>'
         if len(cls.invitation.personal_message) > 3:
             email_text += cls.invitation.personal_message + '\n\n'
-
+            email_text_html += '{{ personal_message|linebreaks }}<br/><br/>'
         # This text to be put in C, ci invitations
-        summary_text = ('\n\nIn summary, SciPost.org is a publication portal managed by '
-                        'professional scientists, offering (among others) high-quality '
-                        'Open Access journals with innovative forms of refereeing, and a '
-                        'means of commenting on all existing literature. SciPost is established as '
-                        'a not-for-profit foundation devoted to serving the interests of the '
-                        'international scientific community.'
-                        '\n\nThe site is anchored at https://scipost.org. Many further details '
-                        'about SciPost, its principles, ideals and implementation can be found at '
-                        'https://scipost.org/about and https://scipost.org/FAQ.'
-                        '\n\nAs a professional academic, you can register at '
-                        'https://scipost.org/register, enabling you to contribute to the site\'s '
-                        'contents, for example by offering submissions, reports and comments.'
-                        '\n\nFor your convenience, I have prepared a partly pre-filled registration '
-                        'form at https://scipost.org/invitation/' + cls.invitation.invitation_key 
-                        + ' (valid for up to a year; you can thereafter still register at '
-                        'https://scipost.org/register).\n\n'
-                        'If you do develop sympathy for the initiative, besides participating in the '
-                        'online platform, I would be very grateful if you considered submitting a '
-                        'publication to one of the journals within the near future, in order to help '
-                        'establish their reputation. I\'ll also be looking forward to your reaction, '
-                        'comments and suggestions about the initiative, which I hope you will find '
-                        'useful to your work as a professional scientist.'
-                        '\n\nMany thanks in advance for taking a few minutes to look into it,'
-                        '\n\nOn behalf of the SciPost Foundation,\n\n'
-                        'Prof. dr Jean-Sébastien Caux\n---------------------------------------------'
-                        '\nInstitute for Theoretical Physics\nUniversity of Amsterdam\nScience Park 904'
-                        '\n1098 XH Amsterdam\nThe Netherlands\n'
-                        '---------------------------------------------\ntel.: +31 (0)20 5255775'
-                        '\nfax: +31 (0)20 5255778\n---------------------------------------------')
+        summary_text = (
+            '\n\nIn summary, SciPost.org is a publication portal managed by '
+            'professional scientists, offering (among others) high-quality '
+            'Open Access journals with innovative forms of refereeing, and a '
+            'means of commenting on all existing literature. SciPost is established as '
+            'a not-for-profit foundation devoted to serving the interests of the '
+            'international scientific community.'
+            '\n\nThe site is anchored at https://scipost.org. Many further details '
+            'about SciPost, its principles, ideals and implementation can be found at '
+            'https://scipost.org/about and https://scipost.org/FAQ.'
+            '\n\nAs a professional academic, you can register at '
+            'https://scipost.org/register, enabling you to contribute to the site\'s '
+            'contents, for example by offering submissions, reports and comments.'
+            '\n\nFor your convenience, I have prepared a partly pre-filled registration '
+            'form at https://scipost.org/invitation/' + cls.invitation.invitation_key 
+            + ' (valid for up to a year; you can thereafter still register at '
+            'https://scipost.org/register).\n\n'
+            'If you do develop sympathy for the initiative, besides participating in the '
+            'online platform, I would be very grateful if you considered submitting a '
+            'publication to one of the journals within the near future, in order to help '
+            'establish their reputation. I\'ll also be looking forward to your reaction, '
+            'comments and suggestions about the initiative, which I hope you will find '
+            'useful to your work as a professional scientist.'
+            '\n\nMany thanks in advance for taking a few minutes to look into it,'
+            '\n\nOn behalf of the SciPost Foundation,\n\n'
+            'Prof. dr Jean-Sébastien Caux\n---------------------------------------------'
+            '\nInstitute for Theoretical Physics\nUniversity of Amsterdam\nScience Park 904'
+            '\n1098 XH Amsterdam\nThe Netherlands\n'
+            '---------------------------------------------\ntel.: +31 (0)20 5255775'
+            '\nfax: +31 (0)20 5255778\n---------------------------------------------')
 
+        summary_text_html = (
+            '<br/><br/>In summary, SciPost.org is a publication portal managed by '
+            'professional scientists, offering (among others) high-quality '
+            'Open Access journals with innovative forms of refereeing, and a '
+            'means of commenting on all existing literature. SciPost is established as '
+            'a not-for-profit foundation devoted to serving the interests of the '
+            'international scientific community.'
+            '<br/><br/>The site is anchored at <a href="https://scipost.org">scipost.org</a>. '
+            'Many further details '
+            'about SciPost, its principles, ideals and implementation can be found at '
+            'the <a href="https://scipost.org/about">about</a> '
+            'and <a href="https://scipost.org/FAQ">FAQ</a> pages.'
+            '<br/><br/>As a professional academic, you can register at the '
+            '<a href="https://scipost.org/register">registration page</a>, '
+            'enabling you to contribute to the site\'s '
+            'contents, for example by offering submissions, reports and comments.'
+            '<br/><br/>For your convenience, I have prepared a partly pre-filled '
+            '<a href="https://scipost.org/invitation/{{ invitation_key }}>registration form</a>'
+            ' (valid for up to a year; you can thereafter still register at the '
+            '<a href="https://scipost.org/register">registration page</a>).<br/><br/>'
+            'If you do develop sympathy for the initiative, besides participating in the '
+            'online platform, I would be very grateful if you considered submitting a '
+            'publication to one of the journals within the near future, in order to help '
+            'establish their reputation. I\'ll also be looking forward to your reaction, '
+            'comments and suggestions about the initiative, which I hope you will find '
+            'useful to your work as a professional scientist.'
+            '<br/><br/>Many thanks in advance for taking a few minutes to look into it,'
+            '<br/><br/>On behalf of the SciPost Foundation,<br/><br/>'
+            'Prof. dr Jean-Sébastien Caux<br/>'
+            '---------------------------------------------'
+            '<br/>Institute for Theoretical Physics'
+            '<br/>University of Amsterdam'
+            '<br/>Science Park 904'
+            '<br/>1098 XH Amsterdam<br/>The Netherlands<br/>'
+            '---------------------------------------------'
+            '<br/>tel.: +31 (0)20 5255775'
+            '<br/>fax: +31 (0)20 5255778'
+            '<br/>---------------------------------------------')
+        email_context['invitation_key'] = cls.invitation.invitation_key
 
         if cls.invitation.invitation_type == 'R':
             # Refereeing invitation
@@ -212,6 +272,17 @@ class Utils(object):
                            'in particular by providing referee reports.\n\n'
                            'We very much hope that we can count on your expertise,\n\n'
                            'Many thanks in advance,\n\nThe SciPost Team')
+            email_text_html += (
+                'We would hereby like to cordially invite you '
+                'to become a Contributor on SciPost '
+                '(this is required in order to deliver reports; '
+                'our records show that you are not yet registered); '
+                'for your convenience, we have prepared a pre-filled '
+                '<a href="https://scipost.org/invitation/{{ invitation_key }}>registration form</a> '
+                'for you. After activation of your registration, you will be allowed to contribute, '
+                'in particular by providing referee reports.<br/><br/>'
+                'We very much hope that we can count on your expertise,<br/><br/>'
+                'Many thanks in advance,\n\nThe SciPost Team')
             email_text += ('\n\n--------------------------------------------------'
                            '\n\nAbout SciPost:\n\n'
                            'In summary, SciPost.org is a publication portal managed by '
@@ -223,6 +294,18 @@ class Utils(object):
                            '\n\nThe site is anchored at https://scipost.org. Many further details '
                            'about SciPost, its principles, ideals and implementation can be found at '
                            'https://scipost.org/about and https://scipost.org/FAQ.')
+            email_text_html += (
+                '<br/><br/>--------------------------------------------------'
+                '<br/><br/>About SciPost:<br/><br/>'
+                'In summary, SciPost.org is a publication portal managed by '
+                'professional scientists, offering (among others) high-quality '
+                'Open Access journals with innovative forms of refereeing, and a '
+                'means of commenting on all existing literature. SciPost is established as '
+                'a not-for-profit foundation devoted to serving the interests of the '
+                'international scientific community.'
+                '<br/><br/>The site is anchored at https://scipost.org. Many further details '
+                'about SciPost, its principles, ideals and implementation can be found at '
+                'https://scipost.org/about and https://scipost.org/FAQ.')
 
             emailmessage = EmailMessage(
                 'SciPost: refereeing request (and registration invitation)', email_text,
@@ -248,17 +331,45 @@ class Utils(object):
                 ['registration@scipost.org'],
                 reply_to=['registration@scipost.org'])
 
+        elif cls.invitation.invitation_type == 'cp':
+            # Has been cited in a Publication. Invite!
+            email_text += ('Your work has been cited in a paper published by SciPost,'
+                           '\n\n' + cls.invitation.cited_in_publication.title 
+                           + ' by ' + cls.invitation.cited_in_publication.author_list + 
+                           '\n\n(published as ' + cls.invitation.cited_in_publication.citation
+                           + ').\n\n'
+                           'I would hereby like to use this opportunity to quickly introduce '
+                           'you to the SciPost initiative, and to invite you to become an active '
+                           'Contributor to the site. You might for example consider reporting or '
+                           'commenting on the above submission before the refereeing deadline.')
+            email_text += summary_text
+            emailmessage = EmailMessage(
+                'SciPost: invitation', email_text,
+                'J.-S. Caux <jscaux@scipost.org>',
+                [cls.invitation.email],
+                ['registration@scipost.org'],
+                reply_to=['registration@scipost.org'])
+
         elif cls.invitation.invitation_type == 'C':
             email_text += ('I would hereby like to quickly introduce '
                            'you to a scientific publishing initiative I recently launched, '
                            'called SciPost, and to invite you to become an active Contributor.')
             email_text += summary_text
-            emailmessage = EmailMessage(
+            email_text_html += (
+                'I would hereby like to quickly introduce '
+                'you to a scientific publishing initiative I recently launched, '
+                'called SciPost, and to invite you to become an active Contributor.')
+            email_text_html += summary_text_html + '<br/><br/>' + EMAIL_FOOTER
+            html_template = Template(email_text_html)
+            html_version = html_template.render(email_context)
+            #emailmessage = EmailMessage(
+            emailmessage = EmailMultiAlternatives(
                 'SciPost: invitation', email_text,
                 'J.-S. Caux <jscaux@scipost.org>',
                 [cls.invitation.email],
                 ['registration@scipost.org'],
                 reply_to=['registration@scipost.org'])
+            emailmessage.attach_alternative(html_version, 'text/html')
 
         elif cls.invitation.invitation_type == 'F':
             email_text += ('You will have noticed that scientific publishing is currently '
diff --git a/scipost/views.py b/scipost/views.py
index 2ab275ce6..e56bf12b0 100644
--- a/scipost/views.py
+++ b/scipost/views.py
@@ -18,7 +18,7 @@ from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
 from django.core.urlresolvers import reverse
 from django.http import HttpResponse, HttpResponseRedirect, JsonResponse
 from django.shortcuts import redirect
-from django.template import RequestContext
+from django.template import Context, RequestContext, Template
 from django.utils.http import is_safe_url
 from django.views.decorators.csrf import csrf_protect
 from django.db.models import Avg
@@ -37,6 +37,7 @@ from .utils import *
 from commentaries.models import Commentary
 from commentaries.forms import CommentarySearchForm
 from comments.models import Comment
+from journals.models import Publication
 from submissions.models import SUBMISSION_STATUS_PUBLICLY_UNLISTED
 from submissions.models import Submission, EditorialAssignment, RefereeInvitation, Report, EICRecommendation
 from submissions.forms import SubmissionSearchForm
@@ -474,8 +475,10 @@ def registration_invitations(request):
     nr_sent_reg_inv_contrib = sent_reg_inv_contrib.count()
     sent_reg_inv_ref = sent_reg_inv.filter(invitation_type='R').order_by('last_name')
     nr_sent_reg_inv_ref = sent_reg_inv_ref.count()
-    sent_reg_inv_cited = sent_reg_inv.filter(invitation_type='ci').order_by('last_name')
-    nr_sent_reg_inv_cited = sent_reg_inv_cited.count()
+    sent_reg_inv_cited_sub = sent_reg_inv.filter(invitation_type='ci').order_by('last_name')
+    nr_sent_reg_inv_cited_sub = sent_reg_inv_cited_sub.count()
+    sent_reg_inv_cited_pub = sent_reg_inv.filter(invitation_type='cp').order_by('last_name')
+    nr_sent_reg_inv_cited_pub = sent_reg_inv_cited_pub.count()
 
     resp_reg_inv = RegistrationInvitation.objects.filter(responded=True)
     resp_reg_inv_fellows = resp_reg_inv.filter(invitation_type='F').order_by('last_name')
@@ -484,8 +487,10 @@ def registration_invitations(request):
     nr_resp_reg_inv_contrib = resp_reg_inv_contrib.count()
     resp_reg_inv_ref = resp_reg_inv.filter(invitation_type='R').order_by('last_name')
     nr_resp_reg_inv_ref = resp_reg_inv_ref.count()
-    resp_reg_inv_cited = resp_reg_inv.filter(invitation_type='ci').order_by('last_name')
-    nr_resp_reg_inv_cited = resp_reg_inv_cited.count()
+    resp_reg_inv_cited_sub = resp_reg_inv.filter(invitation_type='ci').order_by('last_name')
+    nr_resp_reg_inv_cited_sub = resp_reg_inv_cited_sub.count()
+    resp_reg_inv_cited_pub = resp_reg_inv.filter(invitation_type='cp').order_by('last_name')
+    nr_resp_reg_inv_cited_pub = resp_reg_inv_cited_pub.count()
 
     context = {'reg_inv_form': reg_inv_form, 'errormessage': errormessage,
                'sent_reg_inv_fellows': sent_reg_inv_fellows, 
@@ -494,19 +499,33 @@ def registration_invitations(request):
                'nr_sent_reg_inv_contrib': nr_sent_reg_inv_contrib,
                'sent_reg_inv_ref': sent_reg_inv_ref,
                'nr_sent_reg_inv_ref': nr_sent_reg_inv_ref,
-               'sent_reg_inv_cited': sent_reg_inv_cited,
-               'nr_sent_reg_inv_cited': nr_sent_reg_inv_cited,
+               'sent_reg_inv_cited_sub': sent_reg_inv_cited_sub,
+               'nr_sent_reg_inv_cited_sub': nr_sent_reg_inv_cited_sub,
+               'sent_reg_inv_cite_pub': sent_reg_inv_cited_pub,
+               'nr_sent_reg_inv_cited_pub': nr_sent_reg_inv_cited_pub,
                'resp_reg_inv_fellows': resp_reg_inv_fellows, 
                'nr_resp_reg_inv_fellows': nr_resp_reg_inv_fellows,
                'resp_reg_inv_contrib': resp_reg_inv_contrib, 
                'nr_resp_reg_inv_contrib': nr_resp_reg_inv_contrib,
                'resp_reg_inv_ref': resp_reg_inv_ref,
                'nr_resp_reg_inv_ref': nr_resp_reg_inv_ref,
-               'resp_reg_inv_cited': resp_reg_inv_cited,
-               'nr_resp_reg_inv_cited': nr_resp_reg_inv_cited, }
+               'resp_reg_inv_cited_sub': resp_reg_inv_cited_sub,
+               'nr_resp_reg_inv_cited_sub': nr_resp_reg_inv_cited_sub,
+               'resp_reg_inv_cited_pub': resp_reg_inv_cited_pub,
+               'nr_resp_reg_inv_cited_pub': nr_resp_reg_inv_cited_pub, }
     return render(request, 'scipost/registration_invitations.html', context)
 
 
+@permission_required('scipost.can_manage_registration_invitations', return_403=True)
+def renew_registration_invitation(request, invitation_id):
+    """ 
+    Renew an invitation (called from registration_invitations)
+    """
+    invitation = get_object_or_404(RegistrationInvitation, pk=invitation_id)
+    Utils.load({'invitation': invitation})
+    Utils.send_registration_invitation_email(True)
+    return redirect(reverse('scipost:registration_invitations'))
+    
 
 def login_view(request):
     redirect_to = request.POST.get('next', 
-- 
GitLab