Пример #1
0
class OrganizerSettingsForm(SettingsForm):
    auto_fields = [
        'contact_mail', 'imprint_url', 'organizer_info_text',
        'event_list_type', 'event_list_availability',
        'organizer_homepage_text', 'organizer_link_back',
        'organizer_logo_image_large', 'giftcard_length',
        'giftcard_expiry_years', 'locales', 'region',
        'event_team_provisioning', 'primary_color', 'theme_color_success',
        'theme_color_danger', 'theme_color_background', 'theme_round_borders',
        'primary_font'
    ]

    organizer_logo_image = ExtFileField(
        label=_('Header image'),
        ext_whitelist=(".png", ".jpg", ".gif", ".jpeg"),
        max_size=10 * 1024 * 1024,
        required=False,
        help_text=
        _('If you provide a logo image, we will by default not show your organization name '
          'in the page header. By default, we show your logo with a size of up to 1140x120 pixels. You '
          'can increase the size with the setting below. We recommend not using small details on the picture '
          'as it will be resized on smaller screens.'))
    favicon = ExtFileField(
        label=_('Favicon'),
        ext_whitelist=(".ico", ".png", ".jpg", ".gif", ".jpeg"),
        required=False,
        max_size=1 * 1024 * 1024,
        help_text=
        _('If you provide a favicon, we will show it instead of the default pretix icon. '
          'We recommend a size of at least 200x200px to accommodate most devices.'
          ))
Пример #2
0
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()]
Пример #3
0
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
    )
Пример #4
0
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'))
        )
    )
Пример #5
0
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()
        ]
Пример #6
0
 def settings_form_fields(self) -> dict:
     return OrderedDict(
         list(super().settings_form_fields.items()) + [
             ('paper_size',
              forms.ChoiceField(
                  label=_('Paper size'),
                  choices=(
                      ('A4', 'A4'),
                      ('A5', 'A5'),
                      ('B4', 'B4'),
                      ('B5', 'B5'),
                      ('letter', 'Letter'),
                      ('legal', 'Legal'),
                  ),
                  required=False
              )),
             ('orientation',
              forms.ChoiceField(
                  label=_('Paper orientation'),
                  choices=(
                      ('portrait', _('Portrait')),
                      ('landscape', _('Landscape')),
                  ),
                  required=False
              )),
             ('background',
              ExtFileField(
                  label=_('Background PDF'),
                  ext_whitelist=(".pdf", ),
                  required=False
              )),
             ('qr_x', forms.FloatField(label=_('QR-Code x position (mm)'), required=False)),
             ('qr_y', forms.FloatField(label=_('QR-Code y position (mm)'), required=False)),
             ('qr_s', forms.FloatField(label=_('QR-Code size (mm)'), required=False)),
             ('code_x', forms.FloatField(label=_('Ticket code x position (mm)'), required=False)),
             ('code_y', forms.FloatField(label=_('Ticket code y position (mm)'), required=False)),
             ('code_s', forms.FloatField(label=_('Ticket code size (mm)'), required=False)),
             ('name_x', forms.FloatField(label=_('Product name x position (mm)'), required=False)),
             ('name_y', forms.FloatField(label=_('Product name y position (mm)'), required=False)),
             ('name_s', forms.FloatField(label=_('Product name size (mm)'), required=False)),
             ('price_x', forms.FloatField(label=_('Price x position (mm)'), required=False)),
             ('price_y', forms.FloatField(label=_('Price y position (mm)'), required=False)),
             ('price_s', forms.FloatField(label=_('Price size (mm)'), required=False)),
             ('event_x', forms.FloatField(label=_('Event name x position (mm)'), required=False)),
             ('event_y', forms.FloatField(label=_('Event name y position (mm)'), required=False)),
             ('event_s', forms.FloatField(label=_('Event name size (mm)'), required=False)),
         ]
     )
