from postgresqleu.util.forms import ConcurrentProtectedModelForm
from postgresqleu.util.random import generate_random_token
-from postgresqleu.accountinfo.lookups import UserLookup
-from postgresqleu.confreg.lookups import RegistrationLookup
import postgresqleu.accounting.models
from postgresqleu.confreg.models import Conference, ConferenceRegistration, ConferenceAdditionalOption
from postgresqleu.confreg.models import valid_status_transitions, get_status_string
+from backendlookups import GeneralAccountLookup, RegisteredUsersLookup, SpeakerLookup
+
class BackendDateInput(TextInput):
def __init__(self, *args, **kwargs):
kwargs.update({'attrs': {'type': 'date', 'required-pattern': '[0-9]{4}-[0-9]{2}-[0-9]{2}'}})
self.fix_fields()
+ self.fix_selectize_fields(**kwargs)
for k,v in self.fields.items():
# Adjust widgets
for field in self.readonly_fields:
self.fields[field].widget.attrs['readonly'] = 'true'
+ def fix_selectize_fields(self, **kwargs):
+ for field, lookup in self.selectize_multiple_fields.items():
+ # If this is a postback of a selectize field, it may contain ids that are not currently
+ # stored in the field. They must still be among the *allowed* values of course, which
+ # are handled by the existing queryset on the field.
+ vals = [o.pk for o in getattr(self.instance, field).all()]
+ if 'data' in kwargs and unicode(field) in kwargs['data']:
+ vals.extend([int(x) for x in kwargs['data'].getlist(field)])
+ self.fields[field].widget.attrs['data-selecturl'] = lookup.url
+ self.fields[field].queryset = self.fields[field].queryset.filter(pk__in=set(vals))
+ self.fields[field].label_from_instance = lookup.label_from_instance
+
def fix_fields(self):
pass
'asktshirt', 'askfood', 'asknick', 'asktwitter', 'askshareemail', 'askphotoconsent',
'skill_levels', 'additionalintro', 'callforpapersintro', 'sendwelcomemail', 'welcomemail',
'invoice_autocancel_hours', 'attendees_before_waitlist']
- selectize_multiple_fields = ['testers', 'talkvoters', 'staff', 'volunteers']
-
+ selectize_multiple_fields = {
+ 'testers': GeneralAccountLookup(),
+ 'talkvoters': GeneralAccountLookup(),
+ 'staff': GeneralAccountLookup(),
+ 'volunteers': RegisteredUsersLookup(None),
+ }
def fix_fields(self):
- self.fields['testers'].label_from_instance = lambda x: u'{0} {1} ({2})'.format(x.first_name, x.last_name, x.username)
- self.fields['talkvoters'].label_from_instance = lambda x: u'{0} {1} ({2})'.format(x.first_name, x.last_name, x.username)
- self.fields['staff'].label_from_instance = lambda x: u'{0} {1} ({2})'.format(x.first_name, x.last_name, x.username)
- self.fields['volunteers'].label_from_instance = lambda x: u'{0} <{1}>'.format(x.fullname, x.email)
- self.fields['volunteers'].queryset = ConferenceRegistration.objects.filter(conference=self.conference)
+ self.selectize_multiple_fields['volunteers'] = RegisteredUsersLookup(self.conference)
class BackendSuperConferenceForm(BackendForm):
fields = ['conferencename', 'urlname', 'series', 'startdate', 'enddate', 'location',
'timediff', 'contactaddr', 'sponsoraddr', 'confurl', 'administrators',
'jinjadir', 'accounting_object', 'vat_registrations', 'vat_sponsorship', ]
- selectize_multiple_fields = ['administrators', ]
+ selectize_multiple_fields = {
+ 'administrators': GeneralAccountLookup(),
+ }
accounting_object = django.forms.ChoiceField(choices=[], required=False)
exclude_date_validators = ['startdate', 'enddate']
def fix_fields(self):
- self.fields['administrators'].label_from_instance = lambda x: u'{0} {1} ({2})'.format(x.first_name, x.last_name, x.username)
self.fields['accounting_object'].choices = [('', '----'),] + [(o.name, o.name) for o in postgresqleu.accounting.models.Object.objects.filter(active=True)]
if not self.instance.id:
del self.fields['accounting_object']
'speaker_list': 'Speakers',
'status_string': 'Status',
}
- selectize_multiple_fields = ['speaker']
+ selectize_multiple_fields = {
+ 'speaker': SpeakerLookup(),
+ }
allow_copy_previous = True
copy_transform_form = BackendTransformConferenceDateTimeForm
auto_cascade_delete_to = ['conferencesession_speaker', ]
--- /dev/null
+from django.http import HttpResponse, HttpResponseForbidden
+from django.core.exceptions import PermissionDenied
+from django.contrib.auth.models import User
+from django.db.models import Q
+
+import backendviews
+from models import Conference, ConferenceRegistration, Speaker
+
+import datetime
+import json
+
+class LookupBase(object):
+ def __init__(self, conference=None):
+ self.conference = conference
+
+ @classmethod
+ def validate_global_access(self, request):
+ # User must be admin of some conference in the past 3 months (just to add some overlap)
+ # or at some point in the future.
+ if not (request.user.is_superuser or
+ Conference.objects.filter(administrators=request.user,
+ startdate__gt=datetime.datetime.now()-datetime.timedelta(days=90))):
+ raise PermissionDenied("Access denied.")
+
+ @classmethod
+ def lookup(self, request, urlname=None):
+ if urlname is None:
+ self.validate_global_access(request)
+ vals = self.get_values(request.GET['query'])
+ else:
+ conference = backendviews.get_authenticated_conference(request, urlname)
+ vals = self.get_values(request.GET['query'], conference)
+
+ return HttpResponse(json.dumps({
+ 'values': vals,
+ }), content_type='application/json')
+
+class GeneralAccountLookup(LookupBase):
+ @property
+ def url(self):
+ return '/events/admin/lookups/accounts/'
+
+ @property
+ def label_from_instance(self):
+ return lambda x: u'{0} {1} ({2})'.format(x.first_name, x.last_name, x.username)
+
+
+ @classmethod
+ def get_values(self, query):
+ return [{'id': u.id, 'value': u'{0} {1} ({2})'.format(u.first_name, u.last_name, u.username)}
+ for u in User.objects.filter(
+ Q(username__icontains=query) | Q(first_name__icontains=query) | Q(last_name__icontains=query)
+ )[:30]]
+
+
+class RegisteredUsersLookup(LookupBase):
+ @property
+ def url(self):
+ return '/events/admin/{0}/lookups/regs/'.format(self.conference.urlname)
+
+ @property
+ def label_from_instance(self):
+ return lambda x: u'{0} <{1}>'.format(x.fullname, x.email)
+
+ @classmethod
+ def get_values(self, query, conference):
+ return [{'id': r.id, 'value': r.fullname}
+ for r in ConferenceRegistration.objects.filter(
+ conference=conference,
+ payconfirmedat__isnull=False).filter(
+ Q(firstname__icontains=query) | Q(lastname__icontains=query) | Q(email__icontains=query)
+ )[:30]]
+
+
+class SpeakerLookup(LookupBase):
+ @property
+ def url(self):
+ return '/events/admin/lookups/speakers/'
+
+ @property
+ def label_from_instance(self):
+ return lambda x: unicode(x)
+
+ @classmethod
+ def get_values(self, query):
+ return [{'id': s.id, 'value': unicode(s)}
+ for s in Speaker.objects.filter(
+ Q(fullname__icontains=query) | Q(twittername__icontains=query) | Q(user__username__icontains=query)
+ )[:30]]
import postgresqleu.views
import postgresqleu.confreg.views
import postgresqleu.confreg.backendviews
+import postgresqleu.confreg.backendlookups
import postgresqleu.confreg.reporting
import postgresqleu.confreg.mobileviews
import postgresqleu.confreg.feedback
url(r'^events/admin/([^/]+)/reports/schedule/$', postgresqleu.confreg.pdfschedule.pdfschedule),
url(r'^events/admin/newconference/$', postgresqleu.confreg.backendviews.new_conference),
url(r'^events/admin/meta/series/(.*/)?$', postgresqleu.confreg.backendviews.edit_series),
+ url(r'^events/admin/lookups/accounts/$', postgresqleu.confreg.backendlookups.GeneralAccountLookup.lookup),
+ url(r'^events/admin/lookups/speakers/$', postgresqleu.confreg.backendlookups.SpeakerLookup.lookup),
url(r'^events/admin/(\w+)/$', postgresqleu.confreg.views.admin_dashboard_single),
url(r'^events/admin/(\w+)/edit/$', postgresqleu.confreg.backendviews.edit_conference),
url(r'^events/admin/(\w+)/superedit/$', postgresqleu.confreg.backendviews.superedit_conference),
+ url(r'^events/admin/(\w+)/lookups/regs/$', postgresqleu.confreg.backendlookups.RegisteredUsersLookup.lookup),
url(r'^events/admin/(\w+)/mail/$', postgresqleu.confreg.views.admin_attendeemail),
url(r'^events/admin/(\w+)/mail/(\d+)/$', postgresqleu.confreg.views.admin_attendeemail_view),
url(r'^events/admin/(\w+)/regdashboard/$', postgresqleu.confreg.views.admin_registration_dashboard),
<script language="javascript">
$(function() {
-{%for f in form.selectize_multiple_fields%}
- $('#id_{{f}}').selectize({plugins: ['remove_button']});
+{%for f,lookup in form.selectize_multiple_fields.items%}
+ $('#id_{{f}}').selectize({
+ plugins: ['remove_button'],
+ valueField: 'id',
+ labelField: 'value',
+ searchField: 'value',
+ load: function(query, callback) {
+ if (!query.length) return callback();
+ $.ajax({
+ 'url': '{{lookup.url}}',
+ 'type': 'GET',
+ 'dataType': 'json',
+ 'data': {
+ 'query': query,
+ },
+ 'error': function() { callback();},
+ 'success': function(res) { callback(res.values);},
+ });
+ }
+ });
{%endfor%}
{%for f in form.json_fields%}