def __init__(self, *args, **kwargs): """ Takes two additional keyword arguments: :param cartpos: The cart position the form should be for :param event: The event this belongs to """ cartpos = self.cartpos = kwargs.pop('cartpos', None) orderpos = self.orderpos = kwargs.pop('orderpos', None) pos = cartpos or orderpos item = pos.item questions = pos.item.questions_to_ask event = kwargs.pop('event') self.all_optional = kwargs.pop('all_optional', False) super().__init__(*args, **kwargs) add_fields = {} if item.admission and event.settings.attendee_names_asked: add_fields['attendee_name_parts'] = NamePartsFormField( max_length=255, required=event.settings.attendee_names_required and not self.all_optional, scheme=event.settings.name_scheme, titles=event.settings.name_scheme_titles, label=_('Attendee name'), initial=(cartpos.attendee_name_parts if cartpos else orderpos.attendee_name_parts), ) if item.admission and event.settings.attendee_emails_asked: add_fields['attendee_email'] = forms.EmailField( required=event.settings.attendee_emails_required and not self.all_optional, label=_('Attendee email'), initial=(cartpos.attendee_email if cartpos else orderpos.attendee_email), widget=forms.EmailInput(attrs={'autocomplete': 'email'})) if item.admission and event.settings.attendee_company_asked: add_fields['company'] = forms.CharField( required=event.settings.attendee_company_required and not self.all_optional, label=_('Company'), max_length=255, initial=(cartpos.company if cartpos else orderpos.company), ) if item.admission and event.settings.attendee_addresses_asked: add_fields['street'] = forms.CharField( required=event.settings.attendee_addresses_required and not self.all_optional, label=_('Address'), widget=forms.Textarea( attrs={ 'rows': 2, 'placeholder': _('Street and Number'), 'autocomplete': 'street-address' }), initial=(cartpos.street if cartpos else orderpos.street), ) add_fields['zipcode'] = forms.CharField( required=event.settings.attendee_addresses_required and not self.all_optional, max_length=30, label=_('ZIP code'), initial=(cartpos.zipcode if cartpos else orderpos.zipcode), widget=forms.TextInput(attrs={ 'autocomplete': 'postal-code', }), ) add_fields['city'] = forms.CharField( required=event.settings.attendee_addresses_required and not self.all_optional, label=_('City'), max_length=255, initial=(cartpos.city if cartpos else orderpos.city), widget=forms.TextInput(attrs={ 'autocomplete': 'address-level2', }), ) country = (cartpos.country if cartpos else orderpos.country) or guess_country(event) add_fields['country'] = CountryField( countries=CachedCountries).formfield( required=event.settings.attendee_addresses_required and not self.all_optional, label=_('Country'), initial=country, widget=forms.Select(attrs={ 'autocomplete': 'country', }), ) c = [('', pgettext_lazy('address', 'Select state'))] fprefix = str( self.prefix ) + '-' if self.prefix is not None and self.prefix != '-' else '' cc = None if fprefix + 'country' in self.data: cc = str(self.data[fprefix + 'country']) elif country: cc = str(country) if cc and cc in COUNTRIES_WITH_STATE_IN_ADDRESS: types, form = COUNTRIES_WITH_STATE_IN_ADDRESS[cc] statelist = [ s for s in pycountry.subdivisions.get(country_code=cc) if s.type in types ] c += sorted([(s.code[3:], s.name) for s in statelist], key=lambda s: s[1]) elif fprefix + 'state' in self.data: self.data = self.data.copy() del self.data[fprefix + 'state'] add_fields['state'] = forms.ChoiceField( label=pgettext_lazy('address', 'State'), required=False, choices=c, widget=forms.Select(attrs={ 'autocomplete': 'address-level1', }), ) add_fields['state'].widget.is_required = True field_positions = list([(n, event.settings.system_question_order.get( n if n != 'state' else 'country', 0)) for n in add_fields.keys()]) for q in questions: # Do we already have an answer? Provide it as the initial value answers = [a for a in pos.answerlist if a.question_id == q.id] if answers: initial = answers[0] else: initial = None tz = pytz.timezone(event.settings.timezone) help_text = rich_text(q.help_text) label = escape(q.question) # django-bootstrap3 calls mark_safe required = q.required and not self.all_optional if q.type == Question.TYPE_BOOLEAN: if q.required: # For some reason, django-bootstrap3 does not set the required attribute # itself. widget = forms.CheckboxInput( attrs={'required': 'required'}) else: widget = forms.CheckboxInput() if initial: initialbool = (initial.answer == "True") else: initialbool = False field = forms.BooleanField( label=label, required=required, help_text=help_text, initial=initialbool, widget=widget, ) elif q.type == Question.TYPE_NUMBER: field = forms.DecimalField( label=label, required=required, min_value=q.valid_number_min or Decimal('0.00'), max_value=q.valid_number_max, help_text=q.help_text, initial=initial.answer if initial else None, ) elif q.type == Question.TYPE_STRING: field = forms.CharField( label=label, required=required, help_text=help_text, initial=initial.answer if initial else None, ) elif q.type == Question.TYPE_TEXT: field = forms.CharField( label=label, required=required, help_text=help_text, widget=forms.Textarea, initial=initial.answer if initial else None, ) elif q.type == Question.TYPE_COUNTRYCODE: field = CountryField( countries=CachedCountries, blank=True, null=True, blank_label=' ', ).formfield( label=label, required=required, help_text=help_text, widget=forms.Select, empty_label=' ', initial=initial.answer if initial else (guess_country(event) if required else None), ) elif q.type == Question.TYPE_CHOICE: field = forms.ModelChoiceField( queryset=q.options, label=label, required=required, help_text=help_text, widget=forms.Select, to_field_name='identifier', empty_label='', initial=initial.options.first() if initial else None, ) elif q.type == Question.TYPE_CHOICE_MULTIPLE: field = forms.ModelMultipleChoiceField( queryset=q.options, label=label, required=required, help_text=help_text, to_field_name='identifier', widget=QuestionCheckboxSelectMultiple, initial=initial.options.all() if initial else None, ) elif q.type == Question.TYPE_FILE: field = ExtFileField( label=label, required=required, help_text=help_text, initial=initial.file if initial else None, widget=UploadedFileWidget(position=pos, event=event, answer=initial), ext_whitelist=(".png", ".jpg", ".gif", ".jpeg", ".pdf", ".txt", ".docx", ".gif", ".svg", ".pptx", ".ppt", ".doc", ".xlsx", ".xls", ".jfif", ".heic", ".heif", ".pages", ".bmp", ".tif", ".tiff"), max_size=10 * 1024 * 1024, ) elif q.type == Question.TYPE_DATE: attrs = {} if q.valid_date_min: attrs['data-min'] = q.valid_date_min.isoformat() if q.valid_date_max: attrs['data-max'] = q.valid_date_max.isoformat() field = forms.DateField( label=label, required=required, help_text=help_text, initial=dateutil.parser.parse(initial.answer).date() if initial and initial.answer else None, widget=DatePickerWidget(attrs), ) if q.valid_date_min: field.validators.append(MinDateValidator(q.valid_date_min)) if q.valid_date_max: field.validators.append(MaxDateValidator(q.valid_date_max)) elif q.type == Question.TYPE_TIME: field = forms.TimeField( label=label, required=required, help_text=help_text, initial=dateutil.parser.parse(initial.answer).time() if initial and initial.answer else None, widget=TimePickerWidget( time_format=get_format_without_seconds( 'TIME_INPUT_FORMATS')), ) elif q.type == Question.TYPE_DATETIME: field = SplitDateTimeField( label=label, required=required, help_text=help_text, initial=dateutil.parser.parse( initial.answer).astimezone(tz) if initial and initial.answer else None, widget=SplitDateTimePickerWidget( time_format=get_format_without_seconds( 'TIME_INPUT_FORMATS'), min_date=q.valid_datetime_min, max_date=q.valid_datetime_max), ) if q.valid_datetime_min: field.validators.append( MinDateTimeValidator(q.valid_datetime_min)) if q.valid_datetime_max: field.validators.append( MaxDateTimeValidator(q.valid_datetime_max)) elif q.type == Question.TYPE_PHONENUMBER: babel_locale = 'en' # Babel, and therefore django-phonenumberfield, do not support our custom locales such das de_Informal if localedata.exists(get_language()): babel_locale = get_language() elif localedata.exists(get_language()[:2]): babel_locale = get_language()[:2] with language(babel_locale): default_country = guess_country(event) default_prefix = None for prefix, values in _COUNTRY_CODE_TO_REGION_CODE.items(): if str(default_country) in values: default_prefix = prefix try: initial = PhoneNumber().from_string( initial.answer) if initial else "+{}.".format( default_prefix) except NumberParseException: initial = None field = PhoneNumberField( label=label, required=required, help_text=help_text, # We now exploit an implementation detail in PhoneNumberPrefixWidget to allow us to pass just # a country code but no number as an initial value. It's a bit hacky, but should be stable for # the future. initial=initial, widget=WrappedPhoneNumberPrefixWidget()) field.question = q if answers: # Cache the answer object for later use field.answer = answers[0] if q.dependency_question_id: field.widget.attrs[ 'data-question-dependency'] = q.dependency_question_id field.widget.attrs[ 'data-question-dependency-values'] = escapejson_attr( json.dumps(q.dependency_values)) if q.type != 'M': field.widget.attrs[ 'required'] = q.required and not self.all_optional field._required = q.required and not self.all_optional field.required = False add_fields['question_%s' % q.id] = field field_positions.append(('question_%s' % q.id, q.position)) field_positions.sort(key=lambda e: e[1]) for fname, p in field_positions: self.fields[fname] = add_fields[fname] responses = question_form_fields.send(sender=event, position=pos) data = pos.meta_info_data for r, response in sorted(responses, key=lambda r: str(r[0])): for key, value in response.items(): # We need to be this explicit, since OrderedDict.update does not retain ordering self.fields[key] = value value.initial = data.get('question_form_data', {}).get(key) for k, v in self.fields.items(): if v.widget.attrs.get( 'autocomplete') or k == 'attendee_name_parts': v.widget.attrs['autocomplete'] = 'section-{} '.format( self.prefix) + v.widget.attrs.get('autocomplete', '')
def __init__(self, *args, **kwargs): """ Takes two additional keyword arguments: :param cartpos: The cart position the form should be for :param event: The event this belongs to """ cartpos = self.cartpos = kwargs.pop('cartpos', None) orderpos = self.orderpos = kwargs.pop('orderpos', None) pos = cartpos or orderpos item = pos.item questions = pos.item.questions_to_ask event = kwargs.pop('event') self.all_optional = kwargs.pop('all_optional', False) super().__init__(*args, **kwargs) if item.admission and event.settings.attendee_names_asked: self.fields['attendee_name_parts'] = NamePartsFormField( max_length=255, required=event.settings.attendee_names_required, scheme=event.settings.name_scheme, titles=event.settings.name_scheme_titles, label=_('Attendee name'), initial=(cartpos.attendee_name_parts if cartpos else orderpos.attendee_name_parts), ) if item.admission and event.settings.attendee_emails_asked: self.fields['attendee_email'] = forms.EmailField( required=event.settings.attendee_emails_required, label=_('Attendee email'), initial=(cartpos.attendee_email if cartpos else orderpos.attendee_email), widget=forms.EmailInput(attrs={'autocomplete': 'email'})) for q in questions: # Do we already have an answer? Provide it as the initial value answers = [a for a in pos.answerlist if a.question_id == q.id] if answers: initial = answers[0] else: initial = None tz = pytz.timezone(event.settings.timezone) help_text = rich_text(q.help_text) label = escape(q.question) # django-bootstrap3 calls mark_safe required = q.required and not self.all_optional if q.type == Question.TYPE_BOOLEAN: if q.required: # For some reason, django-bootstrap3 does not set the required attribute # itself. widget = forms.CheckboxInput( attrs={'required': 'required'}) else: widget = forms.CheckboxInput() if initial: initialbool = (initial.answer == "True") else: initialbool = False field = forms.BooleanField( label=label, required=required, help_text=help_text, initial=initialbool, widget=widget, ) elif q.type == Question.TYPE_NUMBER: field = forms.DecimalField( label=label, required=required, help_text=q.help_text, initial=initial.answer if initial else None, min_value=Decimal('0.00'), ) elif q.type == Question.TYPE_STRING: field = forms.CharField( label=label, required=required, help_text=help_text, initial=initial.answer if initial else None, ) elif q.type == Question.TYPE_TEXT: field = forms.CharField( label=label, required=required, help_text=help_text, widget=forms.Textarea, initial=initial.answer if initial else None, ) elif q.type == Question.TYPE_COUNTRYCODE: field = CountryField().formfield( label=label, required=required, help_text=help_text, widget=forms.Select, empty_label='', initial=initial.answer if initial else None, ) elif q.type == Question.TYPE_CHOICE: field = forms.ModelChoiceField( queryset=q.options, label=label, required=required, help_text=help_text, widget=forms.Select, to_field_name='identifier', empty_label='', initial=initial.options.first() if initial else None, ) elif q.type == Question.TYPE_CHOICE_MULTIPLE: field = forms.ModelMultipleChoiceField( queryset=q.options, label=label, required=required, help_text=help_text, to_field_name='identifier', widget=forms.CheckboxSelectMultiple, initial=initial.options.all() if initial else None, ) elif q.type == Question.TYPE_FILE: field = forms.FileField( label=label, required=required, help_text=help_text, initial=initial.file if initial else None, widget=UploadedFileWidget(position=pos, event=event, answer=initial), ) elif q.type == Question.TYPE_DATE: field = forms.DateField( label=label, required=required, help_text=help_text, initial=dateutil.parser.parse(initial.answer).date() if initial and initial.answer else None, widget=DatePickerWidget(), ) elif q.type == Question.TYPE_TIME: field = forms.TimeField( label=label, required=required, help_text=help_text, initial=dateutil.parser.parse(initial.answer).time() if initial and initial.answer else None, widget=TimePickerWidget( time_format=get_format_without_seconds( 'TIME_INPUT_FORMATS')), ) elif q.type == Question.TYPE_DATETIME: field = SplitDateTimeField( label=label, required=required, help_text=help_text, initial=dateutil.parser.parse( initial.answer).astimezone(tz) if initial and initial.answer else None, widget=SplitDateTimePickerWidget( time_format=get_format_without_seconds( 'TIME_INPUT_FORMATS')), ) elif q.type == Question.TYPE_PHONENUMBER: babel_locale = 'en' # Babel, and therefore django-phonenumberfield, do not support our custom locales such das de_Informal if localedata.exists(get_language()): babel_locale = get_language() elif localedata.exists(get_language()[:2]): babel_locale = get_language()[:2] with language(babel_locale): default_country = guess_country(event) default_prefix = None for prefix, values in _COUNTRY_CODE_TO_REGION_CODE.items(): if str(default_country) in values: default_prefix = prefix try: initial = PhoneNumber().from_string( initial.answer) if initial else "+{}.".format( default_prefix) except NumberParseException: initial = None field = PhoneNumberField( label=label, required=required, help_text=help_text, # We now exploit an implementation detail in PhoneNumberPrefixWidget to allow us to pass just # a country code but no number as an initial value. It's a bit hacky, but should be stable for # the future. initial=initial, widget=WrappedPhoneNumberPrefixWidget()) field.question = q if answers: # Cache the answer object for later use field.answer = answers[0] if q.dependency_question_id: field.widget.attrs[ 'data-question-dependency'] = q.dependency_question_id field.widget.attrs[ 'data-question-dependency-values'] = escapejson_attr( json.dumps(q.dependency_values)) if q.type != 'M': field.widget.attrs[ 'required'] = q.required and not self.all_optional field._required = q.required and not self.all_optional field.required = False self.fields['question_%s' % q.id] = field responses = question_form_fields.send(sender=event, position=pos) data = pos.meta_info_data for r, response in sorted(responses, key=lambda r: str(r[0])): for key, value in response.items(): # We need to be this explicit, since OrderedDict.update does not retain ordering self.fields[key] = value value.initial = data.get('question_form_data', {}).get(key) for k, v in self.fields.items(): if v.widget.attrs.get( 'autocomplete') or k == 'attendee_name_parts': v.widget.attrs['autocomplete'] = 'section-{} '.format( self.prefix) + v.widget.attrs.get('autocomplete', '')
def __init__(self, *args, **kwargs): """ Takes two additional keyword arguments: :param cartpos: The cart position the form should be for :param event: The event this belongs to """ cartpos = self.cartpos = kwargs.pop('cartpos', None) orderpos = self.orderpos = kwargs.pop('orderpos', None) pos = cartpos or orderpos item = pos.item questions = pos.item.questions_to_ask event = kwargs.pop('event') self.all_optional = kwargs.pop('all_optional', False) super().__init__(*args, **kwargs) if item.admission and event.settings.attendee_names_asked: self.fields['attendee_name_parts'] = NamePartsFormField( max_length=255, required=event.settings.attendee_names_required, scheme=event.settings.name_scheme, label=_('Attendee name'), initial=(cartpos.attendee_name_parts if cartpos else orderpos.attendee_name_parts), ) if item.admission and event.settings.attendee_emails_asked: self.fields['attendee_email'] = forms.EmailField( required=event.settings.attendee_emails_required, label=_('Attendee email'), initial=(cartpos.attendee_email if cartpos else orderpos.attendee_email)) for q in questions: # Do we already have an answer? Provide it as the initial value answers = [a for a in pos.answerlist if a.question_id == q.id] if answers: initial = answers[0] else: initial = None tz = pytz.timezone(event.settings.timezone) help_text = rich_text(q.help_text) label = escape(q.question) # django-bootstrap3 calls mark_safe required = q.required and not self.all_optional if q.type == Question.TYPE_BOOLEAN: if q.required: # For some reason, django-bootstrap3 does not set the required attribute # itself. widget = forms.CheckboxInput( attrs={'required': 'required'}) else: widget = forms.CheckboxInput() if initial: initialbool = (initial.answer == "True") else: initialbool = False field = forms.BooleanField( label=label, required=required, help_text=help_text, initial=initialbool, widget=widget, ) elif q.type == Question.TYPE_NUMBER: field = forms.DecimalField( label=label, required=required, help_text=q.help_text, initial=initial.answer if initial else None, min_value=Decimal('0.00'), ) elif q.type == Question.TYPE_STRING: field = forms.CharField( label=label, required=required, help_text=help_text, initial=initial.answer if initial else None, ) elif q.type == Question.TYPE_TEXT: field = forms.CharField( label=label, required=required, help_text=help_text, widget=forms.Textarea, initial=initial.answer if initial else None, ) elif q.type == Question.TYPE_CHOICE: field = forms.ModelChoiceField( queryset=q.options, label=label, required=required, help_text=help_text, widget=forms.Select, to_field_name='identifier', empty_label='', initial=initial.options.first() if initial else None, ) elif q.type == Question.TYPE_CHOICE_MULTIPLE: field = forms.ModelMultipleChoiceField( queryset=q.options, label=label, required=required, help_text=help_text, to_field_name='identifier', widget=forms.CheckboxSelectMultiple, initial=initial.options.all() if initial else None, ) elif q.type == Question.TYPE_FILE: field = forms.FileField( label=label, required=required, help_text=help_text, initial=initial.file if initial else None, widget=UploadedFileWidget(position=pos, event=event, answer=initial), ) elif q.type == Question.TYPE_DATE: field = forms.DateField( label=label, required=required, help_text=help_text, initial=dateutil.parser.parse(initial.answer).date() if initial and initial.answer else None, widget=DatePickerWidget(), ) elif q.type == Question.TYPE_TIME: field = forms.TimeField( label=label, required=required, help_text=help_text, initial=dateutil.parser.parse(initial.answer).time() if initial and initial.answer else None, widget=TimePickerWidget( time_format=get_format_without_seconds( 'TIME_INPUT_FORMATS')), ) elif q.type == Question.TYPE_DATETIME: field = SplitDateTimeField( label=label, required=required, help_text=help_text, initial=dateutil.parser.parse( initial.answer).astimezone(tz) if initial and initial.answer else None, widget=SplitDateTimePickerWidget( time_format=get_format_without_seconds( 'TIME_INPUT_FORMATS')), ) field.question = q if answers: # Cache the answer object for later use field.answer = answers[0] if q.dependency_question_id: field.widget.attrs[ 'data-question-dependency'] = q.dependency_question_id field.widget.attrs[ 'data-question-dependency-value'] = q.dependency_value if q.type != 'M': field.widget.attrs[ 'required'] = q.required and not self.all_optional field._required = q.required and not self.all_optional field.required = False self.fields['question_%s' % q.id] = field responses = question_form_fields.send(sender=event, position=pos) data = pos.meta_info_data for r, response in sorted(responses, key=lambda r: str(r[0])): for key, value in response.items(): # We need to be this explicit, since OrderedDict.update does not retain ordering self.fields[key] = value value.initial = data.get('question_form_data', {}).get(key)
class EventCancelForm(forms.Form): subevent = forms.ModelChoiceField( SubEvent.objects.none(), label=pgettext_lazy('subevent', 'Date'), required=False, empty_label=pgettext_lazy('subevent', 'All dates') ) all_subevents = forms.BooleanField( label=_('Cancel all dates'), initial=False, required=False, ) subevents_from = forms.SplitDateTimeField( widget=SplitDateTimePickerWidget(attrs={ 'data-inverse-dependency': '#id_all_subevents', }), label=pgettext_lazy('subevent', 'All dates starting at or after'), required=False, ) subevents_to = forms.SplitDateTimeField( widget=SplitDateTimePickerWidget(attrs={ 'data-inverse-dependency': '#id_all_subevents', }), label=pgettext_lazy('subevent', 'All dates starting before'), required=False, ) auto_refund = forms.BooleanField( label=_('Automatically refund money if possible'), initial=True, required=False ) manual_refund = forms.BooleanField( label=_('Create manual refund if the payment method does not support automatic refunds'), widget=forms.CheckboxInput(attrs={'data-display-dependency': '#id_auto_refund'}), initial=True, required=False, help_text=_('If checked, all payments with a payment method not supporting automatic refunds will be on your ' 'manual refund to-do list. Do not check if you want to refund some of the orders by offsetting ' 'with different orders or issuing gift cards.') ) refund_as_giftcard = forms.BooleanField( label=_('Refund order value to a gift card instead instead of the original payment method'), widget=forms.CheckboxInput(attrs={'data-display-dependency': '#id_auto_refund'}), initial=False, required=False, ) gift_card_expires = SplitDateTimeField( label=_('Gift card validity'), required=False, widget=SplitDateTimePickerWidget( attrs={'data-display-dependency': '#id_refund_as_giftcard'}, ) ) gift_card_conditions = forms.CharField( label=_('Special terms and conditions'), required=False, widget=forms.Textarea( attrs={'rows': 2, 'data-display-dependency': '#id_refund_as_giftcard'}, ) ) keep_fee_fixed = forms.DecimalField( label=_("Keep a fixed cancellation fee"), max_digits=10, decimal_places=2, required=False ) keep_fee_per_ticket = forms.DecimalField( label=_("Keep a fixed cancellation fee per ticket"), help_text=_("Free tickets and add-on products are not counted"), max_digits=10, decimal_places=2, required=False ) keep_fee_percentage = forms.DecimalField( label=_("Keep a percentual cancellation fee"), max_digits=10, decimal_places=2, required=False ) keep_fees = forms.MultipleChoiceField( label=_("Keep fees"), widget=forms.CheckboxSelectMultiple, choices=[(k, v) for k, v in OrderFee.FEE_TYPES if k != OrderFee.FEE_TYPE_GIFTCARD], help_text=_('The selected types of fees will not be refunded but instead added to the cancellation fee. Fees ' 'are never refunded in when an order in an event series is only partially canceled since it ' 'consists of tickets for multiple dates.'), required=False, ) send = forms.BooleanField( label=_("Send information via email"), required=False ) send_subject = forms.CharField() send_message = forms.CharField() send_waitinglist = forms.BooleanField( label=_("Send information to waiting list"), required=False ) send_waitinglist_subject = forms.CharField() send_waitinglist_message = forms.CharField() def _set_field_placeholders(self, fn, base_parameters): phs = [ '{%s}' % p for p in sorted(get_available_placeholders(self.event, base_parameters).keys()) ] ht = _('Available placeholders: {list}').format( list=', '.join(phs) ) if self.fields[fn].help_text: self.fields[fn].help_text += ' ' + str(ht) else: self.fields[fn].help_text = ht self.fields[fn].validators.append( PlaceholderValidator(phs) ) def __init__(self, *args, **kwargs): self.event = kwargs.pop('event') kwargs.setdefault('initial', {}) kwargs['initial']['gift_card_expires'] = self.event.organizer.default_gift_card_expiry super().__init__(*args, **kwargs) self.fields['send_subject'] = I18nFormField( label=_("Subject"), required=True, widget_kwargs={'attrs': {'data-display-dependency': '#id_send'}}, initial=_('Canceled: {event}'), widget=I18nTextInput, locales=self.event.settings.get('locales'), ) self.fields['send_message'] = I18nFormField( label=_('Message'), widget=I18nTextarea, required=True, widget_kwargs={'attrs': {'data-display-dependency': '#id_send'}}, locales=self.event.settings.get('locales'), initial=LazyI18nString.from_gettext(gettext_noop( 'Hello,\n\n' 'with this email, we regret to inform you that {event} has been canceled.\n\n' 'We will refund you {refund_amount} to your original payment method.\n\n' 'You can view the current state of your order here:\n\n{url}\n\nBest regards,\n\n' 'Your {event} team' )) ) self._set_field_placeholders('send_subject', ['event_or_subevent', 'refund_amount', 'position_or_address', 'order', 'event']) self._set_field_placeholders('send_message', ['event_or_subevent', 'refund_amount', 'position_or_address', 'order', 'event']) self.fields['send_waitinglist_subject'] = I18nFormField( label=_("Subject"), required=True, initial=_('Canceled: {event}'), widget=I18nTextInput, widget_kwargs={'attrs': {'data-display-dependency': '#id_send_waitinglist'}}, locales=self.event.settings.get('locales'), ) self.fields['send_waitinglist_message'] = I18nFormField( label=_('Message'), widget=I18nTextarea, required=True, locales=self.event.settings.get('locales'), widget_kwargs={'attrs': {'data-display-dependency': '#id_send_waitinglist'}}, initial=LazyI18nString.from_gettext(gettext_noop( 'Hello,\n\n' 'with this email, we regret to inform you that {event} has been canceled.\n\n' 'You will therefore not receive a ticket from the waiting list.\n\n' 'Best regards,\n\n' 'Your {event} team' )) ) self._set_field_placeholders('send_waitinglist_subject', ['event_or_subevent', 'event']) self._set_field_placeholders('send_waitinglist_message', ['event_or_subevent', 'event']) if self.event.has_subevents: self.fields['subevent'].queryset = self.event.subevents.all() self.fields['subevent'].widget = Select2( attrs={ 'data-inverse-dependency': '#id_all_subevents', 'data-model-select2': 'event', 'data-select2-url': reverse('control:event.subevents.select2', kwargs={ 'event': self.event.slug, 'organizer': self.event.organizer.slug, }), 'data-placeholder': pgettext_lazy('subevent', 'All dates') } ) self.fields['subevent'].widget.choices = self.fields['subevent'].choices else: del self.fields['subevent'] del self.fields['all_subevents'] change_decimal_field(self.fields['keep_fee_fixed'], self.event.currency) def clean(self): d = super().clean() if d.get('subevent') and d.get('subevents_from'): raise ValidationError(pgettext_lazy('subevent', 'Please either select a specific date or a date range, not both.')) if d.get('all_subevents') and d.get('subevent_from'): raise ValidationError(pgettext_lazy('subevent', 'Please either select all dates or a date range, not both.')) if bool(d.get('subevents_from')) != bool(d.get('subevents_to')): raise ValidationError(pgettext_lazy('subevent', 'If you set a date range, please set both a start and an end.')) if self.event.has_subevents and not d['subevent'] and not d['all_subevents'] and not d.get('subevents_from'): raise ValidationError(_('Please confirm that you want to cancel ALL dates in this event series.')) return d
def __init__(self, *args, **kwargs): """ Takes two additional keyword arguments: :param cartpos: The cart position the form should be for :param event: The event this belongs to """ cartpos = self.cartpos = kwargs.pop('cartpos', None) orderpos = self.orderpos = kwargs.pop('orderpos', None) pos = cartpos or orderpos item = pos.item questions = pos.item.questions_to_ask event = kwargs.pop('event') super().__init__(*args, **kwargs) if item.admission and event.settings.attendee_names_asked: self.fields['attendee_name_parts'] = NamePartsFormField( max_length=255, required=event.settings.attendee_names_required, scheme=event.settings.name_scheme, label=_('Attendee name'), initial=(cartpos.attendee_name_parts if cartpos else orderpos.attendee_name_parts), ) if item.admission and event.settings.attendee_emails_asked: self.fields['attendee_email'] = forms.EmailField( required=event.settings.attendee_emails_required, label=_('Attendee email'), initial=(cartpos.attendee_email if cartpos else orderpos.attendee_email) ) for q in questions: # Do we already have an answer? Provide it as the initial value answers = [a for a in pos.answerlist if a.question_id == q.id] if answers: initial = answers[0] else: initial = None tz = pytz.timezone(event.settings.timezone) help_text = rich_text(q.help_text) if q.type == Question.TYPE_BOOLEAN: if q.required: # For some reason, django-bootstrap3 does not set the required attribute # itself. widget = forms.CheckboxInput(attrs={'required': 'required'}) else: widget = forms.CheckboxInput() if initial: initialbool = (initial.answer == "True") else: initialbool = False field = forms.BooleanField( label=q.question, required=q.required, help_text=help_text, initial=initialbool, widget=widget, ) elif q.type == Question.TYPE_NUMBER: field = forms.DecimalField( label=q.question, required=q.required, help_text=q.help_text, initial=initial.answer if initial else None, min_value=Decimal('0.00'), ) elif q.type == Question.TYPE_STRING: field = forms.CharField( label=q.question, required=q.required, help_text=help_text, initial=initial.answer if initial else None, ) elif q.type == Question.TYPE_TEXT: field = forms.CharField( label=q.question, required=q.required, help_text=help_text, widget=forms.Textarea, initial=initial.answer if initial else None, ) elif q.type == Question.TYPE_CHOICE: field = forms.ModelChoiceField( queryset=q.options, label=q.question, required=q.required, help_text=help_text, widget=forms.Select, empty_label='', initial=initial.options.first() if initial else None, ) elif q.type == Question.TYPE_CHOICE_MULTIPLE: field = forms.ModelMultipleChoiceField( queryset=q.options, label=q.question, required=q.required, help_text=help_text, widget=forms.CheckboxSelectMultiple, initial=initial.options.all() if initial else None, ) elif q.type == Question.TYPE_FILE: field = forms.FileField( label=q.question, required=q.required, help_text=help_text, initial=initial.file if initial else None, widget=UploadedFileWidget(position=pos, event=event, answer=initial), ) elif q.type == Question.TYPE_DATE: field = forms.DateField( label=q.question, required=q.required, help_text=help_text, initial=dateutil.parser.parse(initial.answer).date() if initial and initial.answer else None, widget=DatePickerWidget(), ) elif q.type == Question.TYPE_TIME: field = forms.TimeField( label=q.question, required=q.required, help_text=help_text, initial=dateutil.parser.parse(initial.answer).time() if initial and initial.answer else None, widget=TimePickerWidget(time_format=get_format_without_seconds('TIME_INPUT_FORMATS')), ) elif q.type == Question.TYPE_DATETIME: field = SplitDateTimeField( label=q.question, required=q.required, help_text=help_text, initial=dateutil.parser.parse(initial.answer).astimezone(tz) if initial and initial.answer else None, widget=SplitDateTimePickerWidget(time_format=get_format_without_seconds('TIME_INPUT_FORMATS')), ) field.question = q if answers: # Cache the answer object for later use field.answer = answers[0] self.fields['question_%s' % q.id] = field responses = question_form_fields.send(sender=event, position=pos) data = pos.meta_info_data for r, response in sorted(responses, key=lambda r: str(r[0])): for key, value in response.items(): # We need to be this explicit, since OrderedDict.update does not retain ordering self.fields[key] = value value.initial = data.get('question_form_data', {}).get(key)
def get_content_field(self, q, initial): tz = pytz.timezone(self.placeholder.question.event.settings.timezone) label = "" help_text = "" required = False if q.type == Question.TYPE_BOOLEAN: widget = forms.CheckboxInput() initialbool = initial == "True" return forms.BooleanField( label=label, required=False, help_text=help_text, initial=initialbool, widget=widget, ) if q.type == Question.TYPE_NUMBER: return forms.DecimalField( label=label, required=required, help_text=q.help_text, initial=initial, ) if q.type == Question.TYPE_STRING: return forms.CharField( label=label, required=required, help_text=help_text, initial=initial, ) if q.type == Question.TYPE_TEXT: return forms.CharField( label=label, required=required, help_text=help_text, widget=forms.Textarea, initial=initial, ) if q.type == Question.TYPE_COUNTRYCODE: return CountryField( countries=CachedCountries, blank=True, null=True, blank_label=" ", ).formfield( label=label, required=required, help_text=help_text, widget=forms.Select, empty_label=" ", initial=initial, ) if q.type == Question.TYPE_CHOICE: return forms.ModelChoiceField( queryset=q.options, label=label, required=required, help_text=help_text, widget=forms.Select, to_field_name="identifier", empty_label="", initial=q.options.filter( pk=initial).first() if initial else None, ) elif q.type == Question.TYPE_CHOICE_MULTIPLE: return forms.ModelMultipleChoiceField( queryset=q.options, label=label, required=required, help_text=help_text, to_field_name="identifier", widget=QuestionCheckboxSelectMultiple, initial=initial, ) elif q.type == Question.TYPE_DATE: attrs = {} if q.valid_date_min: attrs["data-min"] = q.valid_date_min.isoformat() if q.valid_date_max: attrs["data-max"] = q.valid_date_max.isoformat() field = forms.DateField( label=label, required=required, help_text=help_text, initial=dateutil.parser.parse(initial).date() if initial and initial else None, widget=DatePickerWidget(attrs), ) if q.valid_date_min: field.validators.append(MinDateValidator(q.valid_date_min)) if q.valid_date_max: field.validators.append(MaxDateValidator(q.valid_date_max)) return field elif q.type == Question.TYPE_TIME: return forms.TimeField( label=label, required=required, help_text=help_text, initial=dateutil.parser.parse(initial).time() if initial and initial else None, widget=TimePickerWidget(time_format=get_format_without_seconds( "TIME_INPUT_FORMATS")), ) elif q.type == Question.TYPE_DATETIME: field = SplitDateTimeField( label=label, required=required, help_text=help_text, initial=dateutil.parser.parse(initial).astimezone(tz) if initial else None, widget=SplitDateTimePickerWidget( time_format=get_format_without_seconds( "TIME_INPUT_FORMATS"), min_date=q.valid_datetime_min, max_date=q.valid_datetime_max, ), ) if q.valid_datetime_min: field.validators.append( MinDateTimeValidator(q.valid_datetime_min)) if q.valid_datetime_max: field.validators.append( MaxDateTimeValidator(q.valid_datetime_max)) return field elif q.type == Question.TYPE_PHONENUMBER: return PhoneNumberField( label=label, required=required, help_text=help_text, initial=initial, widget=WrappedPhoneNumberPrefixWidget(), )