Allow editing certain fields on a registration
authorMagnus Hagander <magnus@hagander.net>
Mon, 11 Jun 2018 14:34:37 +0000 (16:34 +0200)
committerMagnus Hagander <magnus@hagander.net>
Mon, 11 Jun 2018 14:38:08 +0000 (16:38 +0200)
We don't allow editing the "main" fields on a registration, but allow
things like t-shirt size, dietary needs etc. In particular useful if the
registration is made by somebody else, this allows the actual attendee
to update such fields.

Add a checkbox at the conference level to stop this -- intended to turn
it off once badges are printed for example.

postgresqleu/confreg/backendforms.py
postgresqleu/confreg/forms.py
postgresqleu/confreg/migrations/0025_edit_registrations.py [new file with mode: 0644]
postgresqleu/confreg/models.py
postgresqleu/confreg/views.py
postgresqleu/urls.py
template.jinja/confreg/regform.html
template.jinja/confreg/registration_dashboard.html
template.jinja/confreg/regmulti_form.html

index ec599a1ef1cb756bc0400ac7dacdbe3ea978afd0..dba2734827a1c3b24293859f68dd0b46cf70b718 100644 (file)
@@ -107,7 +107,7 @@ class BackendConferenceForm(BackendForm):
        class Meta:
                model = Conference
                fields = ['active', 'callforpapersopen', 'callforsponsorsopen', 'feedbackopen',
-                                 'conferencefeedbackopen', 'scheduleactive', 'sessionsactive',
+                                 'conferencefeedbackopen', 'scheduleactive', 'sessionsactive', 'allowedit',
                                  'schedulewidth', 'pixelsperminute',
                                  'testers', 'talkvoters', 'staff', 'volunteers',
                                  'asktshirt', 'askfood', 'asknick', 'asktwitter', 'askshareemail', 'askphotoconsent',
index 8b6d93bb147188f4935eec36651f80aa4072b0e6..8f7c593feab618ae9aaeb1cfe6d3ffcd0e1e67a1 100644 (file)
@@ -304,6 +304,21 @@ class ConferenceRegistrationForm(forms.ModelForm):
                                'fields': [self['vouchercode'],],
                                }
 
+class RegistrationChangeForm(forms.ModelForm):
+       def __init__(self, *args, **kwargs):
+               super(RegistrationChangeForm, self).__init__(*args, **kwargs)
+               self.fields['photoconsent'].required = True
+               for f in self.instance.conference.remove_fields:
+                       del self.fields[f]
+
+       class Meta:
+               model = ConferenceRegistration
+               fields = ('shirtsize', 'dietary', 'twittername', 'nick', 'shareemail', 'photoconsent', )
+               widgets = {
+                       'photoconsent': forms.Select(choices=((None, ''), (True, 'I consent to having my photo taken'), (False, "I don't want my photo taken"))),
+               }
+
+
 rating_choices = (
     (1, '1 (Worst)'),
     (2, '2'),
diff --git a/postgresqleu/confreg/migrations/0025_edit_registrations.py b/postgresqleu/confreg/migrations/0025_edit_registrations.py
new file mode 100644 (file)
index 0000000..c4fca7f
--- /dev/null
@@ -0,0 +1,19 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('confreg', '0024_photoconsent'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='conference',
+            name='allowedit',
+            field=models.BooleanField(default=True, verbose_name=b'Allow editing registrations'),
+        ),
+    ]
index dde0db9f6c8137d5b316189acb6eac2f449a7841..c0be39711a7987b510eebb6857d0632926a6e4b1 100644 (file)
@@ -103,6 +103,7 @@ class Conference(models.Model):
        callforsponsorsopen = models.BooleanField(blank=False,null=False,default=False, verbose_name="Call for sponsors open")
        feedbackopen = models.BooleanField(blank=False,null=False,default=False, verbose_name="Session feedback open")
        conferencefeedbackopen = models.BooleanField(blank=False,null=False,default=False, verbose_name="Conference feedback open")
