def __init__(self, *args, **kwargs): self.obj = GlobalSettingsObject() super().__init__(*args, obj=self.obj, **kwargs) self.fields = OrderedDict([ ('footer_text', I18nFormField( widget=I18nTextInput, required=False, label=_("Additional footer text"), help_text= _("Will be included as additional text in the footer, site-wide." ))), ('footer_link', I18nFormField( widget=I18nTextInput, required=False, label=_("Additional footer link"), help_text= _("Will be included as the link in the additional footer text." ))) ]) responses = register_global_settings.send(self) for r, response in responses: for key, value in response.items(): # We need to be this explicit, since OrderedDict.update does not retain ordering self.fields[key] = value
def settings_form_fields(self): d = OrderedDict( [ ('public_name', I18nFormField( label=_('Payment method name'), widget=I18nTextInput, )), ('checkout_description', I18nFormField( label=_('Payment process description during checkout'), help_text=_('This text will be shown during checkout when the user selects this payment method. ' 'It should give a short explanation on this payment method.'), widget=I18nTextarea, )), ('email_instructions', I18nFormField( label=_('Payment process description in order confirmation emails'), help_text=_('This text will be included for the {payment_info} placeholder in order confirmation ' 'mails. It should instruct the user on how to proceed with the payment. You can use' 'the placeholders {order}, {total}, {currency} and {total_with_currency}'), widget=I18nTextarea, validators=[PlaceholderValidator(['{order}', '{total}', '{currency}', '{total_with_currency}'])], )), ('pending_description', I18nFormField( label=_('Payment process description for pending orders'), help_text=_('This text will be shown on the order confirmation page for pending orders. ' 'It should instruct the user on how to proceed with the payment. You can use' 'the placeholders {order}, {total}, {currency} and {total_with_currency}'), widget=I18nTextarea, validators=[PlaceholderValidator(['{order}', '{total}', '{currency}', '{total_with_currency}'])], )), ] + list(super().settings_form_fields.items()) ) d.move_to_end('_enabled', last=False) return d
def __init__(self, *args, **kwargs): event = kwargs.pop('event') super().__init__(*args, **kwargs) self.fields['subject'] = I18nFormField( widget=I18nTextInput, required=True, locales=event.settings.get('locales')) self.fields['message'] = I18nFormField( widget=I18nTextarea, required=True, locales=event.settings.get('locales'), help_text=_( "Available placeholders: {due_date}, {event}, {order}, {order_date}, {order_url}, " "{invoice_name}, {invoice_company}"), validators=[ PlaceholderValidator([ '{due_date}', '{event}', '{order}', '{order_date}', '{order_url}', '{invoice_name}', '{invoice_company}' ]) ]) choices = list(Order.STATUS_CHOICE) if not event.settings.get('payment_term_expire_automatically', as_type=bool): choices.append(('overdue', _('pending with payment overdue'))) self.fields['sendto'] = forms.MultipleChoiceField( label=_("Send to"), widget=forms.CheckboxSelectMultiple, choices=choices)
def __init__(self, *args, **kwargs): event = kwargs.pop('event') super().__init__(*args, **kwargs) self.fields['subject'] = I18nFormField( label=_('Subject'), widget=I18nTextInput, required=True, locales=event.settings.get('locales'), help_text= _("Available placeholders: {event}, {voucher.<tag>.<n>} where <tag> is a voucher tag and <n> is a number" ), validators=[ RegexPlaceholderValidator( ['{event}', '{voucher(\.[^}.]+(\.\d+)?)?}']) ]) self.fields['message'] = I18nFormField( label=_('Message'), widget=I18nTextarea, required=True, locales=event.settings.get('locales'), help_text= _("Available placeholders: {event}, {voucher.<tag>.<n>} where <tag> is a voucher tag and <n> is a number" ), validators=[ RegexPlaceholderValidator( ['{event}', '{voucher(\.[^}.]+(\.\d+)?)?}']) ])
def __init__(self, *args, **kwargs): event = kwargs.pop('event') super().__init__(*args, **kwargs) self.fields['subject'] = I18nFormField( label=_('Subject'), widget=I18nTextInput, required=True, locales=event.settings.get('locales'), help_text=_( "Available placeholders: {expire_date}, {event}, {code}, {date}, {url}, " "{invoice_name}, {invoice_company}"), validators=[ PlaceholderValidator([ '{expire_date}', '{event}', '{code}', '{date}', '{url}', '{invoice_name}', '{invoice_company}' ]) ]) self.fields['message'] = I18nFormField( label=_('Message'), widget=I18nTextarea, required=True, locales=event.settings.get('locales'), help_text=_( "Available placeholders: {expire_date}, {event}, {code}, {date}, {url}, " "{invoice_name}, {invoice_company}"), validators=[ PlaceholderValidator([ '{expire_date}', '{event}', '{code}', '{date}', '{url}', '{invoice_name}', '{invoice_company}' ]) ]) choices = list(Order.STATUS_CHOICE) if not event.settings.get('payment_term_expire_automatically', as_type=bool): choices.append(('overdue', _('pending with payment overdue'))) self.fields['sendto'] = forms.MultipleChoiceField( label=_("Send to customers with order status"), widget=forms.CheckboxSelectMultiple, choices=choices) self.fields['item'].queryset = event.items.all() if event.has_subevents: self.fields['subevent'].queryset = event.subevents.all() self.fields['subevent'].widget = Select2( attrs={ 'data-model-select2': 'event', 'data-select2-url': reverse('control:event.subevents.select2', kwargs={ 'event': event.slug, 'organizer': event.organizer.slug, }), 'data-placeholder': pgettext_lazy('subevent', 'Date') }) self.fields['subevent'].widget.choices = self.fields[ 'subevent'].choices else: del self.fields['subevent']
def __init__(self, *args, **kwargs): self.obj = GlobalSettingsObject() super().__init__(*args, obj=self.obj, **kwargs) self.fields = OrderedDict(list(self.fields.items()) + [ ('footer_text', I18nFormField( widget=I18nTextInput, required=False, label=_("Additional footer text"), help_text=_("Will be included as additional text in the footer, site-wide.") )), ('footer_link', I18nFormField( widget=I18nTextInput, required=False, label=_("Additional footer link"), help_text=_("Will be included as the link in the additional footer text.") )), ('banner_message', I18nFormField( widget=I18nTextarea, required=False, label=_("Global message banner"), )), ('banner_message_detail', I18nFormField( widget=I18nTextarea, required=False, label=_("Global message banner detail text"), )), ('opencagedata_apikey', SecretKeySettingsField( required=False, label=_("OpenCage API key for geocoding"), )), ('mapquest_apikey', SecretKeySettingsField( required=False, label=_("MapQuest API key for geocoding"), )), ('leaflet_tiles', forms.CharField( required=False, label=_("Leaflet tiles URL pattern"), help_text=_("e.g. {sample}").format(sample="https://a.tile.openstreetmap.org/{z}/{x}/{y}.png") )), ('leaflet_tiles_attribution', forms.CharField( required=False, label=_("Leaflet tiles attribution"), help_text=_("e.g. {sample}").format(sample='© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors') )), ]) responses = register_global_settings.send(self) 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 self.fields['banner_message'].widget.attrs['rows'] = '2' self.fields['banner_message_detail'].widget.attrs['rows'] = '3'
def test_widget_empty(): f = I18nFormField(widget=I18nTextInput, required=False, localize=True) rendered = f.widget.render('foo', []) tree = html5parser.fromstring(rendered) assert tree[0].attrib == {'lang': 'de', 'name': 'foo_0', 'type': 'text'} assert tree[1].attrib == {'lang': 'en', 'name': 'foo_1', 'type': 'text'} assert tree[2].attrib == {'lang': 'fr', 'name': 'foo_2', 'type': 'text'}
class QuickSetupProductForm(I18nForm): name = I18nFormField( max_length=255, label=_("Product name"), widget=I18nTextInput ) default_price = forms.DecimalField( label=_("Price (optional)"), max_digits=7, decimal_places=2, required=False, localize=True, widget=forms.TextInput( attrs={ 'placeholder': _('Free') } ), ) quota = forms.IntegerField( label=_("Quantity available"), min_value=0, widget=forms.NumberInput( attrs={ 'placeholder': '∞' } ), initial=100, required=False )
class QuestionForm(I18nModelForm): question = I18nFormField( label=_("Question"), widget_kwargs={'attrs': {'rows': 5}}, widget=I18nTextarea ) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.fields['items'].queryset = self.instance.event.items.all() self.fields['identifier'].required = False class Meta: model = Question localized_fields = '__all__' fields = [ 'question', 'help_text', 'type', 'required', 'ask_during_checkin', 'identifier', 'items' ] widgets = { 'items': forms.CheckboxSelectMultiple( attrs={'class': 'scrolling-multiple-choice'} ), }
class DisplaySettingsForm(SettingsForm): primary_color = forms.CharField( label=_("Primary color"), required=False, validators=[ RegexValidator( regex='^#[0-9a-fA-F]{6}$', message= _('Please enter the hexadecimal code of a color, e.g. #990000.' )) ], widget=forms.TextInput(attrs={'class': 'colorpickerfield'})) logo_image = ExtFileField( label=_('Logo image'), ext_whitelist=(".png", ".jpg", ".gif", ".jpeg"), required=False, help_text= _('If you provide a logo image, we will by default not show your events name and date ' 'in the page header. We will show your logo with a maximal height of 120 pixels.' )) primary_font = forms.ChoiceField( label=_('Font'), choices=[('Open Sans', 'Open Sans')], help_text=_('Only respected by modern browsers.')) frontpage_text = I18nFormField(label=_("Frontpage text"), required=False, widget=I18nTextarea) show_variations_expanded = forms.BooleanField( label=_("Show variations of a product expanded by default"), required=False) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.fields['primary_font'].choices += [(a, a) for a in get_fonts()]
class DisplaySettingsForm(SettingsForm): primary_color = forms.CharField( label=_("Primary color"), required=False, validators=[ RegexValidator(regex='^#[0-9a-fA-F]{6}$', message=_('Please enter the hexadecimal code of a color, e.g. #990000.')) ], widget=forms.TextInput(attrs={'class': 'colorpickerfield'}) ) logo_image = ExtFileField( label=_('Logo image'), ext_whitelist=(".png", ".jpg", ".svg", ".gif", ".jpeg"), required=False, help_text=_('If you provide a logo image, we will by default not show your events name and date ' 'in the page header. We will show your logo with a maximal height of 120 pixels.') ) frontpage_text = I18nFormField( label=_("Frontpage text"), required=False, widget=I18nTextarea ) show_variations_expanded = forms.BooleanField( label=_("Show variations of a product expanded by default"), required=False )
def test_require_all_fields_limited_locales(): f = I18nFormField(widget=I18nTextInput, require_all_fields=True, locales=['de', 'fr']) assert f.clean(['A', 'B']) with pytest.raises(ValidationError): f.clean(['A', '']) with pytest.raises(ValidationError): f.clean(['', 'B'])
def test_limited_locales(): f = I18nFormField(widget=I18nTextInput, locales=['de', 'fr']) assert len(f.fields) == 2 assert f.clean(['A', 'B']) == LazyI18nString({ 'de': 'A', 'fr': 'B' }) assert f.widget.enabled_locales == ['de', 'fr']
def test_field_defaults(): f = I18nFormField(widget=I18nTextInput) assert len(f.fields) == 3 assert f.clean(LazyI18nString('Foo')) == LazyI18nString('Foo') assert f.clean(['A', 'B', 'C']) == LazyI18nString({ 'de': 'A', 'en': 'B', 'fr': 'C' })
class OrganizerSettingsForm(SettingsForm): locales = forms.MultipleChoiceField( choices=settings.LANGUAGES, label=_("Use languages"), widget=forms.CheckboxSelectMultiple, help_text=_('Choose all languages that your organizer homepage should be available in.') ) organizer_homepage_text = I18nFormField( label=_('Homepage text'), required=False, widget=I18nTextarea, help_text=_('This will be displayed on the organizer homepage.') ) organizer_logo_image = ExtFileField( label=_('Logo image'), ext_whitelist=(".png", ".jpg", ".svg", ".gif", ".jpeg"), required=False, help_text=_('If you provide a logo image, we will by default not show your organization name ' 'in the page header. We will show your logo with a maximal height of 120 pixels.') ) event_list_type = forms.ChoiceField( label=_('Event overview stile'), choices=( ('list', _('List')), ('calendar', _('Calendar')) ) )
class QuestionForm(I18nModelForm): question = I18nFormField( label=_("Question"), widget_kwargs={'attrs': {'rows': 2}}, widget=I18nTextarea ) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.fields['items'].queryset = self.instance.event.items.all() self.fields['dependency_question'].queryset = self.instance.event.questions.filter( type__in=(Question.TYPE_BOOLEAN, Question.TYPE_CHOICE, Question.TYPE_CHOICE_MULTIPLE) ) if self.instance.pk: self.fields['dependency_question'].queryset = self.fields['dependency_question'].queryset.exclude( pk=self.instance.pk ) self.fields['identifier'].required = False self.fields['help_text'].widget.attrs['rows'] = 3 def clean_dependency_question(self): dep = val = self.cleaned_data.get('dependency_question') if dep: seen_ids = {self.instance.pk} if self.instance else set() while dep: if dep.pk in seen_ids: raise ValidationError(_('Circular dependency between questions detected.')) seen_ids.add(dep.pk) dep = dep.dependency_question return val def clean(self): d = super().clean() if d.get('dependency_question') and not d.get('dependency_value'): raise ValidationError({'dependency_value': [_('This field is required')]}) if d.get('dependency_question') and d.get('ask_during_checkin'): raise ValidationError(_('Dependencies between questions are not supported during check-in.')) return d class Meta: model = Question localized_fields = '__all__' fields = [ 'question', 'help_text', 'type', 'required', 'ask_during_checkin', 'identifier', 'items', 'dependency_question', 'dependency_value' ] widgets = { 'items': forms.CheckboxSelectMultiple( attrs={'class': 'scrolling-multiple-choice'} ), 'dependency_value': forms.Select, }
class OrganizerSettingsForm(SettingsForm): organizer_info_text = I18nFormField( label=_('Info text'), required=False, widget=I18nTextarea, help_text=_('Not displayed anywhere by default, but if you want to, you can use this e.g. in ticket templates.') )
def test_widget_required(): f = I18nFormField(widget=I18nTextInput, required=True, localize=True) rendered = f.widget.render('foo', LazyI18nString({ 'de': 'Hallo', 'en': 'Hello' })) assert 'required' not in rendered
def test_require_all_fields(): f = I18nFormField(widget=I18nTextInput, require_all_fields=True) assert f.clean(['A', 'B', 'C']) with pytest.raises(ValidationError): f.clean(['A', '', '']) with pytest.raises(ValidationError): f.clean(['', 'B', '']) with pytest.raises(ValidationError): f.clean(['', '', 'C'])
def test_custom_id(): f = I18nFormField(widget=I18nTextInput, required=True, localize=True) rendered = f.widget.render('foo', LazyI18nString({ 'de': 'Hallo', 'en': 'Hello' }), attrs={'id': 'bla'}) assert 'id="bla_0"' in rendered assert 'id="bla_1"' in rendered
class GDPRSettingsForm(SettingsForm): billetaarhusgdpr_message = I18nFormField( label=_("GDPR message"), help_text=_("The GDPR message will be shown before the user registers information in the system."), required=True, widget=I18nTextarea ) billetaarhusgdpr_consent_text = I18nFormField( label=_('GDPR consent text'), help_text=_('The GDPR consent text must be accepted by the user before a purchase is possible.'), required=True, widget=I18nTextarea ) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.fields['billetaarhusgdpr_message'].widget.attrs['rows'] = '3' # self.fields['billetaarhusgdpr_message'].widget.attrs['placeholder'] = _('GDPR message placeholder') self.fields['billetaarhusgdpr_consent_text'].widget.attrs['rows'] = '3'
def test_widget_enabled_locales(): f = I18nFormField(widget=I18nTextInput, required=False) f.widget.enabled_locales = ['de', 'fr'] rendered = f.widget.render('foo', LazyI18nString({'de': 'Hallo', 'en': 'Hello'})) tree = etree.parse(StringIO(rendered)) assert tree.getroot().findall('input')[0].attrib == { 'lang': 'de', 'name': 'foo_0', 'type': 'text', 'value': 'Hallo' } assert tree.getroot().findall('input')[1].attrib == { 'lang': 'fr', 'name': 'foo_2', 'type': 'text' }
def test_widget_enabled_locales(): f = I18nFormField(widget=I18nTextInput, required=False) f.widget.enabled_locales = ['de', 'fr'] rendered = f.widget.render('foo', LazyI18nString({'de': 'Hallo', 'en': 'Hello'})) tree = html5parser.fromstring(rendered) assert tree[0].attrib == { 'lang': 'de', 'name': 'foo_0', 'type': 'text', 'value': 'Hallo' } assert tree[1].attrib == { 'lang': 'fr', 'name': 'foo_2', 'type': 'text' }
class PurpleSettingsForm(SettingsForm): block_multisubevent_checkout = forms.BooleanField(label=_('Block checkout with positions for multiple subevents'), required=False) onpremise_contact_availability = forms.ChoiceField( label=_("Let customers provide emergency contact information"), choices = [('never', _("Never")), ('optional', _("Optional")), ('always', _("Always")), ], ) show_downloadarea = forms.BooleanField(label=_('Show download area to customer'), help_text=_("In the download area, all tickets of that order can be downloaded without restriction."), required=False) show_downloadarea_control = forms.BooleanField(label=_('Show download area in administrive interface'), required=False) downloadarea_heading = I18nFormField( widget=I18nTextInput, required=False, label=_("Download area heading"), ) downloadarea_text = I18nFormField( widget=I18nTextInput, required=False, label=_("Download area text"), )
def test_widget_empty(): f = I18nFormField(widget=I18nTextInput, required=False, localize=True) rendered = f.widget.render('foo', []) tree = etree.parse(StringIO(rendered)) assert tree.getroot().findall('input')[0].attrib == { 'lang': 'de', 'name': 'foo_0', 'type': 'text' } assert tree.getroot().findall('input')[1].attrib == { 'lang': 'en', 'name': 'foo_1', 'type': 'text' } assert tree.getroot().findall('input')[2].attrib == { 'lang': 'fr', 'name': 'foo_2', 'type': 'text' }
class AutoschedSettingsForm(SettingsForm): vacc_autosched_mail = forms.BooleanField( label=_("Send email if second dose has been scheduled"), required=False, ) vacc_autosched_subject = I18nFormField( label=_("Email Subject"), required=True, widget=I18nTextInput, ) vacc_autosched_body = I18nFormField( label=_("Email Body"), required=True, widget=I18nTextarea, ) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.event = kwargs.pop("obj") self._set_field_placeholders("vacc_autosched_subject", ["event"], ["{scheduled_datetime}"]) self._set_field_placeholders("vacc_autosched_body", ["event"], ["{scheduled_datetime}"]) def _set_field_placeholders(self, fn, base_parameters, extras=[]): phs = [ "{%s}" % p for p in sorted( get_available_placeholders(self.event, base_parameters).keys()) ] + extras 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))
class TelephoneFieldSettingsForm(SettingsForm): telephone_field_required = forms.BooleanField( label=_("Require phone number"), help_text=_( "If this is not checked, entering a phone number is optional."), required=False, ) telephone_field_help_text = I18nFormField( label=_("Help text"), help_text=_( "This will be shown below the telephone field. You could use it to indicate why you need the " "number or what you will use it for."), required=False, widget=I18nTextInput)
class OrganizerDisplaySettingsForm(SettingsForm): primary_color = forms.CharField( label=_("Primary color"), required=False, validators=[ RegexValidator(regex='^#[0-9a-fA-F]{6}$', message=_('Please enter the hexadecimal code of a color, e.g. #990000.')) ], widget=forms.TextInput(attrs={'class': 'colorpickerfield'}) ) organizer_homepage_text = I18nFormField( label=_('Homepage text'), required=False, widget=I18nTextarea, help_text=_('This will be displayed on the organizer homepage.') ) organizer_logo_image = ExtFileField( label=_('Logo image'), ext_whitelist=(".png", ".jpg", ".gif", ".jpeg"), required=False, help_text=_('If you provide a logo image, we will by default not show your organization name ' 'in the page header. We will show your logo with a maximal height of 120 pixels.') ) event_list_type = forms.ChoiceField( label=_('Default overview style'), choices=( ('list', _('List')), ('calendar', _('Calendar')) ) ) locales = forms.MultipleChoiceField( choices=settings.LANGUAGES, label=_("Use languages"), widget=forms.CheckboxSelectMultiple, help_text=_('Choose all languages that your organizer homepage should be available in.') ) primary_font = forms.ChoiceField( label=_('Font'), choices=[ ('Open Sans', 'Open Sans') ], help_text=_('Only respected by modern browsers.') ) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.fields['primary_font'].choices += [ (a, a) for a in get_fonts() ]
class NewsletterSettingsForm(SettingsForm): newsletter_ml_subscribe_address = forms.EmailField( label=_("Subscribe address"), required=False, ) newsletter_ml_add_automatically = forms.BooleanField( label=_("Add emails to the list without asking users in the frontend"), help_text=_("Not recommended, might be considered illegal/unfair business practice in your legislation."), required=False, ) newsletter_ml_text = I18nFormField( label=_("Checkbox label"), required=True, widget=I18nTextInput, )
def __init__(self, *args, **kwargs): self.obj = GlobalSettingsObject() super().__init__(*args, obj=self.obj, **kwargs) self.fields = OrderedDict([ ('footer_text', I18nFormField( widget=I18nTextInput, required=False, label=_("Additional footer text"), help_text=_("Will be included as additional text in the footer, site-wide.") )), ('footer_link', I18nFormField( widget=I18nTextInput, required=False, label=_("Additional footer link"), help_text=_("Will be included as the link in the additional footer text.") )), ('banner_message', I18nFormField( widget=I18nTextarea, required=False, label=_("Global message banner"), )), ('banner_message_detail', I18nFormField( widget=I18nTextarea, required=False, label=_("Global message banner detail text"), )), ]) responses = register_global_settings.send(self) 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 self.fields['banner_message'].widget.attrs['rows'] = '2' self.fields['banner_message_detail'].widget.attrs['rows'] = '3'