def view_registration_badge(request, urlname, regid): conference = get_authenticated_conference(request, urlname) reg = get_object_or_404(ConferenceRegistration, conference=conference, pk=regid) resp = HttpResponse(content_type='application/pdf') render_jinja_badges(conference, [reg.safe_export(), ], resp, False, False) return resp
def registration_dashboard_send_dm(request, urlname, regid): conference = get_authenticated_conference(request, urlname) reg = get_object_or_404(ConferenceRegistration, conference=conference, pk=regid) if not reg.messaging: # Should never have the link, but just in case messages.warning(request, 'This registration has no direct messaging configured') return HttpResponseRedirect("../") maxlength = get_messaging_class(reg.messaging.provider.classname).direct_message_max_length if request.method == 'POST': form = BackendRegistrationDmForm(maxlength, data=request.POST) if form.is_valid(): send_reg_direct_message(reg, form.cleaned_data['message']) messages.info(request, "Direct message sent.") return HttpResponseRedirect("../") else: form = BackendRegistrationDmForm(maxlength) return render(request, 'confreg/admin_backend_form.html', { 'conference': conference, 'basetemplate': 'confreg/confadmin_base.html', 'form': form, 'what': 'new direct message', 'savebutton': 'Send direct message', 'cancelurl': '../', 'breadcrumbs': [('../../', 'Registration list'), ('../', reg.fullname)], })
def pendinginvoices_cancel(request, urlname, invoiceid): conference = get_authenticated_conference(request, urlname) invoice = get_object_or_404(Invoice, pk=invoiceid, paidat__isnull=True) # Have to verify that this invoice is actually for this conference if not ( ConferenceRegistration.objects.filter(conference=conference, invoice=invoice).exists() or BulkPayment.objects.filter(conference=conference, invoice=invoice).exists() or Sponsor.objects.filter(conference=conference, invoice=invoice).exists() ): raise PermissionDenied("Invoice not for this conference") if request.method == 'POST': form = ConferenceInvoiceCancelForm(data=request.POST) if form.is_valid(): manager = InvoiceManager() try: manager.cancel_invoice(invoice, form.cleaned_data['reason'], request.user.username) messages.info(request, 'Invoice {} canceled.'.format(invoice.id)) return HttpResponseRedirect('../../') except Exception as e: messages.error(request, 'Failed to cancel invoice: {}'.format(e)) else: form = ConferenceInvoiceCancelForm() return render(request, 'confreg/admin_backend_form.html', { 'conference': conference, 'basetemplate': 'confreg/confadmin_base.html', 'form': form, 'whatverb': 'Cancel invoice', 'savebutton': 'Cancel invoice', 'cancelname': 'Return without canceling', 'cancelurl': '../../', 'note': 'Canceling invoice #{} ({}) will disconnect it from the associated objects and send a notification to the recipient of the invoice ({}).'.format(invoice.id, invoice.title, invoice.recipient_name), })
def edit_messaging(request, urlname, rest): conference = get_authenticated_conference(request, urlname) # How about this for ugly :) Make sure this conference has an instance for every # available messaging on the series. with connection.cursor() as curs: curs.execute( """INSERT INTO confreg_conferencemessaging (conference_id, provider_id, broadcast, privatebcast, notification, orgnotification, config) SELECT %(confid)s, id, false, false, false, false, '{}' FROM confreg_messagingprovider mp WHERE mp.series_id=%(seriesid)s AND NOT EXISTS ( SELECT 1 FROM confreg_conferencemessaging m2 WHERE m2.conference_id=%(confid)s AND m2.provider_id=mp.id )""", { 'confid': conference.id, 'seriesid': conference.series_id, }) return backend_list_editor(request, urlname, BackendMessagingForm, rest, conference=conference, allow_new=False, allow_delete=False, )
def view_registration_ticket(request, urlname, regid): conference = get_authenticated_conference(request, urlname) reg = get_object_or_404(ConferenceRegistration, conference=conference, pk=regid) resp = HttpResponse(content_type='application/pdf') render_jinja_ticket(reg, resp, systemroot=JINJA_TEMPLATE_ROOT) return resp
def purge_personal_data(request, urlname): conference = get_authenticated_conference(request, urlname) if conference.personal_data_purged: messages.warning(request, 'Personal data for this conference has already been purged') return HttpResponseRedirect('../') if request.method == 'POST': exec_no_result("INSERT INTO confreg_aggregatedtshirtsizes (conference_id, size_id, num) SELECT conference_id, shirtsize_id, count(*) FROM confreg_conferenceregistration WHERE conference_id=%(confid)s AND shirtsize_id IS NOT NULL GROUP BY conference_id, shirtsize_id", {'confid': conference.id, }) exec_no_result("INSERT INTO confreg_aggregateddietary (conference_id, dietary, num) SELECT conference_id, lower(dietary), count(*) FROM confreg_conferenceregistration WHERE conference_id=%(confid)s AND dietary IS NOT NULL AND dietary != '' GROUP BY conference_id, lower(dietary)", {'confid': conference.id, }) exec_no_result("UPDATE confreg_conferenceregistration SET shirtsize_id=NULL, dietary='', phone='', address='' WHERE conference_id=%(confid)s", {'confid': conference.id, }) conference.personal_data_purged = timezone.now() conference.save() messages.info(request, "Personal data purged from conference") return HttpResponseRedirect('../') return render(request, 'confreg/admin_purge_personal_data.html', { 'conference': conference, 'helplink': 'personaldata', 'counts': exec_to_dict("""SELECT count(1) FILTER (WHERE shirtsize_id IS NOT NULL) AS "T-shirt size registrations", count(1) FILTER (WHERE dietary IS NOT NULL AND dietary != '') AS "Dietary needs", count(1) FILTER (WHERE phone IS NOT NULL AND phone != '') AS "Phone numbers", count(1) FILTER (WHERE address IS NOT NULL AND address != '') AS "Addresses" FROM confreg_conferenceregistration WHERE conference_id=%(confid)s""", { 'confid': conference.id, })[0], })
def feedback_report(request, confname): conference = get_authenticated_conference(request, confname) sections = [] # Get the global conference feedback. Yes, this will be inefficient, but it will work currentsection = {} for q in ConferenceFeedbackQuestion.objects.filter( conference=conference).order_by('sortkey'): if q.newfieldset: if currentsection: sections.append(currentsection) currentsection = {} if not currentsection: # Either first row, or a new fieldset per above currentsection['title'] = q.newfieldset currentsection['questions'] = [] currentsection['questions'].append(build_feedback_response(q)) else: sections.append(currentsection) return render(request, 'confreg/admin_conference_feedback.html', { 'conference': conference, 'feedback': sections, 'helplink': 'feedback', })
def tweetcampaign(request, urlname, typeid): conference = get_authenticated_conference(request, urlname) campaign = get_campaign_from_id(typeid) if request.method == 'GET' and 'fieldpreview' in request.GET: try: return campaign.get_dynamic_preview(conference, request.GET['fieldpreview'], request.GET['previewval']) except Exception as e: return HttpResponse('Exception rendering preview: {}'.format(e), content_type='text/plain', status=500) if request.method == 'POST': form = campaign.form(conference, request.POST) if form.is_valid(): try: with transaction.atomic(): form.generate_tweets(request.user) messages.info(request, "Campaign tweets generated") return HttpResponseRedirect("../../queue/") except Exception as e: form.add_error('content_template', 'Exception rendering template: {}'.format(e)) else: form = campaign.form(conference) return render(request, 'confreg/admin_backend_form.html', { 'conference': conference, 'basetemplate': 'confreg/confadmin_base.html', 'form': form, 'whatverb': 'Create campaign', 'savebutton': "Create campaign", 'cancelurl': '../../../', 'note': campaign.note, 'helplink': 'integrations#campaigns', })
def addoptorders(request, urlname): conference = get_authenticated_conference(request, urlname) return render(request, 'confreg/admin_addoptorder_list.html', { 'conference': conference, 'orders': PendingAdditionalOrder.objects.select_related('reg', 'invoice__paidusing').filter(reg__conference=conference).order_by('-payconfirmedat', '-createtime'), 'helplink': 'registrations#options', })
def prepaidorders(request, urlname): conference = get_authenticated_conference(request, urlname) return render(request, 'confreg/admin_prepaidorders_list.html', { 'conference': conference, 'orders': PurchasedVoucher.objects.select_related('sponsor', 'user', 'invoice', 'batch').filter(conference=conference).annotate(num_used=Count('batch__prepaidvoucher__user')).order_by('-invoice__paidat', '-invoice__id'), 'helplink': 'vouchers', })
def registration_dashboard_send_email(request, urlname): conference = get_authenticated_conference(request, urlname) return _attendee_email_form(request, conference, "SELECT id AS regid, attendee_id AS user_id, firstname || ' ' || lastname AS fullname, email FROM confreg_conferenceregistration WHERE conference_id=%(conference)s AND id=ANY(%(idlist)s)", [('../', 'Registration list'), ], )
def multiregs(request, urlname): conference = get_authenticated_conference(request, urlname) return render(request, 'confreg/admin_multireg_list.html', { 'conference': conference, 'bulkpays': BulkPayment.objects.select_related('user', 'invoice__paidusing').prefetch_related('conferenceregistration_set').filter(conference=conference).order_by('-paidat', '-createdat'), 'highlight': get_int_or_error(request.GET, 'b', -1), 'helplink': 'registrations', })
def prepaidorder_refund(request, urlname, orderid): conference = get_authenticated_conference(request, urlname) order = get_object_or_404(PurchasedVoucher, pk=orderid, conference=conference) if PrepaidBatch.objects.filter(pk=order.batch_id).aggregate(used=Count('prepaidvoucher__user'))['used'] > 0: # This link should not exist in the first place, but double check if someone # used the voucher in between the click. messages.error(request, 'Cannot refund order, there are used vouchers in the batch!') return HttpResponseRedirect("../../") invoice = order.invoice if not invoice: messages.error(request, 'Order does not have an invoice, there is nothing to refund!') return HttpResponseRedirect("../../") if not invoice.paidat: messages.error(request, 'Invoice for this order has not been paid, there is nothing to refund!') return HttpResponseRedirect("../../") if request.method == 'POST': form = PurchasedVoucherRefundForm(data=request.POST) if form.is_valid(): # Actually issue the refund manager = InvoiceManager() manager.refund_invoice(invoice, 'Prepaid order refunded', invoice.total_amount - invoice.total_vat, invoice.total_vat, conference.vat_registrations) send_conference_notification( conference, 'Prepaid order {} refunded'.format(order.id), 'Prepaid order {} purchased by {} {} has been refunded.\nNo vouchers were in use, and the order and batch have both been deleted.\n'.format(order.id, order.user.first_name, order.user.last_name), ) order.batch.delete() order.delete() messages.info(request, 'Order has been refunded and deleted.') return HttpResponseRedirect("../../") else: form = PurchasedVoucherRefundForm() if settings.EU_VAT: note = 'You are about to refund {}{} ({}{} + {}{} VAT) for invoice {}. Please confirm that this is what you want!'.format(settings.CURRENCY_SYMBOL, invoice.total_amount, settings.CURRENCY_SYMBOL, invoice.total_amount - invoice.total_vat, settings.CURRENCY_SYMBOL, invoice.total_vat, invoice.id) else: note = 'You are about to refund {}{} for invoice {}. Please confirm that this is what you want!'.format(settings.CURRENCY_SYMBOL, invoice.total_amount, invoice.id) return render(request, 'confreg/admin_backend_form.html', { 'conference': conference, 'basetemplate': 'confreg/confadmin_base.html', 'form': form, 'note': note, 'whatverb': 'Refund', 'what': 'repaid vouchers', 'savebutton': 'Refund', 'cancelurl': '../../', 'breadcrumbs': [('/events/admin/{}/prepaidorders/'.format(conference.urlname), 'Prepaid Voucher Orders'), ], 'helplink': 'vouchers', })
def signup_admin_edit(request, urlname, signupid): conference = get_authenticated_conference(request, urlname) if signupid != 'new': signup = get_object_or_404(Signup, conference=conference, pk=signupid) # There can be results, so calculate them. We want both a list and # a summary. results = {} cursor = connection.cursor() cursor.execute("WITH t AS (SELECT choice, count(*) AS num FROM confwiki_attendeesignup WHERE signup_id=%(signup)s GROUP BY choice) SELECT choice, num, CAST(num*100/sum(num) OVER () AS integer), CAST(num*100*4/sum(num) OVER () AS integer) FROM t ORDER BY 2 DESC", { 'signup': signup.id, }) sumresults = cursor.fetchall() results['summary'] = [dict(list(zip(['choice', 'num', 'percent', 'percentwidth'], r))) for r in sumresults] cursor.execute("SELECT s.id, firstname || ' ' || lastname,choice,saved FROM confreg_conferenceregistration r INNER JOIN confwiki_attendeesignup s ON r.id=s.attendee_id WHERE s.signup_id=%(signup)s ORDER BY saved", { 'signup': signup.id, }) results['details'] = [dict(list(zip(['id', 'name', 'choice', 'when'], r))) for r in cursor.fetchall()] if signup.optionvalues: optionstrings = signup.options.split(',') optionvalues = [int(x) for x in signup.optionvalues.split(',')] totalvalues = 0 for choice, num, percent, width in sumresults: totalvalues += num * optionvalues[optionstrings.index(choice)] results['totalvalues'] = totalvalues # If we have a limited number of attendees, then we can generate # a list of pending users. We don't even try if it's set for public. if not signup.public: cursor.execute("SELECT firstname || ' ' || lastname FROM confreg_conferenceregistration r WHERE payconfirmedat IS NOT NULL AND canceledat IS NULL AND (regtype_id IN (SELECT registrationtype_id FROM confwiki_signup_regtypes srt WHERE srt.signup_id=%(signup)s) OR id IN (SELECT conferenceregistration_id FROM confwiki_signup_attendees WHERE signup_id=%(signup)s)) AND id NOT IN (SELECT attendee_id FROM confwiki_attendeesignup WHERE signup_id=%(signup)s) ORDER BY lastname, firstname", { 'signup': signup.id, }) results['awaiting'] = [dict(list(zip(['name', ], r))) for r in cursor.fetchall()] else: author = get_object_or_404(ConferenceRegistration, conference=conference, attendee=request.user) signup = Signup(conference=conference, author=author) results = None if request.method == 'POST': form = SignupAdminEditForm(instance=signup, data=request.POST) if form.is_valid(): # We don't bother with diffs here as the only one who can # edit things are admins anyway. form.save() return HttpResponseRedirect('../') else: form = SignupAdminEditForm(instance=signup) return render(request, 'confwiki/signup_admin_edit_form.html', { 'conference': conference, 'form': form, 'signup': signup, 'results': results, 'breadcrumbs': (('/events/admin/{0}/signups/'.format(conference.urlname), 'Signups'),), 'helplink': 'signups', })
def edit_tweetqueue(request, urlname, rest): conference = get_authenticated_conference(request, urlname) return backend_list_editor(request, urlname, BackendTweetQueueForm, rest, return_url='../../', instancemaker=lambda: ConferenceTweetQueue(conference=conference, author=request.user) )
def signup_admin(request, urlname): conference = get_authenticated_conference(request, urlname) signups = Signup.objects.filter(conference=conference) return render(request, 'confwiki/signup_admin.html', { 'conference': conference, 'signups': signups, 'helplink': 'signups', })
def admin(request, urlname): conference = get_authenticated_conference(request, urlname) pages = Wikipage.objects.filter(conference=conference) return render(request, 'confwiki/admin.html', { 'conference': conference, 'pages': pages, 'helplink': 'wiki', })
def signup_admin_editsignup(request, urlname, signupid, id): conference = get_authenticated_conference(request, urlname) signup = get_object_or_404(Signup, conference=conference, pk=signupid) if id == 'new': attendeesignup = AttendeeSignup(signup=signup) else: attendeesignup = get_object_or_404(AttendeeSignup, signup=signup, pk=id) if request.method == 'POST' and request.POST['submit'] == 'Delete': attendeesignup.delete() return HttpResponseRedirect('../../') elif request.method == 'POST': form = SignupAdminEditSignupForm(signup, isnew=(id == 'new'), instance=attendeesignup, data=request.POST) if form.is_valid(): if (not signup.options) and (not form.cleaned_data['choice']): # Yes/no signup changed to no means we actually delete the # record completeliy. attendeesignup.delete() else: form.save() return HttpResponseRedirect('../../') else: form = SignupAdminEditSignupForm(signup, isnew=(id == 'new'), instance=attendeesignup) return render( request, 'confreg/admin_backend_form.html', { 'basetemplate': 'confreg/confadmin_base.html', 'conference': conference, 'form': form, 'what': 'attendee signup', 'cancelurl': '../../', 'allow_delete': (id != 'new'), 'breadcrumbs': ( ('/events/admin/{0}/signups/'.format( conference.urlname), 'Signups'), ('/events/admin/{0}/signups/{1}/'.format( conference.urlname, signup.id), signup.title), ), 'helplink': 'signups', })
def pendinginvoices(request, urlname): conference = get_authenticated_conference(request, urlname) return render(request, 'confreg/admin_pending_invoices.html', { 'conference': conference, 'invoices': OrderedDict(( ('Attendee invoices', Invoice.objects.filter(paidat__isnull=True, conferenceregistration__conference=conference)), ('Multi-registration invoices', Invoice.objects.filter(paidat__isnull=True, bulkpayment__conference=conference)), ('Sponsor invoices', Invoice.objects.filter(paidat__isnull=True, sponsor__conference=conference)), )), })
def edit_conference(request, urlname): conference = get_authenticated_conference(request, urlname) return backend_process_form(request, urlname, BackendConferenceForm, conference.pk, conference=conference, bypass_conference_filter=True, allow_new=False, allow_delete=False)
def lookup(self, request, urlname=None): if urlname is None: self.validate_global_access(request) vals = self.get_values(request.GET['query']) else: conference = get_authenticated_conference(request, urlname) vals = self.get_values(request.GET['query'], conference) return HttpResponse(json.dumps({ 'values': vals, }), content_type='application/json')
def feedback_sessions(request, confname): conference = get_authenticated_conference(request, confname) # Get all sessions that have actual comments on them cursor = connection.cursor() cursor.execute( "SELECT concat(s.title, ' (' || (SELECT string_agg(fullname, ', ') FROM confreg_speaker spk INNER JOIN confreg_conferencesession_speaker css ON css.speaker_id=spk.id WHERE css.conferencesession_id=s.id) || ')'), conference_feedback FROM confreg_conferencesessionfeedback fb INNER JOIN confreg_conferencesession s ON fb.session_id=s.id WHERE s.conference_id=%s AND NOT conference_feedback='' ORDER BY 1,2" % (conference.id, )) commented_sessions = cursor.fetchall() # Now for all of our fancy toplists # The django ORM just can't do this... minvotes = 10 if request.method == 'POST': minvotes = get_int_or_error(request.POST, 'minvotes') toplists = [] # Start with top sessions toplists.extend( build_toplists( 'Sessions', "SELECT s.title || ' (' || (SELECT string_agg(fullname, ', ') FROM confreg_speaker spk INNER JOIN confreg_conferencesession_speaker css ON css.speaker_id=spk.id WHERE css.conferencesession_id=s.id) || ')', avg(fb.{{key}}), count(*), stddev(fb.{{key}}) FROM confreg_conferencesessionfeedback fb INNER JOIN confreg_conferencesession s ON fb.session_id=s.id WHERE s.conference_id=%s AND fb.{{key}}>0 GROUP BY s.id HAVING count(*)>=%s ORDER BY 2 DESC" % (conference.id, minvotes))) # Now let's do the speakers toplists.extend( build_toplists( 'Speakers', "SELECT (SELECT string_agg(fullname, ', ') FROM confreg_speaker spk INNER JOIN confreg_conferencesession_speaker css ON css.speaker_id=spk.id WHERE css.conferencesession_id=s.id) AS speakername, avg(fb.{{key}}), count(*), stddev(fb.{{key}}) FROM confreg_conferencesessionfeedback fb INNER JOIN confreg_conferencesession s ON fb.session_id=s.id WHERE s.conference_id=%s AND fb.{{key}}>0 GROUP BY speakername HAVING count(*)>=%s ORDER BY 2 DESC" % (conference.id, minvotes))) return render( request, 'confreg/admin_session_feedback.html', { 'conference': conference, 'toplists': toplists, 'minvotes': minvotes, 'commented_sessions': commented_sessions, 'breadcrumbs': (('/events/admin/{0}/reports/feedback/'.format( conference.urlname), 'Feedback'), ), 'helplink': 'feedback', })
def conference_session_send_email(request, urlname): conference = get_authenticated_conference(request, urlname) return _attendee_email_form(request, conference, """ SELECT r.id AS regid, s.user_id, s.fullname, COALESCE(r.email, u.email) AS email FROM confreg_speaker s INNER JOIN auth_user u ON u.id=s.user_id LEFT JOIN confreg_conferenceregistration r ON (r.conference_id=%(conference)s AND r.attendee_id=s.user_id) WHERE EXISTS ( SELECT 1 FROM confreg_conferencesession sess INNER JOIN confreg_conferencesession_speaker ccs ON sess.id=ccs.conferencesession_id WHERE conferencesession_id=ANY(%(idlist)s) AND sess.conference_id=%(conference)s AND speaker_id=s.id)""", [('../', 'Conference sessions'), ], )
def multireg_refund(request, urlname, bulkid): conference = get_authenticated_conference(request, urlname) bulkpay = get_object_or_404(BulkPayment, pk=bulkid, conference=conference) if bulkpay.conferenceregistration_set.exists(): messages.error(request, "This bulk payment has registrations, cannot be canceled!") return HttpResponseRedirect("../../") invoice = bulkpay.invoice if not invoice: messages.error(request, "This bulk payment does not have an invoice!") return HttpResonseRedirect("../../") if not invoice.paidat: messages.error(request, "This bulk payment invoice has not been paid!") return HttpResonseRedirect("../../") if request.method == 'POST': form = BulkPaymentRefundForm(invoice, data=request.POST) if form.is_valid(): manager = InvoiceManager() manager.refund_invoice(invoice, 'Multi registration refunded', form.cleaned_data['amount'], form.cleaned_data['vatamount'], conference.vat_registrations) send_conference_notification( conference, 'Multi registration {} refunded'.format(bulkpay.id), 'Multi registration {} purchased by {} {} has been refunded.\nNo registrations were active in this multi registration, and the multi registration has now been deleted.\n'.format(bulkpay.id, bulkpay.user.first_name, bulkpay.user.last_name), ) bulkpay.delete() messages.info(request, 'Multi registration has been refunded and deleted.') return HttpResponseRedirect("../../") else: form = BulkPaymentRefundForm(invoice, initial={'amount': invoice.total_amount - invoice.total_vat, 'vatamount': invoice.total_vat}) return render(request, 'confreg/admin_backend_form.html', { 'conference': conference, 'basetemplate': 'confreg/confadmin_base.html', 'form': form, 'whatverb': 'Refund', 'what': 'multi registration', 'savebutton': 'Refund', 'cancelurl': '../../', 'breadcrumbs': [('/events/admin/{}/multiregs/'.format(conference.urlname), 'Multi Registrations'), ], 'helplink': 'registrations', })
def edit_sponsor(request, urlname, sponsorid): conference = get_authenticated_conference(request, urlname) sponsor = Sponsor.objects.get(conference=conference, pk=sponsorid) return backend_process_form( request, urlname, BackendSponsorForm, sponsor.pk, conference=conference, allow_new=False, allow_delete=not sponsor.invoice, deleted_url='../../', breadcrumbs=[ ('/events/sponsor/admin/{0}/'.format(urlname), 'Sponsors'), ('/events/sponsor/admin/{0}/{1}/'.format(urlname, sponsor.pk), sponsor.name), ])
def tweetcampaignselect(request, urlname): conference = get_authenticated_conference(request, urlname) if request.method == 'POST': form = TweetCampaignSelectForm(data=request.POST) if form.is_valid(): return HttpResponseRedirect("{}/".format(form.cleaned_data['campaigntype'])) else: form = TweetCampaignSelectForm() return render(request, 'confreg/admin_backend_form.html', { 'conference': conference, 'basetemplate': 'confreg/confadmin_base.html', 'form': form, 'whatverb': 'Create campaign', 'savebutton': 'Select campaign type', 'cancelurl': '../../', 'helplink': 'integrations#campaigns', })
def pdfschedule(request, confname): conference = get_authenticated_conference(request, confname) if request.method == "POST": form = PdfScheduleForm(conference, data=request.POST) if form.is_valid(): if form.cleaned_data.get('room', None): return build_linear_pdf_schedule( conference, form.cleaned_data['room'], form.cleaned_data['tracks'], form.cleaned_data.get('day', None), form.cleaned_data.get('colored', None), form.cleaned_data.get('pagesize', None), form.cleaned_data.get('orientation', None), form.cleaned_data.get('titledatefmt', None), ) else: return build_complete_pdf_schedule( conference, form.cleaned_data['tracks'], form.cleaned_data.get('day', None), form.cleaned_data.get('colored', None), form.cleaned_data.get('pagesize', None), form.cleaned_data.get('orientation', None), form.cleaned_data.get('pagesperday', None), form.cleaned_data.get('titledatefmt', None), ) # Fall through and render the form again if it's not valid else: form = PdfScheduleForm(conference) return render(request, 'confreg/pdfschedule.html', { 'conference': conference, 'form': form, 'helplink': 'schedule#pdf', })
def backend_process_form(request, urlname, formclass, id, cancel_url='../', saved_url='../', allow_new=True, allow_delete=True, breadcrumbs=None, permissions_already_checked=False, conference=None, bypass_conference_filter=False, instancemaker=None, deleted_url=None, topadmin=None): if not conference and not bypass_conference_filter: conference = get_authenticated_conference(request, urlname) if not formclass.Meta.fields: raise Exception("This view only works if fields are explicitly listed") if request.GET.get('fieldpreview', ''): f = request.GET.get('fieldpreview') if f not in formclass.dynamic_preview_fields: raise Http404() try: return HttpResponse(formclass.get_dynamic_preview(f, request.GET.get('previewval', ''), id), content_type='text/plain') except Exception: return HttpResponse('', content_type='text/plain') nopostprocess = False newformdata = None if not deleted_url: deleted_url = cancel_url if not instancemaker: if conference: instancemaker = lambda: formclass.Meta.model(conference=conference) else: instancemaker = lambda: formclass.Meta.model() if topadmin: basetemplate = 'adm/admin_base.html' else: basetemplate = 'confreg/confadmin_base.html' if allow_new and not id: if formclass.form_before_new: if request.method == 'POST' and '_validator' in request.POST: # This is a postback from the *actual* form newformdata = request.POST['_newformdata'] instance = instancemaker() else: # Postback to the first step create form newinfo = False if request.method == 'POST': # Making the new one! newform = formclass.form_before_new(request.POST) if newform.is_valid(): newinfo = True else: newform = formclass.form_before_new() if not newinfo: return render(request, 'confreg/admin_backend_form.html', { 'conference': conference, 'basetemplate': basetemplate, 'topadmin': topadmin, 'form': newform, 'whatverb': 'Create new', 'what': formclass._verbose_name(), 'savebutton': 'Create', 'cancelurl': cancel_url, 'helplink': newform.helplink, 'breadcrumbs': breadcrumbs, }) instance = instancemaker() newformdata = newform.get_newform_data() nopostprocess = True else: # No special form_before_new, so just create an empty instance instance = instancemaker() # Set initial values on newly created instance, if any are set for k, v in list(formclass.get_initial().items()): setattr(instance, k, v) else: if bypass_conference_filter: instance = get_object_or_404(formclass.Meta.model, pk=id) else: if hasattr(formclass.Meta, 'conference_queryset'): try: instance = formclass.Meta.conference_queryset(conference).get(pk=id) except formclass.Meta.model.DoesNotExist: raise Http404() else: instance = get_object_or_404(formclass.Meta.model, pk=id, conference=conference) if request.method == 'GET' and request.GET.get('validate', '') == '1': if not id: return HttpResponse("Record not saved, cannot preview", content_type='text/plain') else: try: return HttpResponse(formclass.validate_data_for(instance)) except Exception as e: return HttpResponse("Validation failed: {}".format(e)) if request.method == 'POST' and not nopostprocess: extra_error = None if allow_delete and request.POST.get('submit', None) == 'Delete': if instance.pk: if hasattr(instance, 'validate_object_delete'): try: instance.validate_object_delete() except ValidationError as e: extra_error = "This {0} cannot be deleted: {1}".format(formclass.Meta.model._meta.verbose_name, e.message) # Are there any associated objects here, by any chance? collector = NestedObjects(using='default') collector.collect([instance, ]) to_delete = collector.nested() to_delete.remove(instance) if to_delete: to_delete = [d for d in flatten_list(to_delete[0]) if d._meta.model_name not in formclass.auto_cascade_delete_to] if extra_error: pass elif to_delete: pieces = [str(to_delete[n]) for n in range(0, min(5, len(to_delete))) if not isinstance(to_delete[n], list)] extra_error = "This {0} cannot be deleted. It would have resulted in the following other objects also being deleted: {1}".format(formclass._verbose_name(), ', '.join(pieces)) else: messages.info(request, "{0} {1} deleted.".format(formclass._verbose_name().capitalize(), instance)) instance.delete() return HttpResponseRedirect(deleted_url) else: messages.warning(request, "New {0} not deleted, object was never saved.".format(formclass._verbose_name().capitalize())) return HttpResponseRedirect(cancel_url) form = formclass(request, conference, instance=instance, data=request.POST, files=request.FILES, newformdata=newformdata) if extra_error: form.add_error(None, extra_error) # Figure out if a custom submit button was pressed for k, v in request.POST.items(): if k.startswith('submit_id_'): # We do! f = form.fields[k[10:]] if f.callback: f.callback(request) return HttpResponseRedirect(".") if form.is_valid(): # We don't want to use form.save(), because it actually saves all # fields on the model, including those we don't care about. # The savem2m model, however, *does* care about the listed fields. # Consistency is overrated! with transaction.atomic(): if allow_new and ((not instance.pk) or form.force_insert): form.pre_create_item() form.save() form._save_m2m() all_excludes = ['_validator', '_newformdata'] + list(form.readonly_fields) + form.nosave_fields if form.json_form_fields: for fn, ffields in form.json_form_fields.items(): all_excludes.extend(ffields) form.instance.save(update_fields=[f for f in form.fields.keys() if f not in all_excludes and not isinstance(form[f].field, forms.ModelMultipleChoiceField)]) # Merge fields stored in json if form.json_form_fields: for fn, ffields in form.json_form_fields.items(): d = getattr(form.instance, fn, {}) d.update({fld: form.cleaned_data[fld] for fld in ffields}) setattr(form.instance, fn, d) form.instance.save(update_fields=form.json_form_fields.keys()) return HttpResponseRedirect(saved_url) else: form = formclass(request, conference, instance=instance, newformdata=newformdata) if instance.pk: try: adminurl = reverse('admin:{0}_{1}_change'.format(instance._meta.app_label, instance._meta.model_name), args=(instance.pk,)) except NoReverseMatch: adminurl = None what = formclass._verbose_name() else: adminurl = None what = 'new {0}'.format(formclass._verbose_name()) return render(request, 'confreg/admin_backend_form.html', { 'conference': conference, 'basetemplate': basetemplate, 'topadmin': topadmin, 'form': form, 'id': instance.pk, 'what': what, 'cancelurl': cancel_url, 'breadcrumbs': breadcrumbs, 'helplink': form.helplink, 'allow_delete': allow_delete and instance.pk, 'adminurl': adminurl, 'linked': [(url, handler, handler.get_list(form.instance)) for url, handler in list(form.linked_objects.items()) if form.instance], })
def backend_list_editor(request, urlname, formclass, resturl, allow_new=True, allow_delete=True, conference=None, breadcrumbs=[], bypass_conference_filter=False, instancemaker=None, return_url='../', topadmin=None, object_queryset=None): if not conference and not bypass_conference_filter: conference = get_authenticated_conference(request, urlname) if topadmin: basetemplate = 'adm/admin_base.html' else: basetemplate = 'confreg/confadmin_base.html' if resturl: resturl = resturl.rstrip('/') if resturl == '' or resturl is None: # Render the list of objects if bypass_conference_filter: if object_queryset is not None: objects = object_queryset.all() else: objects = formclass.Meta.model.objects.all() else: if hasattr(formclass.Meta, 'conference_queryset'): objects = formclass.Meta.conference_queryset(conference).all() else: objects = formclass.Meta.model.objects.filter(conference=conference) if formclass.list_order_by: objects = objects.order_by(*formclass.list_order_by) if formclass.queryset_select_related: objects = objects.select_related(*formclass.queryset_select_related) if formclass.queryset_extra_fields: objects = objects.extra(select=formclass.queryset_extra_fields) objects = objects.only(*(formclass.list_fields - formclass.queryset_extra_fields.keys())) if request.method == "POST": if request.POST.get('operation') == 'assign': what = request.POST.get('what') related = formclass.Meta.model._meta.get_field(what).related_model setval = request.POST.get('assignid') if setval: setval = int(setval) if what not in formclass.Meta.fields: # Trying to update invalid field! raise PermissionDenied() with transaction.atomic(): for obj in objects.filter(id__in=request.POST.get('idlist').split(',')): if isinstance(getattr(obj, what), bool): # Special-case booleans, they can only be set to true or false, and clearfing # means the same as set to false. if setval: setattr(obj, what, True) else: setattr(obj, what, False) else: if setval: setattr(obj, what, related.objects.get(pk=setval)) else: setattr(obj, what, None) obj.save() return HttpResponseRedirect('.') else: raise Http404() values = [{ 'id': o.pk, 'vals': [getattr(o, '_display_{0}'.format(f), getattr(o, f)) for f in formclass.list_fields], 'rowclass': formclass.get_rowclass(o), } for o in objects] return render(request, 'confreg/admin_backend_list.html', { 'conference': conference, 'basetemplate': basetemplate, 'topadmin': topadmin, 'values': values, 'title': formclass._verbose_name_plural().capitalize(), 'singular_name': formclass._verbose_name(), 'plural_name': formclass._verbose_name_plural(), 'headers': [formclass.get_field_verbose_name(f) for f in formclass.list_fields], 'coltypes': formclass.coltypes, 'filtercolumns': formclass.get_column_filters(conference), 'defaultsort': formclass.numeric_defaultsort(), 'return_url': return_url, 'allow_new': allow_new, 'allow_delete': allow_delete, 'allow_copy_previous': formclass.allow_copy_previous, 'allow_email': formclass.allow_email, 'assignable_columns': formclass.get_assignable_columns(conference), 'breadcrumbs': breadcrumbs, 'helplink': formclass.helplink, }) if allow_new and resturl == 'new': # This one is more interesting... return backend_process_form(request, urlname, formclass, None, allow_new=True, allow_delete=allow_delete, breadcrumbs=breadcrumbs + [('../', formclass._verbose_name_plural().capitalize()), ], conference=conference, bypass_conference_filter=bypass_conference_filter, instancemaker=instancemaker, topadmin=topadmin, ) restpieces = resturl.split('/') if formclass.allow_copy_previous and restpieces[0] == 'copy': return backend_handle_copy_previous(request, formclass, restpieces, conference) # Is it an id? try: id = int(restpieces[0]) except ValueError: # No id. So we don't know. Fail. raise Http404() if len(restpieces) > 2 and restpieces[1] in formclass.linked_objects: # We are editing a sub-object! handler = formclass.linked_objects[restpieces[1]] if conference: if hasattr(formclass.Meta, 'conference_queryset'): masterobj = formclass.Meta.conference_queryset(conference).get(pk=id) else: masterobj = formclass.Meta.model.objects.get(pk=id, conference=conference) else: masterobj = formclass.Meta.model.objects.get(pk=id) if restpieces[2] == 'new': subid = None subobj = None else: try: subid = int(restpieces[2]) subobj = handler.get_object(masterobj, subid) if not subobj: raise Http404() except ValueError: # No proper subid. So fail. raise Http404() return backend_process_form(request, urlname, handler.get_form(subobj, request.POST), subid, breadcrumbs=breadcrumbs + [ ('../../../', formclass._verbose_name_plural().capitalize()), ('../../', masterobj), ], cancel_url='../../', saved_url='../../', conference=conference, bypass_conference_filter=True, instancemaker=handler.get_instancemaker(masterobj), topadmin=topadmin, ) if len(restpieces) > 1: raise Http404() return backend_process_form(request, urlname, formclass, id, allow_delete=allow_delete, breadcrumbs=breadcrumbs + [('../', formclass._verbose_name_plural().capitalize()), ], conference=conference, bypass_conference_filter=bypass_conference_filter, topadmin=topadmin, )
def backend_handle_copy_previous(request, formclass, restpieces, conference): if len(restpieces) == 1: # No conference selected yet, so start by doing that if request.method == 'POST': form = BackendCopySelectConferenceForm(request, conference, formclass.Meta.model, data=request.POST) if form.is_valid(): return HttpResponseRedirect("{0}/".format(form.cleaned_data.get('conference').pk)) else: form = BackendCopySelectConferenceForm(request, conference, formclass.Meta.model) return render(request, 'confreg/admin_backend_copy_select_conf.html', { 'conference': conference, 'form': form, 'what': formclass._verbose_name(), 'savebutton': 'Copy', 'cancelurl': '../', 'breadcrumbs': [('../', formclass._verbose_name_plural().capitalize()), ], 'helplink': formclass.helplink, }) elif len(restpieces) == 2: idlist = None confirmed_transform_value = None confirmed_transform_example = None sourceconfid = int(restpieces[1]) sourceconf = get_authenticated_conference(request, confid=sourceconfid) if request.method == "POST": idlist = sorted([int(k[2:]) for k, v in list(request.POST.items()) if k.startswith('c_') and v == '1']) if formclass.copy_transform_form: # First validate the transform form transform_form = formclass.copy_transform_form(conference, sourceconf, data=request.POST) if transform_form.is_valid(): # Transform input is valid, but is it correct? if request.POST.get('confirmed_transform', '') == transform_form.confirm_value(): with transaction.atomic(): errors = list(formclass.copy_from_conference(conference, sourceconf, idlist, transform_form)) if errors: for e in errors: messages.error(request, e) transaction.set_rollback(True) # Fall-through and re-render the form else: return HttpResponseRedirect("../../") else: # Transform input is valid, but it has not been confirmed. confirmed_transform_example = formclass.get_transform_example(conference, sourceconf, idlist, transform_form) if confirmed_transform_example: confirmed_transform_value = transform_form.confirm_value() # Fall-through to re-render the form else: with transaction.atomic(): errors = list(formclass.copy_from_conference(conference, sourceconf, idlist)) if errors: for e in errors: messages.error(request, e) transaction.set_rollback(True) transform_form = None # Fall through and re-render our forms else: return HttpResponseRedirect("../../") else: if formclass.copy_transform_form: transform_form = formclass.copy_transform_form(conference, sourceconf) else: transform_form = None objects = formclass.Meta.model.objects.filter(conference=sourceconf) if formclass.queryset_extra_fields: objects = objects.extra(select=formclass.queryset_extra_fields) values = [{'id': o.pk, 'vals': [getattr(o, '_display_{0}'.format(f), getattr(o, f)) for f in formclass.list_fields]} for o in objects] return render(request, 'confreg/admin_backend_list.html', { 'conference': conference, 'basetemplate': 'confreg/confadmin_base.html', 'values': values, 'title': formclass._verbose_name_plural().capitalize(), 'singular_name': formclass._verbose_name(), 'plural_name': formclass._verbose_name_plural(), 'headers': [formclass.get_field_verbose_name(f) for f in formclass.list_fields], 'coltypes': formclass.coltypes, 'filtercolumns': formclass.get_column_filters(conference), 'defaultsort': formclass.numeric_defaultsort(), 'return_url': '../', 'allow_new': False, 'allow_delete': False, 'allow_copy_previous': False, 'is_copy_previous': True, 'transform_form': transform_form, 'idlist': idlist, 'confirmed_transform_value': confirmed_transform_value, 'transform_example': confirmed_transform_example, 'noeditlinks': True, 'breadcrumbs': [ ('../../', formclass._verbose_name_plural().capitalize()), ('../', 'Copy {0}'.format(formclass._verbose_name_plural().capitalize())), ], 'helplink': formclass.helplink, })