+       allowedit = models.BooleanField(blank=False,null=False,default=True, verbose_name="Allow editing registrations")
        scheduleactive = models.BooleanField(blank=False,null=False,default=False,verbose_name="Schedule publishing active")
        sessionsactive = models.BooleanField(blank=False,null=False,default=False,verbose_name="Session list publishing active")
        schedulewidth = models.IntegerField(blank=False, default=600, null=False, verbose_name="Width of HTML schedule")
@@ -509,6 +510,12 @@ class ConferenceRegistration(models.Model):
 
                return "Payment details not available"
 
+       def get_field_string(self, field):
+               r = getattr(self, field)
+               if isinstance(r, bool):
+                       return r and 'Yes' or 'No'
+               return getattr(self, field)
+
        # For the admin interface (mainly)
        def __unicode__(self):
                return "%s: %s %s <%s>" % (self.conference, self.firstname, self.lastname, self.email)
index fcc9c1b40860b68220378a2016459aecb13342f0..8f95f5180e1e55966c7a04b98f55f8dca0c7393d 100644 (file)
@@ -25,7 +25,7 @@ from models import AttendeeMail, ConferenceAdditionalOption
 from models import PendingAdditionalOrder
 from models import RegistrationWaitlistEntry, RegistrationWaitlistHistory
 from models import STATUS_CHOICES
-from forms import ConferenceRegistrationForm, ConferenceSessionFeedbackForm
+from forms import ConferenceRegistrationForm, RegistrationChangeForm, ConferenceSessionFeedbackForm
 from forms import ConferenceFeedbackForm, SpeakerProfileForm
 from forms import CallForPapersForm, CallForPapersSpeakerForm, CallForPapersSubmissionForm
 from forms import CallForPapersCopyForm, PrepaidCreateForm, BulkRegistrationForm
@@ -127,6 +127,24 @@ def _registration_dashboard(request, conference, reg, has_other_multiregs, redir
        for pao in PendingAdditionalOrder.objects.filter(reg=reg, invoice__isnull=False):
                invoices.append(('Additional options invoice and receipt', InvoicePresentationWrapper(pao.invoice, '.')))
 
+       if conference.allowedit:
+               # Form for changeable fields
+               if request.method == 'POST':
+                       changeform = RegistrationChangeForm(instance=reg, data=request.POST)
+                       if changeform.is_valid():
+                               changeform.save()
+                               messages.info(request, "Registration updated.")
+                               return HttpResponseRedirect("../")
+               else:
+                       changeform = RegistrationChangeForm(instance=reg)
+       else:
+               changeform = None
+
+       fields = ['shirtsize', 'dietary', 'nick', 'twittername', 'shareemail', 'photoconsent']
+       for f in conference.remove_fields:
+               fields.remove(f)
+       displayfields = [(reg._meta.get_field(k).verbose_name.capitalize(), reg.get_field_string(k)) for k in fields]
+
        return render_conference_response(request, conference, 'reg', 'confreg/registration_dashboard.html', {
                'redir_root': redir_root,
                'reg': reg,
@@ -139,6 +157,8 @@ def _registration_dashboard(request, conference, reg, has_other_multiregs, redir
                'pendingadditional': pendingadditional,
                'pendingadditionalinvoice': pendingadditionalinvoice,
                'invoices': invoices,
+               'changeform': changeform,
+               'displayfields': displayfields,
        })
 
 def confhome(request, confname):
@@ -286,6 +306,18 @@ def home(request, confname, whatfor=None):
                'costamount': reg.regtype and reg.regtype.cost or 0,
        })
 
+@login_required
+@transaction.atomic
+def changereg(request, confname):
+       # Change certain allowed fields on a registration.
+       conference = get_object_or_404(Conference, urlname=confname)
+       reg = get_object_or_404(ConferenceRegistration, conference=conference, attendee=request.user)
+
+       if not conference.allowedit:
+               return HttpResponseRedirect('../')
+
+       return _registration_dashboard(request, conference, reg, False, '../')
+
 @login_required
 @transaction.atomic
 def multireg(request, confname, regid=None):
