from postgresqleu.mailqueue.util import send_template_mail, send_simple_mail
 from models import ConferenceRegistration, BulkPayment, PendingAdditionalOrder
 from models import RegistrationWaitlistHistory, PrepaidVoucher
-from util import notify_reg_confirmed, expire_additional_options
+from util import notify_reg_confirmed, expire_additional_options, cancel_registration
 
 from datetime import datetime
 
                except ConferenceRegistration.DoesNotExist:
                        raise Exception("Could not find conference registration %s" % invoice.processorid)
 
-               if not reg.payconfirmedat:
-                       raise Exception("Registration not paid, data is out of sync!")
-
-               # Unlink this invoice from the registration, and remove the payment
-               # confirmation. This will automatically "unlock" the registration.
-               reg.invoice = None
-               reg.payconfirmedat = None
-               reg.payconfirmedby = None
-               reg.save()
-
-               # Once unlinked, remove the registration as well. If we don't
-               # do this, the user will get notifications to remember to
-               # complete their registration in the future, and that will be
-               # confusing.
-               reg.delete()
+               cancel_registration(reg)
 
        # Return the user to a page showing what happened as a result
        # of their payment. In our case, we just return the user directly
 
        )
 
 
+def cancel_registration(reg):
+       # Verify that we're only canceling a real registration
+       if not reg.payconfirmedat:
+               raise Exception("Registration not paid, data is out of sync!")
+
+       # If we sent a welcome mail, also send a goodbye mail
+       if reg.conference.sendwelcomemail:
+               send_template_mail(reg.conference.contactaddr,
+                                                  reg.email,
+                                                  "[{0}] Registration canceled".format(reg.conference),
+                                                  'confreg/mail/reg_canceled.txt',
+                                                  {
+                                                          'conference': reg.conference,
+                                                          'reg': reg,
+                                                  },
+                                                  sendername=reg.conference.conferencename,
+                                                  receivername=reg.fullname,
+               )
+
+       # Now actually delete the reg. Start by unlinking things that might be there.
+       if reg.vouchercode:
+               if PrepaidVoucher.objects.filter(user=reg).exists():
+                       v = PrepaidVoucher.objects.get(user=reg)
+                       v.user = None
+                       v.usedate = None
+                       v.save()
+               elif DiscountCode.objects.filter(registrations=reg).exists():
+                       d = DiscountCode.objects.get(registrations=reg)
+                       d.registrations.remove(reg)
+                       d.save()
+       reg.invoice = None
+       reg.payconfirmedat = None
+       reg.payconfirmedby = None
+       reg.save()
+
+       # Once unlinked, remove the registration as well. If we don't
+       # do this, the user will get notifications to remember to
+       # complete their registration in the future, and that will be
+       # confusing.
+       reg.delete()
+
+
 
 def get_invoice_autocancel(*args):
        # Each argument is expected to be an integer with number of hours,
 
 from forms import NewMultiRegForm, MultiRegInvoiceForm
 from forms import SessionSlidesUrlForm, SessionSlidesFileForm
 from util import invoicerows_for_registration, notify_reg_confirmed, InvoicerowsException
-from util import get_invoice_autocancel
+from util import get_invoice_autocancel, cancel_registration
 
 from models import get_status_string
 from regtypes import confirm_special_reg_type, validate_special_reg_type
                'signups': _get_registration_signups(conference, reg),
        }, RequestContext(request))
 
+@login_required
+@transaction.atomic
+def admin_registration_cancel(request, urlname, regid):
+       if request.user.is_superuser:
+               conference = get_object_or_404(Conference, urlname=urlname)
+       else:
+               conference = get_object_or_404(Conference, urlname=urlname, administrators=request.user)
+
+       reg = get_object_or_404(ConferenceRegistration, id=regid, conference=conference)
+
+       if request.method == 'POST' and request.POST.get('docancel') == '1':
+               name = reg.fullname
+               cancel_registration(reg)
+               return render_to_response('confreg/admin_registration_cancel_confirm.html', {
+                       'conference': conference,
+                       'name': name,
+               })
+       else:
+               return render_to_response('confreg/admin_registration_cancel.html', {
+                       'conference': conference,
+                       'reg': reg,
+               }, RequestContext(request))
 
 @login_required
 @transaction.atomic
 
     (r'^events/admin/(\w+)/regdashboard/$', postgresqleu.confreg.views.admin_registration_dashboard),
     (r'^events/admin/(\w+)/regdashboard/list/$', postgresqleu.confreg.views.admin_registration_list),
     (r'^events/admin/(\w+)/regdashboard/list/(\d+)/$', postgresqleu.confreg.views.admin_registration_single),