Пример #7
0
class InvoiceSettingsForm(SettingsForm):
    invoice_address_asked = forms.BooleanField(
        label=_("Ask for invoice address"), required=False)
    invoice_address_required = forms.BooleanField(
        label=_("Require invoice address"),
        required=False,
        widget=forms.CheckboxInput(
            attrs={'data-checkbox-dependency': '#id_invoice_address_asked'}),
    )
    invoice_name_required = forms.BooleanField(
        label=_("Require customer name"),
        required=False,
        widget=forms.CheckboxInput(
            attrs={
                'data-checkbox-dependency': '#id_invoice_address_asked',
                'data-inverse-dependency': '#id_invoice_address_required'
            }),
    )
    invoice_address_vatid = forms.BooleanField(
        label=_("Ask for VAT ID"),
        help_text=
        _("Does only work if an invoice address is asked for. VAT ID is not required."
          ),
        widget=forms.CheckboxInput(
            attrs={'data-checkbox-dependency': '#id_invoice_address_asked'}),
        required=False)
    invoice_include_free = forms.BooleanField(
        label=_("Show free products on invoices"),
        help_text=_(
            "Note that invoices will never be generated for orders that contain only free "
            "products."),
        required=False)
    invoice_numbers_consecutive = forms.BooleanField(
        label=_("Generate invoices with consecutive numbers"),
        help_text=_(
            "If deactivated, the order code will be used in the invoice number."
        ),
        required=False)
    invoice_numbers_prefix = forms.CharField(
        label=_("Invoice number prefix"),
        help_text=
        _("This will be prepended to invoice numbers. If you leave this field empty, your event slug will "
          "be used followed by a dash. Attention: If multiple events within the same organization use the "
          "same value in this field, they will share their number range, i.e. every full number will be "
          "used at most once over all of your events. This setting only affects future invoices."
          ),
        required=False,
    )
    invoice_generate = forms.ChoiceField(
        label=_("Generate invoices"),
        required=False,
        choices=(
            ('False', _('No')),
            ('admin', _('Manually in admin panel')),
            ('user', _('Automatically on user request')),
            ('True', _('Automatically for all created orders')),
            ('paid', _('Automatically on payment')),
        ),
        help_text=_(
            "Invoices will never be automatically generated for free orders."))
    invoice_attendee_name = forms.BooleanField(
        label=_("Show attendee names on invoices"), required=False)
    invoice_email_attachment = forms.BooleanField(
        label=_("Attach invoices to emails"),
        help_text=
        _("If invoices are automatically generated for all orders, they will be attached to the order "
          "confirmation mail. If they are automatically generated on payment, they will be attached to the "
          "payment confirmation mail. If they are not automatically generated, they will not be attached "
          "to emails."),
        required=False)
    invoice_renderer = forms.ChoiceField(label=_("Invoice style"),
                                         required=True,
                                         choices=[])
    invoice_address_from = forms.CharField(
        widget=forms.Textarea(
            attrs={
                'rows':
                5,
                'placeholder':
                _('Sample Event Company\n'
                  'Albert Einstein Road 52\n'
                  '12345 Samplecity')
            }),
        required=False,
        label=_("Your address"),
        help_text=_(
            "Will be printed as the sender on invoices. Be sure to include relevant details required in "
            "your jurisdiction."))
    invoice_introductory_text = I18nFormField(
        widget=I18nTextarea,
        widget_kwargs={
            'attrs': {
                'rows':
                3,
                'placeholder':
                _('e.g. With this document, we sent you the invoice for your ticket order.'
                  )
            }
        },
        required=False,
        label=_("Introductory text"),
        help_text=_(
            "Will be printed on every invoice above the invoice rows."))
    invoice_additional_text = I18nFormField(
        widget=I18nTextarea,
        widget_kwargs={
            'attrs': {
                'rows':
                3,
                'placeholder':
                _('e.g. Thank you for your purchase! You can find more information on the event at ...'
                  )
            }
        },
        required=False,
        label=_("Additional text"),
        help_text=_(
            "Will be printed on every invoice below the invoice total."))
    invoice_footer_text = I18nFormField(
        widget=I18nTextarea,
        widget_kwargs={
            'attrs': {
                'rows':
                5,
                'placeholder':
                _('e.g. your bank details, legal details like your VAT ID, registration numbers, etc.'
                  )
            }
        },
        required=False,
        label=_("Footer"),
        help_text=
        _("Will be printed centered and in a smaller font at the end of every invoice page."
          ))
    invoice_language = forms.ChoiceField(
        widget=forms.Select,
        required=True,
        label=_("Invoice language"),
        choices=[('__user__', _('The user\'s language'))] + settings.LANGUAGES,
    )
    invoice_logo_image = ExtFileField(
        label=_('Logo image'),
        ext_whitelist=(".png", ".jpg", ".gif", ".jpeg"),
        required=False,
        help_text=_(
            'We will show your logo with a maximal height and width of 2.5 cm.'
        ))

    def __init__(self, *args, **kwargs):
        event = kwargs.get('obj')
        super().__init__(*args, **kwargs)
        self.fields['invoice_renderer'].choices = [
            (r.identifier, r.verbose_name)
            for r in event.get_invoice_renderers().values()
        ]
        self.fields['invoice_numbers_prefix'].widget.attrs[
            'placeholder'] = event.slug.upper() + '-'