index ab3d44ac0471fc67be3842b194a9f8a630fae3be..f755691da37b6b40af0017655fbe53fe90b45492 100644 (file)
@@ -56,6 +56,7 @@ urlpatterns = [
        url(r'^events/(?P<confname>[^/]+)/register/other/newinvoice/$', postgresqleu.confreg.views.multireg_newinvoice),
        url(r'^events/(?P<confname>[^/]+)/register/other/b(?P<bulkid>(\d+))/$', postgresqleu.confreg.views.multireg_bulkview),
        url(r'^events/(?P<confname>[^/]+)/register/other/z/$', postgresqleu.confreg.views.multireg_zeropay),
+       url(r'^events/(?P<confname>[^/]+)/register/change/$', postgresqleu.confreg.views.changereg),
        url(r'^events/register/attach/([a-z0-9]{64})/$', postgresqleu.confreg.views.multireg_attach),
        url(r'^events/([^/]+)/bulkpay/$', postgresqleu.confreg.views.bulkpay),
        url(r'^events/([^/]+)/bulkpay/(\d+)/$', postgresqleu.confreg.views.bulkpay_view),
index 3da8736a948a11f49a8c1f197d4a30b08b0a98ec..9006a138ad770c0856114e3f855a4190b08053ee 100644 (file)
@@ -12,7 +12,7 @@ div.regwrap {
    overflow: hidden;
 }
 div.regleft {
-   width: 350px;
+   width: 450px;
    float: left;
 }
 div.regright {
index a85f322f102bad17adc519d7e26c6beb699a7596..0477504113016dcd31d121f953a5638525056c04 100644 (file)
@@ -8,6 +8,35 @@ div#dash_pagelinks_wrap {
 ul.dash_pagelinks li {
    display: inline;
 }
+
+div.regwrap {
+   width: 100%;
+   overflow: hidden;
+}
+div.regleft {
+   width: 450px;
+   float: left;
+}
+div.regright {
+   width: 200px;
+   float: right;
+}
+div.regright ul {
+   padding: 0px;
+}
+div.regright ul li {
+   margin-top: 0px;
+}
+div.regwrap.errfld {
+   background-color: rgb(255,255,204);
+   border: 1px solid red;
+}
+div.regwrap.errfld ul.errorlist li {
+   font-weight: normal !important;
+}
+form input[type=submit] {
+   width: 200px !important;
+}
 </style>
 {%endblock%}
 {%block content%}
@@ -118,6 +147,17 @@ spamfilters.
 This registration type gives you access to the conference on {{reg.regtype.available_days}}.
 {%endif%}
  </dd>
+ <dt>Registration details</dt>
+ <dd>
+   <p>
+     This registration is made for <i>{{reg.firstname}} {{reg.lastname}}</i> with email address <i>{{reg.email}}</i>.
+   </p>
+   <ul>
+{%for k,v in displayfields%}
+    <li>{{k}}: {{v}}</li>
+{%endfor%}
+   </ul>
+ </dd>
 
 {%if reg.additionaloptions.all()%}
  <dt>Additional options</dt>
@@ -144,6 +184,28 @@ invoices and receipts in the <a href="/invoices/">invoice section</a>.
 
 </dl>
 
+{%if changeform%}
+<h2>Change your registration</h2>
+<p>
+A limited number of fields on your registration can be changed. If you need
+to change anything other than this, please contact the conference organizers.
+</p>
+{%if changeform.non_field_errors()%}{{changeform.non_field_errors()}}{%endif%}
+<form method="post" action="{{redir_root}}change/">{{ csrf_input }}
+  {%for f in changeform %}
+  <div class="regwrap{%if f.errors%} errfld{%endif%}">
+    <div class="regleft">
+      {{f.label_tag()}} <br/>
+      {{f}}
+    </div>
+    <div class="regright">
+      {% if f.errors %}{{ f.errors}}{% endif %}
+    </div>
+  </div>
+  {%endfor%}
+<input type="submit" value="Update registration">
+</form>
+{%endif%}
 
 {%if availableoptions%}
 <a name="options"></a>
index e206983d43859415dc3171482744cd3b241449f2..889ca6ec76587ab16cb73ed8f89df26cbe4fe3f4 100644 (file)
@@ -7,7 +7,7 @@ div.regwrap {
    overflow: hidden;
 }
 div.regleft {
-   width: 350px;
+   width: 450px;
    float: left;
 }
 div.regright {