+    (r'^events/admin/(\w+)/regdashboard/list/(\d+)/cancel/$', postgresqleu.confreg.views.admin_registration_cancel),
     (r'^events/admin/(\w+)/waitlist/$', postgresqleu.confreg.views.admin_waitlist),
     (r'^events/admin/(\w+)/waitlist/cancel/(\d+)/$', postgresqleu.confreg.views.admin_waitlist_cancel),
     (r'^events/admin/(\w+)/wiki/$', postgresqleu.confwiki.views.admin),
 
--- /dev/null
+{%extends "confreg/confadmin_base.html" %}
+{%load date_or_string%}
+{%block extrahead%}
+<script language="javascript">
+function confirmit() {
+   return confirm('Are you absolutely sure you want to cancel this registration? There is no way to roll i tback!');
+}
+</script>
+{%endblock%}
+
+{%block title%}Cancel registration{%endblock%}
+
+{%block layoutblock%}
+<h1>Cancel registration</h1>
+<h2>{{reg.fullname}}</h2>
+{%if reg.invoice%}
+<p>
+  This registration has an invoice attached to it. If you want to do a refund of this
+  invoice (full or partial), the cancellation must currently be done through the
+  invoice system.
+</p>
+<a class="btn btn-default btn-block" href="/invoiceadmin/{{reg.invoice.pk}}/refund/">Cancel with refund</a>
+{%elif reg.bulkpayment%}
+<p>
+  This registration is part of a bulk payment or a pay by somebody else invoice.
+  If you want to do a refund of this registration, has to be done through the
+  invoice system. However, in doing so the actual registration will not be canceled,
+  so you will <i>also</i> need to manually cancel thre reservation in question.
+</p>
+<a class="btn btn-default btn-block" href="/invoiceadmin/{{reg.bulkpayment.invoice.pk}}/refund/">Refund bulk invoice</a>
+{%else%}
+<p>
+  This registration does not have an invoice or bulk payment. That means it was either
+  a no-pay registration (such as voucher) or a manually confirmed one (speaker, staff,
+  or fully manual).
+</p>
+{%endif%}
+<form method="post" action=".">{% csrf_token %}
+  <input type="hidden" name="docancel" value="1">
+  <input type="submit" class="btn btn-default btn-block" value="Cancel registration without refund"  onclick="return confirmit()">
+</form>
+
+<a class="btn btn-default btn-block" href="/events/admin/{{conference.urlname}}/regdashboard/list/{{reg.id}}/">Back to registration</a>
+
+{%endblock%}
 
--- /dev/null
+{%extends "confreg/confadmin_base.html" %}
+{%load date_or_string%}
+{%block title%}Cancel registration{%endblock%}
+
+{%block layoutblock%}
+<h1>Cancel registration</h1>
+<p>
+  Registration for {{reg.fullname}} has been cancelled.
+</p>
+<p>
+{%if conference.sendwelcomemail%}
+An email has been sent to the attendee confirming the cancelation.
+{%else%}
+Since welcome email is not enabled for this conference, no
+email was sent to the attendee about the cancelation.
+{%endif%}
+</p>
+
+<a class="btn btn-default btn-block" href="/events/admin/{{conference.urlname}}/regdashboard/list/">Back to registration list</a>
+
+{%endblock%}
 
 
 </table>
 
+{%if reg.payconfirmedat%}
+<a class="btn btn-default btn-block" href="/events/admin/{{conference.urlname}}/regdashboard/list/{{reg.id}}/cancel/">Cancel registration</a>
+{%endif%}
+
 <a class="btn btn-default btn-block" href="/events/admin/{{conference.urlname}}/regdashboard/list/">Back to list</a>
 
 {%if user.is_superuser%}
 
--- /dev/null
+Your registration for {{conference.conferencename}} has been canceled.
+
+If you did not expect this or do not know why this happened,
+please contact us ASAP by responding to this email, and we will
+investigate the situation.
+
+Your registration has now been fully canceled, so you do not need
+to do anything else to complete it. We hope to see you again at
+a future event!