Пример #8
0
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.'
          ))

    event_team_provisioning = forms.BooleanField(
        label=_('Allow creating a new team during event creation'),
        help_text=
        _('Users that do not have access to all events under this organizer, must select one of their teams '
          'to have access to the created event. This setting allows users to create an event-specified team'
          ' on-the-fly, even when they do not have \"Can change teams and permissions\" permission.'
          ),
        required=False,
    )

    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'}))
    theme_color_success = forms.CharField(
        label=_("Accent color for success"),
        help_text=_("We strongly suggest to use a shade of green."),
        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'}))
    theme_color_danger = forms.CharField(
        label=_("Accent color for errors"),
        help_text=_("We strongly suggest to use a shade of red."),
        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'}))
    theme_color_background = forms.CharField(
        label=_("Page background 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 no-contrast'}))
    theme_round_borders = forms.BooleanField(
        label=_("Use round edges"),
        required=False,
    )
    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=_('Header 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. By default, we show your logo with a size of up to 1140x120 pixels. You '
          'can increase the size with the setting below. We recommend not using small details on the picture '
          'as it will be resized on smaller screens.'))
    organizer_logo_image_large = forms.BooleanField(
        label=_('Use header image in its full size'),
        help_text=_(
            'We recommend to upload a picture at least 1170 pixels wide.'),
        required=False,
    )
    event_list_type = forms.ChoiceField(label=_('Default overview style'),
                                        choices=(
                                            ('list', _('List')),
                                            ('week', _('Week calendar')),
                                            ('calendar', _('Month calendar')),
                                        ))
    event_list_availability = forms.BooleanField(
        label=_('Show availability in event overviews'),
        help_text=
        _('If checked, the list of events will show if events are sold out. This might '
          'make for longer page loading times if you have lots of events and the shown status might be out '
          'of date for up to two minutes.'),
        required=False)
    organizer_link_back = forms.BooleanField(
        label=_('Link back to organizer overview on all event pages'),
        required=False)
    locales = forms.MultipleChoiceField(
        choices=settings.LANGUAGES,
        label=_("Use languages"),
        widget=MultipleLanguagesWidget,
        help_text=
        _('Choose all languages that your organizer homepage should be available in.'
          ))
    primary_font = forms.ChoiceField(
        label=_('Font'),
        choices=[('Open Sans', 'Open Sans')],
        widget=FontSelect,
        help_text=_('Only respected by modern browsers.'))
    favicon = ExtFileField(
        label=_('Favicon'),
        ext_whitelist=(".ico", ".png", ".jpg", ".gif", ".jpeg"),
        required=False,
        help_text=
        _('If you provide a favicon, we will show it instead of the default pretix icon. '
          'We recommend a size of at least 200x200px to accommodate most devices.'
          ))
    giftcard_length = forms.IntegerField(
        label=_('Length of gift card codes'),
        help_text=_(
            'The system generates by default {}-character long gift card codes. However, if a different length '
            'is required, it can be set here.'.format(
                settings.ENTROPY['giftcard_secret'])),
        required=False)
    giftcard_expiry_years = forms.IntegerField(
        label=_('Validity of gift card codes in years'),
        help_text=
        _('If you set a number here, gift cards will by default expire at the end of the year after this '
          'many years. If you keep it empty, gift cards do not have an explicit expiry date.'
          ),
        required=False)

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields['primary_font'].choices += [(a, {
            "title": a,
            "data": v
        }) for a, v in get_fonts().items()]
Пример #9
0
    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', '')
Пример #10
0
class InvoiceSettingsForm(SettingsForm):

    auto_fields = [
        'invoice_address_asked',
        'invoice_address_required',
        'invoice_address_vatid',
        'invoice_address_company_required',
        'invoice_address_beneficiary',
        'invoice_address_custom_field',
        'invoice_name_required',
        'invoice_address_not_asked_free',
        'invoice_include_free',
        'invoice_show_payments',
        'invoice_reissue_after_modify',
        'invoice_generate',
        'invoice_attendee_name',
        'invoice_include_expire_date',
        'invoice_numbers_consecutive',
        'invoice_numbers_prefix',
        'invoice_numbers_prefix_cancellations',
        'invoice_address_explanation_text',
        'invoice_email_attachment',
        'invoice_address_from_name',
        'invoice_address_from',
        'invoice_address_from_zipcode',
        'invoice_address_from_city',
        'invoice_address_from_country',
        'invoice_address_from_tax_id',
        'invoice_address_from_vat_id',
        'invoice_introductory_text',
        'invoice_additional_text',
        'invoice_footer_text',
        'invoice_eu_currencies',
    ]

    invoice_generate_sales_channels = forms.MultipleChoiceField(
        label=_('Generate invoices for Sales channels'),
        choices=[],
        widget=forms.CheckboxSelectMultiple,
        help_text=_("If you have enabled invoice generation in the previous setting, you can limit it here to specific "
                    "sales channels.")
    )
    invoice_renderer = forms.ChoiceField(
        label=_("Invoice style"),
        required=True,
        choices=[]
    )
    invoice_language = forms.ChoiceField(
        widget=forms.Select, required=True,
        label=_("Invoice language"),
        choices=[('__user__', _('The user\'s language'))] + settings.LANGUAGES,
    )
    invoice_logo_image = ExtFileField(
        label=_('Logo image'),
        ext_whitelist=(".png", ".jpg", ".gif", ".jpeg"),
        required=False,
        max_size=10 * 1024 * 1024,
        help_text=_('We will show your logo with a maximal height and width of 2.5 cm.')
    )

    def __init__(self, *args, **kwargs):
        event = kwargs.get('obj')
        super().__init__(*args, **kwargs)
        self.fields['invoice_renderer'].choices = [
            (r.identifier, r.verbose_name) for r in event.get_invoice_renderers().values()
        ]
        self.fields['invoice_numbers_prefix'].widget.attrs['placeholder'] = event.slug.upper() + '-'
        if event.settings.invoice_numbers_prefix:
            self.fields['invoice_numbers_prefix_cancellations'].widget.attrs['placeholder'] = event.settings.invoice_numbers_prefix
        else:
            self.fields['invoice_numbers_prefix_cancellations'].widget.attrs['placeholder'] = event.slug.upper() + '-'
        locale_names = dict(settings.LANGUAGES)
        self.fields['invoice_language'].choices = [('__user__', _('The user\'s language'))] + [(a, locale_names[a]) for a in event.settings.locales]
        self.fields['invoice_generate_sales_channels'].choices = (
            (c.identifier, c.verbose_name) for c in get_all_sales_channels().values()
        )

    def clean(self):
        data = super().clean()
        settings_dict = self.obj.settings.freeze()
        settings_dict.update(data)
        validate_settings(self.obj, data)
        return data
Пример #11
0
class EventSettingsForm(SettingsForm):
    timezone = forms.ChoiceField(
        choices=((a, a) for a in common_timezones),
        label=_("Event timezone"),
    )
    name_scheme = forms.ChoiceField(
        label=_("Name format"),
        help_text=_("This defines how pretix will ask for human names. Changing this after you already received "
                    "orders might lead to unexpected behavior when sorting or changing names."),
        required=True,
    )
    name_scheme_titles = forms.ChoiceField(
        label=_("Allowed titles"),
        help_text=_("If the naming scheme you defined above allows users to input a title, you can use this to "
                    "restrict the set of selectable titles."),
        required=False,
    )
    logo_image = ExtFileField(
        label=_('Header image'),
        ext_whitelist=(".png", ".jpg", ".gif", ".jpeg"),
        required=False,
        max_size=10 * 1024 * 1024,
        help_text=_('If you provide a logo image, we will by default not show your event name and date '
                    'in the page header. By default, we show your logo with a size of up to 1140x120 pixels. You '
                    'can increase the size with the setting below. We recommend not using small details on the picture '
                    'as it will be resized on smaller screens.')
    )
    logo_image_large = forms.BooleanField(
        label=_('Use header image in its full size'),
        help_text=_('We recommend to upload a picture at least 1170 pixels wide.'),
        required=False,
    )
    logo_show_title = forms.BooleanField(
        label=_('Show event title even if a header image is present'),
        help_text=_('The title will only be shown on the event front page.'),
        required=False,
    )
    og_image = ExtFileField(
        label=_('Social media image'),
        ext_whitelist=(".png", ".jpg", ".gif", ".jpeg"),
        required=False,
        max_size=10 * 1024 * 1024,
        help_text=_('This picture will be used as a preview if you post links to your ticket shop on social media. '
                    'Facebook advises to use a picture size of 1200 x 630 pixels, however some platforms like '
                    'WhatsApp and Reddit only show a square preview, so we recommend to make sure it still looks good '
                    'only the center square is shown. If you do not fill this, we will use the logo given above.')
    )
    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'})
    )
    theme_color_success = forms.CharField(
        label=_("Accent color for success"),
        help_text=_("We strongly suggest to use a shade of green."),
        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'})
    )
    theme_color_danger = forms.CharField(
        label=_("Accent color for errors"),
        help_text=_("We strongly suggest to use a dark shade of red."),
        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'})
    )
    theme_color_background = forms.CharField(
        label=_("Page background 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 no-contrast'})
    )
    theme_round_borders = forms.BooleanField(
        label=_("Use round edges"),
        required=False,
    )
    primary_font = forms.ChoiceField(
        label=_('Font'),
        choices=[
            ('Open Sans', 'Open Sans')
        ],
        widget=FontSelect,
        help_text=_('Only respected by modern browsers.')
    )

    auto_fields = [
        'imprint_url',
        'checkout_email_helptext',
        'presale_has_ended_text',
        'voucher_explanation_text',
        'show_dates_on_frontpage',
        'show_date_to',
        'show_times',
        'show_items_outside_presale_period',
        'display_net_prices',
        'presale_start_show_date',
        'locales',
        'locale',
        'show_quota_left',
        'waiting_list_enabled',
        'waiting_list_hours',
        'waiting_list_auto',
        'max_items_per_order',
        'reservation_time',
        'contact_mail',
        'show_variations_expanded',
        'hide_sold_out',
        'meta_noindex',
        'redirect_to_checkout_directly',
        'frontpage_subevent_ordering',
        'event_list_type',
        'frontpage_text',
        'attendee_names_asked',
        'attendee_names_required',
        'attendee_emails_asked',
        'attendee_emails_required',
        'attendee_company_asked',
        'attendee_company_required',
        'attendee_addresses_asked',
        'attendee_addresses_required',
        'confirm_text',
        'banner_text',
        'banner_text_bottom',
        'order_email_asked_twice',
        'last_order_modification_date',
    ]

    def clean(self):
        data = super().clean()
        settings_dict = self.event.settings.freeze()
        settings_dict.update(data)
        validate_settings(self.event, data)
        return data

    def __init__(self, *args, **kwargs):
        self.event = kwargs['obj']
        super().__init__(*args, **kwargs)
        self.fields['confirm_text'].widget.attrs['rows'] = '3'
        self.fields['confirm_text'].widget.attrs['placeholder'] = _(
            'e.g. I hereby confirm that I have read and agree with the event organizer\'s terms of service '
            'and agree with them.'
        )
        self.fields['name_scheme'].choices = (
            (k, _('Ask for {fields}, display like {example}').format(
                fields=' + '.join(str(vv[1]) for vv in v['fields']),
                example=v['concatenation'](v['sample'])
            ))
            for k, v in PERSON_NAME_SCHEMES.items()
        )
        self.fields['name_scheme_titles'].choices = [('', _('Free text input'))] + [
            (k, '{scheme}: {samples}'.format(
                scheme=v[0],
                samples=', '.join(v[1])
            ))
            for k, v in PERSON_NAME_TITLE_GROUPS.items()
        ]
        if not self.event.has_subevents:
            del self.fields['frontpage_subevent_ordering']
            del self.fields['event_list_type']
        self.fields['primary_font'].choices += [
            (a, {"title": a, "data": v}) for a, v in get_fonts().items()
        ]
Пример #12
0
class OrganizerSettingsForm(SettingsForm):
    timezone = forms.ChoiceField(
        choices=((a, a) for a in common_timezones),
        label=_("Default timezone"),
    )
    name_scheme = forms.ChoiceField(
        label=_("Name format"),
        help_text=
        _("This defines how pretix will ask for human names. Changing this after you already received "
          "orders might lead to unexpected behavior when sorting or changing names."
          ),
        required=True,
    )
    name_scheme_titles = forms.ChoiceField(
        label=_("Allowed titles"),
        help_text=_(
            "If the naming scheme you defined above allows users to input a title, you can use this to "
            "restrict the set of selectable titles."),
        required=False,
    )
    auto_fields = [
        'customer_accounts',
        'customer_accounts_link_by_email',
        'invoice_regenerate_allowed',
        'contact_mail',
        'imprint_url',
        'organizer_info_text',
        'event_list_type',
        'event_list_availability',
        'organizer_homepage_text',
        'organizer_link_back',
        'organizer_logo_image_large',
        'organizer_logo_image_inherit',
        'giftcard_length',
        'giftcard_expiry_years',
        'locales',
        'region',
        'meta_noindex',
        'event_team_provisioning',
        'primary_color',
        'theme_color_success',
        'theme_color_danger',
        'theme_color_background',
        'theme_round_borders',
        'primary_font',
        'privacy_url',
        'cookie_consent',
        'cookie_consent_dialog_title',
        'cookie_consent_dialog_text',
        'cookie_consent_dialog_text_secondary',
        'cookie_consent_dialog_button_yes',
        'cookie_consent_dialog_button_no',
    ]

    organizer_logo_image = ExtFileField(
        label=_('Header image'),
        ext_whitelist=(".png", ".jpg", ".gif", ".jpeg"),
        max_size=settings.FILE_UPLOAD_MAX_SIZE_IMAGE,
        required=False,
        help_text=
        _('If you provide a logo image, we will by default not show your organization name '
          'in the page header. By default, we show your logo with a size of up to 1140x120 pixels. You '
          'can increase the size with the setting below. We recommend not using small details on the picture '
          'as it will be resized on smaller screens.'))
    favicon = ExtFileField(
        label=_('Favicon'),
        ext_whitelist=(".ico", ".png", ".jpg", ".gif", ".jpeg"),
        required=False,
        max_size=settings.FILE_UPLOAD_MAX_SIZE_FAVICON,
        help_text=
        _('If you provide a favicon, we will show it instead of the default pretix icon. '
          'We recommend a size of at least 200x200px to accommodate most devices.'
          ))

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields['name_scheme'].choices = (
            (k, _('Ask for {fields}, display like {example}').format(
                fields=' + '.join(str(vv[1]) for vv in v['fields']),
                example=v['concatenation'](v['sample'])))
            for k, v in PERSON_NAME_SCHEMES.items())
        self.fields['name_scheme_titles'].choices = [
            ('', _('Free text input'))
        ] + [(k, '{scheme}: {samples}'.format(scheme=v[0],
                                              samples=', '.join(v[1])))
             for k, v in PERSON_NAME_TITLE_GROUPS.items()]
Пример #13
0
class InvoiceSettingsForm(SettingsForm):
    invoice_address_asked = forms.BooleanField(
        label=_("Ask for invoice address"), required=False)
    invoice_address_required = forms.BooleanField(
        label=_("Require invoice address"),
        required=False,
        widget=forms.CheckboxInput(
            attrs={'data-checkbox-dependency': '#id_invoice_address_asked'}),
    )
    invoice_address_vatid = forms.BooleanField(
        label=_("Ask for VAT ID"),
        help_text=
        _("Does only work if an invoice address is asked for. VAT ID is not required."
          ),
        widget=forms.CheckboxInput(
            attrs={'data-checkbox-dependency': '#id_invoice_address_asked'}),
        required=False)
    invoice_numbers_consecutive = forms.BooleanField(
        label=_("Generate invoices with consecutive numbers"),
        help_text=_(
            "If deactivated, the order code will be used in the invoice number."
        ),
        required=False)
    invoice_generate = forms.ChoiceField(
        label=_("Generate invoices"),
        required=False,
        choices=(
            ('False', _('No')),
            ('admin', _('Manually in admin panel')),
            ('user', _('Automatically on user request')),
            ('True', _('Automatically for all created orders')),
            ('paid', _('Automatically on payment')),
        ))
    invoice_address_from = forms.CharField(
        widget=forms.Textarea(attrs={'rows': 5}),
        required=False,
        label=_("Your address"),
        help_text=_(
            "Will be printed as the sender on invoices. Be sure to include relevant details required in "
            "your jurisdiction (e.g. your VAT ID)."))
    invoice_introductory_text = I18nFormField(
        widget=I18nTextarea,
        required=False,
        label=_("Introductory text"),
        help_text=_(
            "Will be printed on every invoice above the invoice rows."))
    invoice_additional_text = I18nFormField(
        widget=I18nTextarea,
        required=False,
        label=_("Additional text"),
        help_text=_(
            "Will be printed on every invoice below the invoice total."))
    invoice_footer_text = I18nFormField(
        widget=I18nTextarea,
        required=False,
        label=_("Footer"),
        help_text=
        _("Will be printed centered and in a smaller font at the end of every invoice page."
          ))
    invoice_language = forms.ChoiceField(
        widget=forms.Select,
        required=True,
        label=_("Invoice language"),
        choices=[('__user__', _('The user\'s language'))] + settings.LANGUAGES,
    )
    invoice_logo_image = ExtFileField(
        label=_('Logo image'),
        ext_whitelist=(".png", ".jpg", ".svg", ".gif", ".jpeg"),
        required=False,
        help_text=_(
            'We will show your logo with a maximal height and width of 2.5 cm.'
        ))
Пример #14
0
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.'
          ))
    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'}))
    theme_color_success = forms.CharField(
        label=_("Accent color for success"),
        help_text=_("We strongly suggest to use a shade of green."),
        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'}))
    theme_color_danger = forms.CharField(
        label=_("Accent color for errors"),
        help_text=_("We strongly suggest to use a shade of red."),
        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'))))
    event_list_availability = forms.BooleanField(
        label=_('Show availability in event overviews'),
        help_text=
        _('If checked, the list of events will show if events are sold out. This might '
          'make for longer page loading times if you have lots of events and the shown status might be out '
          'of date for up to two minutes.'),
        required=False)
    organizer_link_back = forms.BooleanField(
        label=_('Link back to organizer overview on all event pages'),
        required=False)
    locales = forms.MultipleChoiceField(
        choices=settings.LANGUAGES,
        label=_("Use languages"),
        widget=MultipleLanguagesWidget,
        help_text=
        _('Choose all languages that your organizer homepage should be available in.'
          ))
    primary_font = forms.ChoiceField(
        label=_('Font'),
        choices=[('Open Sans', 'Open Sans')],
        widget=FontSelect,
        help_text=_('Only respected by modern browsers.'))
    favicon = ExtFileField(
        label=_('Favicon'),
        ext_whitelist=(".ico", ".png", ".jpg", ".gif", ".jpeg"),
        required=False,
        help_text=
        _('If you provide a favicon, we will show it instead of the default pretix icon. '
          'We recommend a size of at least 200x200px to accomodate most devices.'
          ))

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields['primary_font'].choices += [(a, a) for a in get_fonts()]
Пример #15
0
 def settings_form_fields(self) -> dict:
     return OrderedDict(
         list(super().settings_form_fields.items()) +
         [('paper_size',
           forms.ChoiceField(label=_('Paper size'),
                             choices=(
                                 ('A4', 'A4'),
                                 ('A5', 'A5'),
                                 ('B4', 'B4'),
                                 ('B5', 'B5'),
                                 ('letter', 'Letter'),
                                 ('legal', 'Legal'),
                             ),
                             required=False)),
          ('orientation',
           forms.ChoiceField(label=_('Paper orientation'),
                             choices=(
                                 ('portrait', _('Portrait')),
                                 ('landscape', _('Landscape')),
                             ),
                             required=False)),
          ('background',
           ExtFileField(label=_('Background PDF'),
                        ext_whitelist=(".pdf", ),
                        required=False)),
          ('qr_x',
           forms.FloatField(label=_('QR-Code x position (mm)'),
                            required=False)),
          ('qr_y',
           forms.FloatField(label=_('QR-Code y position (mm)'),
                            required=False)),
          ('qr_s',
           forms.FloatField(label=_('QR-Code size (mm)'), required=False)),
          ('code_x',
           forms.FloatField(label=_('Ticket code x position (mm)'),
                            required=False)),
          ('code_y',
           forms.FloatField(label=_('Ticket code y position (mm)'),
                            required=False)),
          ('code_s',
           forms.FloatField(
               label=_('Ticket code size (mm)'),
               required=False,
               help_text=_(
                   'Visible by default, set this to 0 to hide the element.')
           )),
          ('order_x',
           forms.FloatField(label=_('Order x position (mm)'),
                            required=False)),
          ('order_y',
           forms.FloatField(label=_('Order y position (mm)'),
                            required=False)),
          ('order_s',
           forms.FloatField(
               label=_('Order size (mm)'),
               required=False,
               help_text=_(
                   'Visible by default, set this to 0 to hide the element.')
           )),
          ('name_x',
           forms.FloatField(label=_('Product name x position (mm)'),
                            required=False)),
          ('name_y',
           forms.FloatField(label=_('Product name y position (mm)'),
                            required=False)),
          ('name_s',
           forms.FloatField(
               label=_('Product name size (mm)'),
               required=False,
               help_text=_(
                   'Visible by default, set this to 0 to hide the element.')
           )),
          ('price_x',
           forms.FloatField(label=_('Price x position (mm)'),
                            required=False)),
          ('price_y',
           forms.FloatField(label=_('Price y position (mm)'),
                            required=False)),
          ('price_s',
           forms.FloatField(
               label=_('Price size (mm)'),
               required=False,
               help_text=_(
                   'Visible by default, set this to 0 to hide the element.')
           )),
          ('event_x',
           forms.FloatField(label=_('Event name x position (mm)'),
                            required=False)),
          ('event_y',
           forms.FloatField(label=_('Event name y position (mm)'),
                            required=False)),
          ('event_s',
           forms.FloatField(
               label=_('Event name size (mm)'),
               required=False,
               help_text=_(
                   'Visible by default, set this to 0 to hide the element.')
           )),
          ('attendee_x',
           forms.FloatField(label=_('Attendee name x position (mm)'),
                            required=False)),
          ('attendee_y',
           forms.FloatField(label=_('Attendee name y position (mm)'),
                            required=False)),
          ('attendee_s',
           forms.FloatField(
               label=_('Attendee name size (mm)'),
               required=False,
               help_text=
               _('Invisible by default, set this to a number greater than 0 '
                 'to show.')))])
Пример #16
0
class MailForm(forms.Form):
    recipients = forms.ChoiceField(label=_('Send email to'),
                                   widget=forms.RadioSelect,
                                   initial='orders',
                                   choices=[])
    sendto = forms.MultipleChoiceField()  # overridden later
    subject = forms.CharField(label=_("Subject"))
    message = forms.CharField(label=_("Message"))
    attachment = ExtFileField(
        label=_("Attachment"),
        required=False,
        ext_whitelist=(".png", ".jpg", ".gif", ".jpeg", ".pdf", ".txt",
                       ".docx", ".gif", ".svg", ".pptx", ".ppt", ".doc",
                       ".xlsx", ".xls", ".jfif", ".heic", ".heif", ".pages",
                       ".bmp", ".tif", ".tiff"),
        help_text=_(
            'Sending an attachment increases the chance of your email not arriving or being sorted into spam folders. We recommend only using PDFs '
            'of no more than 2 MB in size.'),
        max_size=10 * 1024 * 1024)  # TODO i18n
    items = forms.ModelMultipleChoiceField(
        widget=forms.CheckboxSelectMultiple(
            attrs={'class': 'scrolling-multiple-choice'}),
        label=_('Only send to people who bought'),
        required=True,
        queryset=Item.objects.none())
    filter_checkins = forms.BooleanField(label=_('Filter check-in status'),
                                         required=False)
    checkin_lists = SafeModelMultipleChoiceField(
        queryset=CheckinList.objects.none(),
        required=False)  # overridden later
    not_checked_in = forms.BooleanField(
        label=_("Send to customers not checked in"), required=False)
    subevent = forms.ModelChoiceField(SubEvent.objects.none(),
                                      label=_('Only send to customers of'),
                                      required=False,
                                      empty_label=pgettext_lazy(
                                          'subevent', 'All dates'))

    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):
        event = self.event = kwargs.pop('event')
        super().__init__(*args, **kwargs)

        recp_choices = [('orders', _('Everyone who created a ticket order'))]
        if event.settings.attendee_emails_asked:
            recp_choices += [
                ('attendees',
                 _('Every attendee (falling back to the order contact when no attendee email address is '
                   'given)')),
                ('both',
                 _('Both (all order contact addresses and all attendee email addresses)'
                   ))
            ]
        self.fields['recipients'].choices = recp_choices

        self.fields['subject'] = I18nFormField(
            label=_('Subject'),
            widget=I18nTextInput,
            required=True,
            locales=event.settings.get('locales'),
        )
        self.fields['message'] = I18nFormField(
            label=_('Message'),
            widget=I18nTextarea,
            required=True,
            locales=event.settings.get('locales'),
        )
        self._set_field_placeholders('subject',
                                     ['event', 'order', 'position_or_address'])
        self._set_field_placeholders('message',
                                     ['event', 'order', 'position_or_address'])
        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(
                attrs={'class': 'scrolling-multiple-choice'}),
            choices=choices)
        if not self.initial.get('sendto'):
            self.initial['sendto'] = ['p', 'n']

        self.fields['items'].queryset = event.items.all()
        if not self.initial.get('items'):
            self.initial['items'] = event.items.all()

        self.fields['checkin_lists'].queryset = event.checkin_lists.all()
        self.fields['checkin_lists'].widget = Select2Multiple(
            attrs={
                'data-model-select2':
                'generic',
                'data-select2-url':
                reverse('control:event.orders.checkinlists.select2',
                        kwargs={
                            'event': event.slug,
                            'organizer': event.organizer.slug,
                        }),
                'data-placeholder':
                _('Send to customers checked in on list'),
            })
        self.fields['checkin_lists'].widget.choices = self.fields[
            'checkin_lists'].choices
        self.fields['checkin_lists'].label = _(
            'Send to customers checked in on list')

        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']