Пример #1
0
def confirm_messages(sender, *args, **kwargs):
    if not sender.settings.confirm_text:
        return {}

    return {
        'confirm_text': rich_text(str(sender.settings.confirm_text))
    }
Пример #2
0
    def _get_items(self):
        items, display_add_to_cart = get_grouped_items(
            self.request.event, subevent=self.subevent, voucher=self.voucher, channel='web'
        )
        grps = []
        for cat, g in item_group_by_category(items):
            grps.append({
                'id': cat.pk if cat else None,
                'name': str(cat.name) if cat else None,
                'description': str(rich_text(cat.description, safelinks=False)) if cat and cat.description else None,
                'items': [
                    {
                        'id': item.pk,
                        'name': str(item.name),
                        'picture': get_picture(self.request.event, item.picture) if item.picture else None,
                        'description': str(rich_text(item.description, safelinks=False)) if item.description else None,
                        'has_variations': item.has_variations,
                        'require_voucher': item.require_voucher,
                        'order_min': item.min_per_order,
                        'order_max': item.order_max if not item.has_variations else None,
                        'price': price_dict(item.display_price) if not item.has_variations else None,
                        'min_price': item.min_price if item.has_variations else None,
                        'max_price': item.max_price if item.has_variations else None,
                        'free_price': item.free_price,
                        'avail': [
                            item.cached_availability[0],
                            item.cached_availability[1] if self.request.event.settings.show_quota_left else None
                        ] if not item.has_variations else None,
                        'original_price': item.original_price,
                        'variations': [
                            {
                                'id': var.id,
                                'value': str(var.value),
                                'order_max': var.order_max,
                                'description': str(rich_text(var.description, safelinks=False)) if var.description else None,
                                'price': price_dict(var.display_price),
                                'avail': [
                                    var.cached_availability[0],
                                    var.cached_availability[1] if self.request.event.settings.show_quota_left else None
                                ],
                            } for var in item.available_variations
                        ]

                    } for item in g
                ]
            })
        return grps, display_add_to_cart, len(items)
Пример #3
0
    def _get_items(self):
        items, display_add_to_cart = get_grouped_items(
            self.request.event, subevent=self.subevent, voucher=self.voucher, channel='web'
        )
        grps = []
        for cat, g in item_group_by_category(items):
            grps.append({
                'id': cat.pk if cat else None,
                'name': str(cat.name) if cat else None,
                'description': str(rich_text(cat.description, safelinks=False)) if cat and cat.description else None,
                'items': [
                    {
                        'id': item.pk,
                        'name': str(item.name),
                        'picture': get_picture(self.request.event, item.picture) if item.picture else None,
                        'description': str(rich_text(item.description, safelinks=False)) if item.description else None,
                        'has_variations': item.has_variations,
                        'require_voucher': item.require_voucher,
                        'order_min': item.min_per_order,
                        'order_max': item.order_max if not item.has_variations else None,
                        'price': price_dict(item.display_price) if not item.has_variations else None,
                        'min_price': item.min_price if item.has_variations else None,
                        'max_price': item.max_price if item.has_variations else None,
                        'free_price': item.free_price,
                        'avail': [
                            item.cached_availability[0],
                            item.cached_availability[1] if self.request.event.settings.show_quota_left else None
                        ] if not item.has_variations else None,
                        'original_price': item.original_price,
                        'variations': [
                            {
                                'id': var.id,
                                'value': str(var.value),
                                'order_max': var.order_max,
                                'description': str(rich_text(var.description, safelinks=False)) if var.description else None,
                                'price': price_dict(var.display_price),
                                'avail': [
                                    var.cached_availability[0],
                                    var.cached_availability[1] if self.request.event.settings.show_quota_left else None
                                ],
                            } for var in item.available_variations
                        ]

                    } for item in g
                ]
            })
        return grps, display_add_to_cart, len(items)
Пример #4
0
 def _comment(self, trans, comment):
     from pretix.base.templatetags.rich_text import rich_text
     trans.comment = comment
     trans.save()
     return JsonResponse({
         'status': 'ok',
         'comment': rich_text(comment),
         'plain': comment,
     })
Пример #5
0
 def _comment(self, trans, comment):
     from pretix.base.templatetags.rich_text import rich_text
     trans.comment = comment
     trans.save()
     return JsonResponse({
         'status': 'ok',
         'comment': rich_text(comment),
         'plain': comment,
     })
Пример #6
0
    def __init__(self, *args, **kwargs):
        self.event = kwargs.pop('event')
        self.request = kwargs.pop('request')
        self.all_optional = kwargs.pop('all_optional', False)
        super().__init__(*args, **kwargs)

        if self.event.settings.order_email_asked_twice:
            self.fields['email_repeat'] = forms.EmailField(
                label=_('E-mail address (repeated)'),
                help_text=_('Please enter the same email address again to make sure you typed it correctly.'),
            )

        if self.event.settings.order_phone_asked:
            if not self.initial.get('phone'):
                phone_prefix = guess_phone_prefix(self.event)
                if phone_prefix:
                    # 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.
                    self.initial['phone'] = "+{}.".format(phone_prefix)

            self.fields['phone'] = PhoneNumberField(
                label=_('Phone number'),
                required=self.event.settings.order_phone_required,
                help_text=rich_text(self.event.settings.checkout_phone_helptext),
                widget=WrappedPhoneNumberPrefixWidget()
            )

        if not self.request.session.get('iframe_session', False):
            # There is a browser quirk in Chrome that leads to incorrect initial scrolling in iframes if there
            # is an autofocus field. Who would have thought… See e.g. here:
            # https://floatboxjs.com/forum/topic.php?post=8440&usebb_sid=2e116486a9ec6b7070e045aea8cded5b#post8440
            self.fields['email'].widget.attrs['autofocus'] = 'autofocus'
        self.fields['email'].help_text = rich_text(self.event.settings.checkout_email_helptext)

        responses = contact_form_fields.send(self.event, request=self.request)
        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
        if self.all_optional:
            for k, v in self.fields.items():
                v.required = False
                v.widget.is_required = False
Пример #7
0
 def render(self, name=None, value=None, attrs=None):
     if self.id_for_label:
         label_for = format_html(' for="{}"', self.id_for_label)
     else:
         label_for = ''
     attrs = dict(self.attrs, **attrs) if attrs else self.attrs
     if self.description:
         return format_html(
             '<label{}>{} {}</label> <span class="fa fa-info-circle toggle-variation-description"></span>'
             '<div class="variation-description addon-variation-description">{}</div>',
             label_for, self.tag(attrs), self.choice_label,
             rich_text(str(self.description)))
     else:
         return format_html(
             '<label{}>{} {}</label>',
             label_for,
             self.tag(attrs),
             self.choice_label,
         )
Пример #8
0
 def payment_form_render(self, request) -> str:
     return rich_text(
         str(self.settings.get('checkout_description', as_type=LazyI18nString))
     )
Пример #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)

        if item.admission and event.settings.attendee_names_asked:
            self.fields['attendee_name_parts'] = NamePartsFormField(
                max_length=255,
                required=event.settings.attendee_names_required,
                scheme=event.settings.name_scheme,
                label=_('Attendee name'),
                initial=(cartpos.attendee_name_parts
                         if cartpos else orderpos.attendee_name_parts),
            )
        if item.admission and event.settings.attendee_emails_asked:
            self.fields['attendee_email'] = forms.EmailField(
                required=event.settings.attendee_emails_required,
                label=_('Attendee email'),
                initial=(cartpos.attendee_email
                         if cartpos else orderpos.attendee_email))

        for q in questions:
            # Do we already have an answer? Provide it as the initial value
            answers = [a for a in pos.answerlist if a.question_id == q.id]
            if answers:
                initial = answers[0]
            else:
                initial = None
            tz = pytz.timezone(event.settings.timezone)
            help_text = rich_text(q.help_text)
            label = escape(q.question)  # django-bootstrap3 calls mark_safe
            required = q.required and not self.all_optional
            if q.type == Question.TYPE_BOOLEAN:
                if q.required:
                    # For some reason, django-bootstrap3 does not set the required attribute
                    # itself.
                    widget = forms.CheckboxInput(
                        attrs={'required': 'required'})
                else:
                    widget = forms.CheckboxInput()

                if initial:
                    initialbool = (initial.answer == "True")
                else:
                    initialbool = False

                field = forms.BooleanField(
                    label=label,
                    required=required,
                    help_text=help_text,
                    initial=initialbool,
                    widget=widget,
                )
            elif q.type == Question.TYPE_NUMBER:
                field = forms.DecimalField(
                    label=label,
                    required=required,
                    help_text=q.help_text,
                    initial=initial.answer if initial else None,
                    min_value=Decimal('0.00'),
                )
            elif q.type == Question.TYPE_STRING:
                field = forms.CharField(
                    label=label,
                    required=required,
                    help_text=help_text,
                    initial=initial.answer if initial else None,
                )
            elif q.type == Question.TYPE_TEXT:
                field = forms.CharField(
                    label=label,
                    required=required,
                    help_text=help_text,
                    widget=forms.Textarea,
                    initial=initial.answer if initial else None,
                )
            elif q.type == Question.TYPE_CHOICE:
                field = forms.ModelChoiceField(
                    queryset=q.options,
                    label=label,
                    required=required,
                    help_text=help_text,
                    widget=forms.Select,
                    to_field_name='identifier',
                    empty_label='',
                    initial=initial.options.first() if initial else None,
                )
            elif q.type == Question.TYPE_CHOICE_MULTIPLE:
                field = forms.ModelMultipleChoiceField(
                    queryset=q.options,
                    label=label,
                    required=required,
                    help_text=help_text,
                    to_field_name='identifier',
                    widget=forms.CheckboxSelectMultiple,
                    initial=initial.options.all() if initial else None,
                )
            elif q.type == Question.TYPE_FILE:
                field = forms.FileField(
                    label=label,
                    required=required,
                    help_text=help_text,
                    initial=initial.file if initial else None,
                    widget=UploadedFileWidget(position=pos,
                                              event=event,
                                              answer=initial),
                )
            elif q.type == Question.TYPE_DATE:
                field = forms.DateField(
                    label=label,
                    required=required,
                    help_text=help_text,
                    initial=dateutil.parser.parse(initial.answer).date()
                    if initial and initial.answer else None,
                    widget=DatePickerWidget(),
                )
            elif q.type == Question.TYPE_TIME:
                field = forms.TimeField(
                    label=label,
                    required=required,
                    help_text=help_text,
                    initial=dateutil.parser.parse(initial.answer).time()
                    if initial and initial.answer else None,
                    widget=TimePickerWidget(
                        time_format=get_format_without_seconds(
                            'TIME_INPUT_FORMATS')),
                )
            elif q.type == Question.TYPE_DATETIME:
                field = SplitDateTimeField(
                    label=label,
                    required=required,
                    help_text=help_text,
                    initial=dateutil.parser.parse(
                        initial.answer).astimezone(tz)
                    if initial and initial.answer else None,
                    widget=SplitDateTimePickerWidget(
                        time_format=get_format_without_seconds(
                            'TIME_INPUT_FORMATS')),
                )
            field.question = q
            if answers:
                # Cache the answer object for later use
                field.answer = answers[0]

            if q.dependency_question_id:
                field.widget.attrs[
                    'data-question-dependency'] = q.dependency_question_id
                field.widget.attrs[
                    'data-question-dependency-value'] = q.dependency_value
                if q.type != 'M':
                    field.widget.attrs[
                        'required'] = q.required and not self.all_optional
                    field._required = q.required and not self.all_optional
                field.required = False

            self.fields['question_%s' % q.id] = field

        responses = question_form_fields.send(sender=event, position=pos)
        data = pos.meta_info_data
        for r, response in sorted(responses, key=lambda r: str(r[0])):
            for key, value in response.items():
                # We need to be this explicit, since OrderedDict.update does not retain ordering
                self.fields[key] = value
                value.initial = data.get('question_form_data', {}).get(key)
Пример #10
0
    def __init__(self, *args, **kwargs):
        self.event = kwargs.pop('event')
        self.channel = kwargs.pop('channel')
        customer = kwargs.pop('customer')
        super().__init__(*args, **kwargs)

        choices = [('', '')]
        items, display_add_to_cart = get_grouped_items(
            self.event,
            self.instance.subevent,
            require_seat=None,
            memberships=(customer.usable_memberships(
                for_event=self.instance.subevent or self.event,
                testmode=self.event.testmode) if customer else None),
        )
        for i in items:
            if not i.allow_waitinglist:
                continue

            if i.has_variations:
                for v in i.available_variations:
                    if v.cached_availability[0] == Quota.AVAILABILITY_OK:
                        continue
                    choices.append((f'{i.pk}-{v.pk}', f'{i.name} – {v.value}'))

            else:
                if i.cached_availability[0] == Quota.AVAILABILITY_OK:
                    continue
                choices.append((f'{i.pk}', f'{i.name}'))

        self.fields['itemvar'] = forms.ChoiceField(
            label=_('Product'),
            choices=choices,
        )

        event = self.event

        if event.settings.waiting_list_names_asked:
            self.fields['name_parts'] = NamePartsFormField(
                max_length=255,
                required=event.settings.waiting_list_names_required,
                scheme=event.settings.name_scheme,
                titles=event.settings.name_scheme_titles,
                label=_('Name'),
            )
        else:
            del self.fields['name_parts']

        if event.settings.waiting_list_phones_asked:
            if not self.initial.get('phone'):
                phone_prefix = guess_phone_prefix(event)
                if phone_prefix:
                    self.initial['phone'] = "+{}.".format(phone_prefix)

            self.fields['phone'] = PhoneNumberField(
                label=_("Phone number"),
                required=event.settings.waiting_list_phones_required,
                help_text=rich_text(
                    event.settings.waiting_list_phones_explanation_text),
                widget=WrappedPhoneNumberPrefixWidget())
        else:
            del self.fields['phone']
Пример #11
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', '')
Пример #12
0
    def _get_event_list(self, request, **kwargs):
        data = {}
        o = getattr(request, 'event', request.organizer)
        list_type = self.request.GET.get("style", o.settings.event_list_type)
        data['list_type'] = list_type

        if hasattr(self.request,
                   'event') and data['list_type'] not in ("calendar", "week"):
            # only allow list-view of more than 50 subevents if ordering is by data as this can be done in the database
            # ordering by name is currently not supported in database due to I18NField-JSON
            ordering = self.request.event.settings.get(
                'frontpage_subevent_ordering',
                default='date_ascending',
                as_type=str)
            if ordering not in ("date_ascending", "date_descending"
                                ) and self.request.event.subevents.filter(
                                    date_from__gt=now()).count() > 50:
                if self.request.event.settings.event_list_type not in (
                        "calendar", "week"):
                    self.request.event.settings.event_list_type = "calendar"
                data['list_type'] = list_type = 'calendar'

        if hasattr(self.request, 'event'):
            data['name'] = str(request.event.name)
            data['frontpage_text'] = str(
                rich_text(request.event.settings.frontpage_text,
                          safelinks=False))

        cache_key = ':'.join([
            'widget.py',
            'eventlist',
            request.organizer.slug,
            request.event.slug if hasattr(request, 'event') else '-',
            list_type,
            request.GET.urlencode(),
            get_language(),
        ])
        cached_data = cache.get(cache_key)
        if cached_data:
            return self.response(cached_data)

        if list_type == "calendar":
            self._set_month_year()
            _, ndays = calendar.monthrange(self.year, self.month)

            data['date'] = date(self.year, self.month, 1)
            if hasattr(self.request, 'event'):
                tz = pytz.timezone(self.request.event.settings.timezone)
            else:
                tz = pytz.UTC
            before = datetime(self.year, self.month, 1, 0, 0, 0,
                              tzinfo=tz) - timedelta(days=1)
            after = datetime(self.year, self.month, ndays, 0, 0, 0,
                             tzinfo=tz) + timedelta(days=1)

            ebd = defaultdict(list)

            if hasattr(self.request, 'event'):
                add_subevents_for_days(
                    filter_qs_by_attr(
                        self.request.event.subevents_annotated('web').filter(
                            event__sales_channels__contains=self.request.
                            sales_channel.identifier), self.request), before,
                    after, ebd, set(), self.request.event,
                    kwargs.get('cart_namespace'))
            else:
                timezones = set()
                add_events_for_days(
                    self.request,
                    filter_qs_by_attr(
                        Event.annotated(
                            self.request.organizer.events,
                            'web').filter(sales_channels__contains=self.
                                          request.sales_channel.identifier),
                        self.request), before, after, ebd, timezones)
                add_subevents_for_days(
                    filter_qs_by_attr(
                        SubEvent.annotated(
                            SubEvent.objects.filter(
                                event__organizer=self.request.organizer,
                                event__is_public=True,
                                event__live=True,
                                event__sales_channels__contains=self.request.
                                sales_channel.identifier).prefetch_related(
                                    'event___settings_objects',
                                    'event__organizer___settings_objects')),
                        self.request), before, after, ebd, timezones)

            data['weeks'] = weeks_for_template(ebd, self.year, self.month)
            for w in data['weeks']:
                for d in w:
                    if not d:
                        continue
                    d['events'] = self._serialize_events(d['events'] or [])
        elif list_type == "week":
            self._set_week_year()

            if hasattr(self.request, 'event'):
                tz = pytz.timezone(self.request.event.settings.timezone)
            else:
                tz = pytz.UTC

            week = isoweek.Week(self.year, self.week)
            data['week'] = [self.year, self.week]
            before = datetime(week.monday().year,
                              week.monday().month,
                              week.monday().day,
                              0,
                              0,
                              0,
                              tzinfo=tz) - timedelta(days=1)
            after = datetime(week.sunday().year,
                             week.sunday().month,
                             week.sunday().day,
                             0,
                             0,
                             0,
                             tzinfo=tz) + timedelta(days=1)

            ebd = defaultdict(list)
            if hasattr(self.request, 'event'):
                add_subevents_for_days(
                    filter_qs_by_attr(
                        self.request.event.subevents_annotated('web'),
                        self.request), before, after, ebd, set(),
                    self.request.event, kwargs.get('cart_namespace'))
            else:
                timezones = set()
                add_events_for_days(
                    self.request,
                    filter_qs_by_attr(
                        Event.annotated(self.request.organizer.events, 'web'),
                        self.request), before, after, ebd, timezones)
                add_subevents_for_days(
                    filter_qs_by_attr(
                        SubEvent.annotated(
                            SubEvent.objects.filter(
                                event__organizer=self.request.organizer,
                                event__is_public=True,
                                event__live=True,
                            ).prefetch_related(
                                'event___settings_objects',
                                'event__organizer___settings_objects')),
                        self.request), before, after, ebd, timezones)

            data['days'] = days_for_template(ebd, week)
            for d in data['days']:
                d['events'] = self._serialize_events(d['events'] or [])
        else:
            offset = int(self.request.GET.get("offset", 0))
            limit = 50
            if hasattr(self.request, 'event'):
                evs = self.request.event.subevents_sorted(
                    filter_qs_by_attr(
                        self.request.event.subevents_annotated(
                            self.request.sales_channel.identifier),
                        self.request))
                ordering = self.request.event.settings.get(
                    'frontpage_subevent_ordering',
                    default='date_ascending',
                    as_type=str)
                data['has_more_events'] = False
                if ordering in ("date_ascending", "date_descending"):
                    # fetch one more result than needed to check if more events exist
                    evs = list(evs[offset:offset + limit + 1])
                    if len(evs) > limit:
                        data['has_more_events'] = True
                        evs = evs[:limit]

                tz = pytz.timezone(request.event.settings.timezone)
                if self.request.event.settings.event_list_available_only:
                    evs = [
                        se for se in evs if not se.presale_has_ended and (
                            se.best_availability_state is not None
                            and se.best_availability_state >=
                            Quota.AVAILABILITY_RESERVED)
                    ]

                data['events'] = [{
                    'name':
                    str(ev.name),
                    'location':
                    str(ev.location),
                    'date_range':
                    self._get_date_range(ev, ev.event, tz),
                    'availability':
                    self._get_availability(ev, ev.event, tz=tz),
                    'event_url':
                    build_absolute_uri(ev.event, 'presale:event.index'),
                    'subevent':
                    ev.pk,
                } for ev in evs]
            else:
                data['events'] = []
                qs = self._get_event_queryset()
                for event in qs:
                    tz = pytz.timezone(
                        event.cache.get_or_set(
                            'timezone', lambda: event.settings.timezone))
                    if event.has_subevents:
                        dr = daterange(event.min_from.astimezone(tz),
                                       (event.max_fromto or event.max_to
                                        or event.max_from).astimezone(tz))
                        avail = {
                            'color': 'none',
                            'text': gettext('Event series')
                        }
                    else:
                        dr = self._get_date_range(event, event, tz)
                        avail = self._get_availability(event, event, tz=tz)
                    data['events'].append({
                        'name':
                        str(event.name),
                        'location':
                        str(event.location),
                        'date_range':
                        dr,
                        'availability':
                        avail,
                        'event_url':
                        build_absolute_uri(event, 'presale:event.index'),
                    })

        cache.set(cache_key, data, 30)
        # These pages are cached for a really short duration – this should make them pretty accurate, while still
        # providing some protection against burst traffic.
        return self.response(data)
Пример #13
0
    def __init__(self, *args, **kwargs):
        """
        Takes additional keyword arguments:

        :param iao: The ItemAddOn object
        :param event: The event this belongs to
        :param subevent: The event the parent cart position belongs to
        :param initial: The current set of add-ons
        :param quota_cache: A shared dictionary for quota caching
        :param item_cache: A shared dictionary for item/category caching
        """
        self.iao = kwargs.pop('iao')
        category = self.iao.addon_category
        self.event = kwargs.pop('event')
        subevent = kwargs.pop('subevent')
        current_addons = kwargs.pop('initial')
        quota_cache = kwargs.pop('quota_cache')
        item_cache = kwargs.pop('item_cache')
        self.price_included = kwargs.pop('price_included')
        self.sales_channel = kwargs.pop('sales_channel')
        self.base_position = kwargs.pop('base_position')

        super().__init__(*args, **kwargs)

        if subevent:
            item_price_override = subevent.item_price_overrides
            var_price_override = subevent.var_price_overrides
        else:
            item_price_override = {}
            var_price_override = {}

        ckey = '{}-{}'.format(subevent.pk if subevent else 0, category.pk)
        if ckey not in item_cache:
            # Get all items to possibly show
            items = category.items.filter_available(
                channel=self.sales_channel,
                allow_addons=True
            ).select_related('tax_rule').prefetch_related(
                Prefetch('quotas',
                         to_attr='_subevent_quotas',
                         queryset=self.event.quotas.filter(subevent=subevent)),
                Prefetch('variations', to_attr='available_variations',
                         queryset=ItemVariation.objects.filter(active=True, quotas__isnull=False).prefetch_related(
                             Prefetch('quotas',
                                      to_attr='_subevent_quotas',
                                      queryset=self.event.quotas.filter(subevent=subevent))
                         ).distinct()),
                'event'
            ).annotate(
                quotac=Count('quotas'),
                has_variations=Count('variations')
            ).filter(
                quotac__gt=0
            ).order_by('category__position', 'category_id', 'position', 'name')
            item_cache[ckey] = items
        else:
            items = item_cache[ckey]

        self.vars_cache = {}

        for i in items:
            if i.hidden_if_available:
                q = i.hidden_if_available.availability(_cache=quota_cache)
                if q[0] == Quota.AVAILABILITY_OK:
                    continue

            if i.has_variations:
                choices = [('', _('no selection'), '')]
                for v in i.available_variations:
                    cached_availability = v.check_quotas(subevent=subevent, _cache=quota_cache)
                    if self.event.settings.hide_sold_out and cached_availability[0] < Quota.AVAILABILITY_RESERVED:
                        continue

                    if v._subevent_quotas:
                        self.vars_cache[v.pk] = v
                        choices.append(
                            (v.pk,
                             self._label(self.event, v, cached_availability,
                                         override_price=var_price_override.get(v.pk)),
                             v.description)
                        )

                n = i.name
                if i.picture:
                    n = escape(n)
                    n += '<br>'
                    n += '<a href="{}" class="productpicture" data-title="{}" data-lightbox="{}">'.format(
                        i.picture.url, escape(escape(i.name)), i.id
                    )
                    n += '<img src="{}" alt="{}">'.format(
                        thumb(i.picture, '60x60^'),
                        escape(i.name)
                    )
                    n += '</a>'
                    n = mark_safe(n)
                field = AddOnVariationField(
                    choices=choices,
                    label=n,
                    required=False,
                    widget=AddOnRadioSelect,
                    help_text=rich_text(str(i.description)),
                    initial=current_addons.get(i.pk),
                )
                field.item = i
                if len(choices) > 1:
                    self.fields['item_%s' % i.pk] = field
            else:
                if not i._subevent_quotas:
                    continue
                cached_availability = i.check_quotas(subevent=subevent, _cache=quota_cache)
                if self.event.settings.hide_sold_out and cached_availability[0] < Quota.AVAILABILITY_RESERVED:
                    continue
                field = forms.BooleanField(
                    label=self._label(self.event, i, cached_availability,
                                      override_price=item_price_override.get(i.pk)),
                    required=False,
                    initial=i.pk in current_addons,
                    help_text=rich_text(str(i.description)),
                )
                field.item = i
                self.fields['item_%s' % i.pk] = field
Пример #14
0
def test_linkify_abs(link):
    input, output = link
    assert rich_text_snippet(input, safelinks=False) == output
    assert rich_text(input, safelinks=False) == f'<p>{output}</p>'
    assert markdown_compile_email(input) == f'<p>{output}</p>'
Пример #15
0
    def __init__(self, *args, **kwargs):
        """
        Takes additional keyword arguments:

        :param category: The category to choose from
        :param event: The event this belongs to
        :param subevent: The event the parent cart position belongs to
        :param initial: The current set of add-ons
        :param quota_cache: A shared dictionary for quota caching
        :param item_cache: A shared dictionary for item/category caching
        """
        category = kwargs.pop('category')
        event = kwargs.pop('event')
        subevent = kwargs.pop('subevent')
        current_addons = kwargs.pop('initial')
        quota_cache = kwargs.pop('quota_cache')
        item_cache = kwargs.pop('item_cache')
        self.price_included = kwargs.pop('price_included')
        self.sales_channel = kwargs.pop('sales_channel')

        super().__init__(*args, **kwargs)

        if subevent:
            item_price_override = subevent.item_price_overrides
            var_price_override = subevent.var_price_overrides
        else:
            item_price_override = {}
            var_price_override = {}

        ckey = '{}-{}'.format(subevent.pk if subevent else 0, category.pk)
        if ckey not in item_cache:
            # Get all items to possibly show
            items = category.items.filter_available(
                channel=self.sales_channel,
                allow_addons=True
            ).select_related('tax_rule').prefetch_related(
                Prefetch('quotas',
                         to_attr='_subevent_quotas',
                         queryset=event.quotas.filter(subevent=subevent)),
                Prefetch('variations', to_attr='available_variations',
                         queryset=ItemVariation.objects.filter(active=True, quotas__isnull=False).prefetch_related(
                             Prefetch('quotas',
                                      to_attr='_subevent_quotas',
                                      queryset=event.quotas.filter(subevent=subevent))
                         ).distinct()),
            ).annotate(
                quotac=Count('quotas'),
                has_variations=Count('variations')
            ).filter(
                quotac__gt=0
            ).order_by('category__position', 'category_id', 'position', 'name')
            item_cache[ckey] = items
        else:
            items = item_cache[ckey]

        for i in items:
            if i.has_variations:
                choices = [('', _('no selection'), '')]
                for v in i.available_variations:
                    cached_availability = v.check_quotas(subevent=subevent, _cache=quota_cache)
                    if v._subevent_quotas:
                        choices.append(
                            (v.pk,
                             self._label(event, v, cached_availability,
                                         override_price=var_price_override.get(v.pk)),
                             v.description)
                        )

                n = i.name
                if i.picture:
                    n = escape(n)
                    n += '<br>'
                    n += '<a href="{}" class="productpicture" data-title="{}" data-lightbox="{}">'.format(
                        i.picture.url, escape(escape(i.name)), i.id
                    )
                    n += '<img src="{}" alt="{}">'.format(
                        thumb(i.picture, '60x60^'),
                        escape(i.name)
                    )
                    n += '</a>'
                    n = mark_safe(n)
                field = AddOnVariationField(
                    choices=choices,
                    label=n,
                    required=False,
                    widget=AddOnRadioSelect,
                    help_text=rich_text(str(i.description)),
                    initial=current_addons.get(i.pk),
                )
                if len(choices) > 1:
                    self.fields['item_%s' % i.pk] = field
            else:
                if not i._subevent_quotas:
                    continue
                cached_availability = i.check_quotas(subevent=subevent, _cache=quota_cache)
                field = forms.BooleanField(
                    label=self._label(event, i, cached_availability,
                                      override_price=item_price_override.get(i.pk)),
                    required=False,
                    initial=i.pk in current_addons,
                    help_text=rich_text(str(i.description)),
                )
                self.fields['item_%s' % i.pk] = field
Пример #16
0
    def __init__(self, *args, **kwargs):
        """
        Takes additional keyword arguments:

        :param category: The category to choose from
        :param event: The event this belongs to
        :param initial: The current set of add-ons
        :param quota_cache: A shared dictionary for quota caching
        :param item_cache: A shared dictionary for item/category caching
        """
        category = kwargs.pop('category')
        event = kwargs.pop('event')
        current_addons = kwargs.pop('initial')
        quota_cache = kwargs.pop('quota_cache')
        item_cache = kwargs.pop('item_cache')

        super().__init__(*args, **kwargs)

        if category.pk not in item_cache:
            # Get all items to possibly show
            items = category.items.filter(
                Q(active=True)
                & Q(
                    Q(available_from__isnull=True)
                    | Q(available_from__lte=now()))
                & Q(
                    Q(available_until__isnull=True)
                    | Q(available_until__gte=now()))
                & Q(hide_without_voucher=False)).prefetch_related(
                    'variations__quotas',  # for .availability()
                    Prefetch('quotas', queryset=event.quotas.all()),
                    Prefetch('variations',
                             to_attr='available_variations',
                             queryset=ItemVariation.objects.filter(
                                 active=True,
                                 quotas__isnull=False).distinct()),
                ).annotate(quotac=Count('quotas'),
                           has_variations=Count('variations')).filter(
                               quotac__gt=0).order_by('category__position',
                                                      'category_id',
                                                      'position', 'name')
            item_cache[category.pk] = items
        else:
            items = item_cache[category.pk]

        for i in items:
            if i.has_variations:
                choices = [('', _('no selection'), '')]
                for v in i.available_variations:
                    cached_availability = v.check_quotas(_cache=quota_cache)
                    choices.append(
                        (v.pk, self._label(event, v, cached_availability),
                         v.description))

                field = AddOnVariationField(
                    choices=choices,
                    label=i.name,
                    required=False,
                    widget=AddOnVariationSelect,
                    help_text=rich_text(str(i.description)),
                    initial=current_addons.get(i.pk),
                )
            else:
                cached_availability = i.check_quotas(_cache=quota_cache)
                field = forms.BooleanField(
                    label=self._label(event, i, cached_availability),
                    required=False,
                    initial=i.pk in current_addons,
                    help_text=rich_text(str(i.description)),
                )

            self.fields['item_%s' % i.pk] = field
Пример #17
0
 def payment_pending_render(self, request, payment) -> str:
     return rich_text(
         str(self.settings.get('pending_description', as_type=LazyI18nString)).format_map(self.format_map(payment.order))
     )
Пример #18
0
 def payment_pending_render(self, request, payment) -> str:
     return rich_text(
         str(
             self.settings.get('pending_description',
                               as_type=LazyI18nString)).format_map(
                                   self.format_map(payment.order)))
Пример #19
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)

        if item.admission and event.settings.attendee_names_asked:
            self.fields['attendee_name_parts'] = NamePartsFormField(
                max_length=255,
                required=event.settings.attendee_names_required,
                scheme=event.settings.name_scheme,
                titles=event.settings.name_scheme_titles,
                label=_('Attendee name'),
                initial=(cartpos.attendee_name_parts
                         if cartpos else orderpos.attendee_name_parts),
            )
        if item.admission and event.settings.attendee_emails_asked:
            self.fields['attendee_email'] = forms.EmailField(
                required=event.settings.attendee_emails_required,
                label=_('Attendee email'),
                initial=(cartpos.attendee_email
                         if cartpos else orderpos.attendee_email),
                widget=forms.EmailInput(attrs={'autocomplete': 'email'}))

        for q in questions:
            # Do we already have an answer? Provide it as the initial value
            answers = [a for a in pos.answerlist if a.question_id == q.id]
            if answers:
                initial = answers[0]
            else:
                initial = None
            tz = pytz.timezone(event.settings.timezone)
            help_text = rich_text(q.help_text)
            label = escape(q.question)  # django-bootstrap3 calls mark_safe
            required = q.required and not self.all_optional
            if q.type == Question.TYPE_BOOLEAN:
                if q.required:
                    # For some reason, django-bootstrap3 does not set the required attribute
                    # itself.
                    widget = forms.CheckboxInput(
                        attrs={'required': 'required'})
                else:
                    widget = forms.CheckboxInput()

                if initial:
                    initialbool = (initial.answer == "True")
                else:
                    initialbool = False

                field = forms.BooleanField(
                    label=label,
                    required=required,
                    help_text=help_text,
                    initial=initialbool,
                    widget=widget,
                )
            elif q.type == Question.TYPE_NUMBER:
                field = forms.DecimalField(
                    label=label,
                    required=required,
                    help_text=q.help_text,
                    initial=initial.answer if initial else None,
                    min_value=Decimal('0.00'),
                )
            elif q.type == Question.TYPE_STRING:
                field = forms.CharField(
                    label=label,
                    required=required,
                    help_text=help_text,
                    initial=initial.answer if initial else None,
                )
            elif q.type == Question.TYPE_TEXT:
                field = forms.CharField(
                    label=label,
                    required=required,
                    help_text=help_text,
                    widget=forms.Textarea,
                    initial=initial.answer if initial else None,
                )
            elif q.type == Question.TYPE_COUNTRYCODE:
                field = CountryField().formfield(
                    label=label,
                    required=required,
                    help_text=help_text,
                    widget=forms.Select,
                    empty_label='',
                    initial=initial.answer if initial else None,
                )
            elif q.type == Question.TYPE_CHOICE:
                field = forms.ModelChoiceField(
                    queryset=q.options,
                    label=label,
                    required=required,
                    help_text=help_text,
                    widget=forms.Select,
                    to_field_name='identifier',
                    empty_label='',
                    initial=initial.options.first() if initial else None,
                )
            elif q.type == Question.TYPE_CHOICE_MULTIPLE:
                field = forms.ModelMultipleChoiceField(
                    queryset=q.options,
                    label=label,
                    required=required,
                    help_text=help_text,
                    to_field_name='identifier',
                    widget=forms.CheckboxSelectMultiple,
                    initial=initial.options.all() if initial else None,
                )
            elif q.type == Question.TYPE_FILE:
                field = forms.FileField(
                    label=label,
                    required=required,
                    help_text=help_text,
                    initial=initial.file if initial else None,
                    widget=UploadedFileWidget(position=pos,
                                              event=event,
                                              answer=initial),
                )
            elif q.type == Question.TYPE_DATE:
                field = forms.DateField(
                    label=label,
                    required=required,
                    help_text=help_text,
                    initial=dateutil.parser.parse(initial.answer).date()
                    if initial and initial.answer else None,
                    widget=DatePickerWidget(),
                )
            elif q.type == Question.TYPE_TIME:
                field = forms.TimeField(
                    label=label,
                    required=required,
                    help_text=help_text,
                    initial=dateutil.parser.parse(initial.answer).time()
                    if initial and initial.answer else None,
                    widget=TimePickerWidget(
                        time_format=get_format_without_seconds(
                            'TIME_INPUT_FORMATS')),
                )
            elif q.type == Question.TYPE_DATETIME:
                field = SplitDateTimeField(
                    label=label,
                    required=required,
                    help_text=help_text,
                    initial=dateutil.parser.parse(
                        initial.answer).astimezone(tz)
                    if initial and initial.answer else None,
                    widget=SplitDateTimePickerWidget(
                        time_format=get_format_without_seconds(
                            'TIME_INPUT_FORMATS')),
                )
            elif q.type == Question.TYPE_PHONENUMBER:
                babel_locale = 'en'
                # Babel, and therefore django-phonenumberfield, do not support our custom locales such das de_Informal
                if localedata.exists(get_language()):
                    babel_locale = get_language()
                elif localedata.exists(get_language()[:2]):
                    babel_locale = get_language()[:2]
                with language(babel_locale):
                    default_country = guess_country(event)
                    default_prefix = None
                    for prefix, values in _COUNTRY_CODE_TO_REGION_CODE.items():
                        if str(default_country) in values:
                            default_prefix = prefix
                    try:
                        initial = PhoneNumber().from_string(
                            initial.answer) if initial else "+{}.".format(
                                default_prefix)
                    except NumberParseException:
                        initial = None
                    field = PhoneNumberField(
                        label=label,
                        required=required,
                        help_text=help_text,
                        # We now exploit an implementation detail in PhoneNumberPrefixWidget to allow us to pass just
                        # a country code but no number as an initial value. It's a bit hacky, but should be stable for
                        # the future.
                        initial=initial,
                        widget=WrappedPhoneNumberPrefixWidget())
            field.question = q
            if answers:
                # Cache the answer object for later use
                field.answer = answers[0]

            if q.dependency_question_id:
                field.widget.attrs[
                    'data-question-dependency'] = q.dependency_question_id
                field.widget.attrs[
                    'data-question-dependency-values'] = escapejson_attr(
                        json.dumps(q.dependency_values))
                if q.type != 'M':
                    field.widget.attrs[
                        'required'] = q.required and not self.all_optional
                    field._required = q.required and not self.all_optional
                field.required = False

            self.fields['question_%s' % q.id] = field

        responses = question_form_fields.send(sender=event, position=pos)
        data = pos.meta_info_data
        for r, response in sorted(responses, key=lambda r: str(r[0])):
            for key, value in response.items():
                # We need to be this explicit, since OrderedDict.update does not retain ordering
                self.fields[key] = value
                value.initial = data.get('question_form_data', {}).get(key)

        for k, v in self.fields.items():
            if v.widget.attrs.get(
                    'autocomplete') or k == 'attendee_name_parts':
                v.widget.attrs['autocomplete'] = 'section-{} '.format(
                    self.prefix) + v.widget.attrs.get('autocomplete', '')
Пример #20
0
    def _get_event_list(self, request, **kwargs):
        data = {}
        o = getattr(request, 'event', request.organizer)
        list_type = self.request.GET.get("style", o.settings.event_list_type)
        data['list_type'] = list_type

        if hasattr(self.request,
                   'event') and data['list_type'] not in ("calendar", "week"):
            if self.request.event.subevents.filter(
                    date_from__gt=now()).count() > 50:
                if self.request.event.settings.event_list_type not in (
                        "calendar", "week"):
                    self.request.event.settings.event_list_type = "calendar"
                data['list_type'] = list_type = 'calendar'

        if hasattr(self.request, 'event'):
            data['name'] = str(request.event.name)
            data['frontpage_text'] = str(
                rich_text(request.event.settings.frontpage_text,
                          safelinks=False))

        cache_key = ':'.join([
            'widget.py',
            'eventlist',
            request.organizer.slug,
            request.event.slug if hasattr(request, 'event') else '-',
            list_type,
            request.GET.urlencode(),
            get_language(),
        ])
        cached_data = cache.get(cache_key)
        if cached_data:
            return self.response(cached_data)

        if list_type == "calendar":
            self._set_month_year()
            _, ndays = calendar.monthrange(self.year, self.month)

            data['date'] = date(self.year, self.month, 1)
            if hasattr(self.request, 'event'):
                tz = pytz.timezone(self.request.event.settings.timezone)
            else:
                tz = pytz.UTC
            before = datetime(self.year, self.month, 1, 0, 0, 0,
                              tzinfo=tz) - timedelta(days=1)
            after = datetime(self.year, self.month, ndays, 0, 0, 0,
                             tzinfo=tz) + timedelta(days=1)

            ebd = defaultdict(list)

            if hasattr(self.request, 'event'):
                add_subevents_for_days(
                    filter_qs_by_attr(
                        self.request.event.subevents_annotated('web').filter(
                            event__sales_channels__contains=self.request.
                            sales_channel.identifier), self.request), before,
                    after, ebd, set(), self.request.event,
                    kwargs.get('cart_namespace'))
            else:
                timezones = set()
                add_events_for_days(
                    self.request,
                    filter_qs_by_attr(
                        Event.annotated(
                            self.request.organizer.events,
                            'web').filter(sales_channels__contains=self.
                                          request.sales_channel.identifier),
                        self.request), before, after, ebd, timezones)
                add_subevents_for_days(
                    filter_qs_by_attr(
                        SubEvent.annotated(
                            SubEvent.objects.filter(
                                event__organizer=self.request.organizer,
                                event__is_public=True,
                                event__live=True,
                                event__sales_channels__contains=self.request.
                                sales_channel.identifier).prefetch_related(
                                    'event___settings_objects',
                                    'event__organizer___settings_objects')),
                        self.request), before, after, ebd, timezones)

            data['weeks'] = weeks_for_template(ebd, self.year, self.month)
            for w in data['weeks']:
                for d in w:
                    if not d:
                        continue
                    d['events'] = self._serialize_events(d['events'] or [])
        elif list_type == "week":
            self._set_week_year()

            if hasattr(self.request, 'event'):
                tz = pytz.timezone(self.request.event.settings.timezone)
            else:
                tz = pytz.UTC

            week = isoweek.Week(self.year, self.week)
            data['week'] = [self.year, self.week]
            before = datetime(week.monday().year,
                              week.monday().month,
                              week.monday().day,
                              0,
                              0,
                              0,
                              tzinfo=tz) - timedelta(days=1)
            after = datetime(week.sunday().year,
                             week.sunday().month,
                             week.sunday().day,
                             0,
                             0,
                             0,
                             tzinfo=tz) + timedelta(days=1)

            ebd = defaultdict(list)
            if hasattr(self.request, 'event'):
                add_subevents_for_days(
                    filter_qs_by_attr(
                        self.request.event.subevents_annotated('web'),
                        self.request), before, after, ebd, set(),
                    self.request.event, kwargs.get('cart_namespace'))
            else:
                timezones = set()
                add_events_for_days(
                    self.request,
                    filter_qs_by_attr(
                        Event.annotated(self.request.organizer.events, 'web'),
                        self.request), before, after, ebd, timezones)
                add_subevents_for_days(
                    filter_qs_by_attr(
                        SubEvent.annotated(
                            SubEvent.objects.filter(
                                event__organizer=self.request.organizer,
                                event__is_public=True,
                                event__live=True,
                            ).prefetch_related(
                                'event___settings_objects',
                                'event__organizer___settings_objects')),
                        self.request), before, after, ebd, timezones)

            data['days'] = days_for_template(ebd, week)
            for d in data['days']:
                d['events'] = self._serialize_events(d['events'] or [])
        else:
            if hasattr(self.request, 'event'):
                evs = self.request.event.subevents_sorted(
                    filter_qs_by_attr(
                        self.request.event.subevents_annotated(
                            self.request.sales_channel.identifier),
                        self.request))
                tz = pytz.timezone(request.event.settings.timezone)
                data['events'] = [{
                    'name':
                    str(ev.name),
                    'location':
                    str(ev.location),
                    'date_range':
                    self._get_date_range(ev, ev.event, tz),
                    'availability':
                    self._get_availability(ev, ev.event),
                    'event_url':
                    build_absolute_uri(ev.event, 'presale:event.index'),
                    'subevent':
                    ev.pk,
                } for ev in evs]
            else:
                data['events'] = []
                qs = self._get_event_queryset()
                for event in qs:
                    tz = pytz.timezone(
                        event.cache.get_or_set(
                            'timezone', lambda: event.settings.timezone))
                    if event.has_subevents:
                        dr = daterange(event.min_from.astimezone(tz),
                                       (event.max_fromto or event.max_to
                                        or event.max_from).astimezone(tz))
                        avail = {
                            'color': 'none',
                            'text': gettext('Event series')
                        }
                    else:
                        dr = self._get_date_range(event, event, tz)
                        avail = self._get_availability(event, event)
                    data['events'].append({
                        'name':
                        str(event.name),
                        'location':
                        str(event.location),
                        'date_range':
                        dr,
                        'availability':
                        avail,
                        'event_url':
                        build_absolute_uri(event, 'presale:event.index'),
                    })

        cache.set(cache_key, data, 30)
        # These pages are cached for a really short duration – this should make them pretty accurate, while still
        # providing some protection against burst traffic.
        return self.response(data)
Пример #21
0
 def payment_form_render(self, request) -> str:
     return rich_text(
         str(
             self.settings.get('checkout_description',
                               as_type=LazyI18nString)))
Пример #22
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')

        super().__init__(*args, **kwargs)

        if item.admission and event.settings.attendee_names_asked:
            self.fields['attendee_name_parts'] = NamePartsFormField(
                max_length=255,
                required=event.settings.attendee_names_required,
                scheme=event.settings.name_scheme,
                label=_('Attendee name'),
                initial=(cartpos.attendee_name_parts if cartpos else orderpos.attendee_name_parts),
            )
        if item.admission and event.settings.attendee_emails_asked:
            self.fields['attendee_email'] = forms.EmailField(
                required=event.settings.attendee_emails_required,
                label=_('Attendee email'),
                initial=(cartpos.attendee_email if cartpos else orderpos.attendee_email)
            )

        for q in questions:
            # Do we already have an answer? Provide it as the initial value
            answers = [a for a in pos.answerlist if a.question_id == q.id]
            if answers:
                initial = answers[0]
            else:
                initial = None
            tz = pytz.timezone(event.settings.timezone)
            help_text = rich_text(q.help_text)
            if q.type == Question.TYPE_BOOLEAN:
                if q.required:
                    # For some reason, django-bootstrap3 does not set the required attribute
                    # itself.
                    widget = forms.CheckboxInput(attrs={'required': 'required'})
                else:
                    widget = forms.CheckboxInput()

                if initial:
                    initialbool = (initial.answer == "True")
                else:
                    initialbool = False

                field = forms.BooleanField(
                    label=q.question, required=q.required,
                    help_text=help_text,
                    initial=initialbool, widget=widget,
                )
            elif q.type == Question.TYPE_NUMBER:
                field = forms.DecimalField(
                    label=q.question, required=q.required,
                    help_text=q.help_text,
                    initial=initial.answer if initial else None,
                    min_value=Decimal('0.00'),
                )
            elif q.type == Question.TYPE_STRING:
                field = forms.CharField(
                    label=q.question, required=q.required,
                    help_text=help_text,
                    initial=initial.answer if initial else None,
                )
            elif q.type == Question.TYPE_TEXT:
                field = forms.CharField(
                    label=q.question, required=q.required,
                    help_text=help_text,
                    widget=forms.Textarea,
                    initial=initial.answer if initial else None,
                )
            elif q.type == Question.TYPE_CHOICE:
                field = forms.ModelChoiceField(
                    queryset=q.options,
                    label=q.question, required=q.required,
                    help_text=help_text,
                    widget=forms.Select,
                    empty_label='',
                    initial=initial.options.first() if initial else None,
                )
            elif q.type == Question.TYPE_CHOICE_MULTIPLE:
                field = forms.ModelMultipleChoiceField(
                    queryset=q.options,
                    label=q.question, required=q.required,
                    help_text=help_text,
                    widget=forms.CheckboxSelectMultiple,
                    initial=initial.options.all() if initial else None,
                )
            elif q.type == Question.TYPE_FILE:
                field = forms.FileField(
                    label=q.question, required=q.required,
                    help_text=help_text,
                    initial=initial.file if initial else None,
                    widget=UploadedFileWidget(position=pos, event=event, answer=initial),
                )
            elif q.type == Question.TYPE_DATE:
                field = forms.DateField(
                    label=q.question, required=q.required,
                    help_text=help_text,
                    initial=dateutil.parser.parse(initial.answer).date() if initial and initial.answer else None,
                    widget=DatePickerWidget(),
                )
            elif q.type == Question.TYPE_TIME:
                field = forms.TimeField(
                    label=q.question, required=q.required,
                    help_text=help_text,
                    initial=dateutil.parser.parse(initial.answer).time() if initial and initial.answer else None,
                    widget=TimePickerWidget(time_format=get_format_without_seconds('TIME_INPUT_FORMATS')),
                )
            elif q.type == Question.TYPE_DATETIME:
                field = SplitDateTimeField(
                    label=q.question, required=q.required,
                    help_text=help_text,
                    initial=dateutil.parser.parse(initial.answer).astimezone(tz) if initial and initial.answer else None,
                    widget=SplitDateTimePickerWidget(time_format=get_format_without_seconds('TIME_INPUT_FORMATS')),
                )
            field.question = q
            if answers:
                # Cache the answer object for later use
                field.answer = answers[0]
            self.fields['question_%s' % q.id] = field

        responses = question_form_fields.send(sender=event, position=pos)
        data = pos.meta_info_data
        for r, response in sorted(responses, key=lambda r: str(r[0])):
            for key, value in response.items():
                # We need to be this explicit, since OrderedDict.update does not retain ordering
                self.fields[key] = value
                value.initial = data.get('question_form_data', {}).get(key)
Пример #23
0
    def _get_items(self):
        qs = self.request.event.items
        if 'items' in self.request.GET:
            qs = qs.filter(pk__in=self.request.GET.get('items').split(","))
        if 'categories' in self.request.GET:
            qs = qs.filter(
                category__pk__in=self.request.GET.get('categories').split(","))

        items, display_add_to_cart = get_grouped_items(
            self.request.event,
            subevent=self.subevent,
            voucher=self.voucher,
            channel=self.request.sales_channel.identifier,
            base_qs=qs)

        grps = []
        for cat, g in item_group_by_category(items):
            grps.append({
                'id':
                cat.pk if cat else None,
                'name':
                str(cat.name) if cat else None,
                'description':
                str(rich_text(cat.description, safelinks=False))
                if cat and cat.description else None,
                'items': [{
                    'id':
                    item.pk,
                    'name':
                    str(item.name),
                    'picture':
                    get_picture(self.request.event, item.picture)
                    if item.picture else None,
                    'description':
                    str(rich_text(item.description, safelinks=False))
                    if item.description else None,
                    'has_variations':
                    item.has_variations,
                    'require_voucher':
                    item.require_voucher,
                    'order_min':
                    item.min_per_order,
                    'order_max':
                    item.order_max if not item.has_variations else None,
                    'price':
                    price_dict(item, item.display_price)
                    if not item.has_variations else None,
                    'min_price':
                    item.min_price if item.has_variations else None,
                    'max_price':
                    item.max_price if item.has_variations else None,
                    'allow_waitinglist':
                    item.allow_waitinglist,
                    'free_price':
                    item.free_price,
                    'avail': [
                        item.cached_availability[0],
                        item.cached_availability[1]
                        if item.do_show_quota_left else None
                    ] if not item.has_variations else None,
                    'original_price':
                    ((item.original_price.net if self.request.event.settings.
                      display_net_prices else item.original_price.gross)
                     if item.original_price else None),
                    'variations': [{
                        'id':
                        var.id,
                        'value':
                        str(var.value),
                        'order_max':
                        var.order_max,
                        'description':
                        str(rich_text(var.description, safelinks=False))
                        if var.description else None,
                        'price':
                        price_dict(item, var.display_price),
                        'original_price':
                        ((var.original_price.net if self.request.event.settings
                          .display_net_prices else var.original_price.gross)
                         if var.original_price else None)
                        or ((item.original_price.net
                             if self.request.event.settings.display_net_prices
                             else item.original_price.gross)
                            if item.original_price else None),
                        'avail': [
                            var.cached_availability[0],
                            var.cached_availability[1]
                            if item.do_show_quota_left else None
                        ],
                    } for var in item.available_variations]
                } for item in g]
            })
        return grps, display_add_to_cart, len(items)
Пример #24
0
def confirm_messages(sender, *args, **kwargs):
    if not sender.settings.confirm_text:
        return {}

    return {'confirm_text': rich_text(str(sender.settings.confirm_text))}
Пример #25
0
    def _get_event_view(self, request, **kwargs):
        cache_key = ':'.join([
            'widget.py',
            'event',
            request.organizer.slug,
            request.event.slug,
            str(self.subevent.pk) if self.subevent else "",
            request.GET.urlencode(),
            get_language(),
            request.sales_channel.identifier,
        ])
        if "cart_id" not in request.GET:
            cached_data = cache.get(cache_key)
            if cached_data:
                return self.response(cached_data)

        data = {
            'currency':
            request.event.currency,
            'display_net_prices':
            request.event.settings.display_net_prices,
            'show_variations_expanded':
            request.event.settings.show_variations_expanded,
            'waiting_list_enabled':
            request.event.settings.waiting_list_enabled,
            'voucher_explanation_text':
            str(request.event.settings.voucher_explanation_text),
            'error':
            None,
            'cart_exists':
            False
        }

        if 'cart_id' in request.GET and CartPosition.objects.filter(
                event=request.event,
                cart_id=request.GET.get('cart_id')).exists():
            data['cart_exists'] = True

        ev = self.subevent or request.event
        data['name'] = str(ev.name)
        if self.subevent:
            data['frontpage_text'] = str(
                rich_text(self.subevent.frontpage_text, safelinks=False))
        else:
            data['frontpage_text'] = str(
                rich_text(request.event.settings.frontpage_text,
                          safelinks=False))
        data['date_range'] = self._get_date_range(ev, request.event)
        fail = False

        if not ev.presale_is_running:
            if ev.presale_has_ended:
                if request.event.settings.presale_has_ended_text:
                    data['error'] = str(
                        request.event.settings.presale_has_ended_text)
                else:
                    data['error'] = gettext(
                        'The presale period for this event is over.')
            elif request.event.settings.presale_start_show_date:
                data['error'] = gettext(
                    'The presale for this event will start on %(date)s at %(time)s.'
                ) % {
                    'date':
                    date_format(
                        ev.effective_presale_start.astimezone(
                            request.event.timezone), "SHORT_DATE_FORMAT"),
                    'time':
                    date_format(
                        ev.effective_presale_start.astimezone(
                            request.event.timezone), "TIME_FORMAT"),
                }
            else:
                data['error'] = gettext(
                    'The presale for this event has not yet started.')

        self.voucher = None
        if 'voucher' in request.GET:
            try:
                self.voucher = request.event.vouchers.get(
                    code__iexact=request.GET.get('voucher').strip())
                if self.voucher.redeemed >= self.voucher.max_usages:
                    data['error'] = error_messages['voucher_redeemed']
                    fail = True
                if self.voucher.valid_until is not None and self.voucher.valid_until < now(
                ):
                    data['error'] = error_messages['voucher_expired']
                    fail = True

                cart_id = get_or_create_cart_id(request, create=False)
                if cart_id:
                    redeemed_in_carts = CartPosition.objects.filter(
                        Q(voucher=self.voucher) & Q(event=request.event)
                        & (Q(expires__gte=now())
                           | Q(cart_id=get_or_create_cart_id(request))))
                else:
                    redeemed_in_carts = CartPosition.objects.filter(
                        Q(voucher=self.voucher) & Q(event=request.event)
                        & Q(expires__gte=now()))
                v_avail = self.voucher.max_usages - self.voucher.redeemed - redeemed_in_carts.count(
                )

                if v_avail < 1:
                    data['error'] = error_messages['voucher_redeemed']
                    fail = True
            except Voucher.DoesNotExist:
                data['error'] = error_messages['voucher_invalid']
                fail = True

        if not fail and (
                ev.presale_is_running
                or request.event.settings.show_items_outside_presale_period):
            data['items_by_category'], data['display_add_to_cart'], data[
                'itemnum'] = self._get_items()
            data['display_add_to_cart'] = data[
                'display_add_to_cart'] and ev.presale_is_running
        else:
            data['items_by_category'] = []
            data['display_add_to_cart'] = False
            data['itemnum'] = 0

        data['has_seating_plan'] = ev.seating_plan is not None

        vouchers_exist = self.request.event.get_cache().get('vouchers_exist')
        if vouchers_exist is None:
            vouchers_exist = self.request.event.vouchers.exists()
            self.request.event.get_cache().set('vouchers_exist',
                                               vouchers_exist)
        data['vouchers_exist'] = vouchers_exist

        if "cart_id" not in request.GET:
            cache.set(cache_key, data, 10)
            # These pages are cached for a really short duration – this should make them pretty accurate with
            # regards to availability display, while still providing some protection against burst traffic.
        return self.response(data)
Пример #26
0
    def __init__(self, *args, **kwargs):
        """
        Takes additional keyword arguments:

        :param category: The category to choose from
        :param event: The event this belongs to
        :param subevent: The event the parent cart position belongs to
        :param initial: The current set of add-ons
        :param quota_cache: A shared dictionary for quota caching
        :param item_cache: A shared dictionary for item/category caching
        """
        category = kwargs.pop('category')
        event = kwargs.pop('event')
        subevent = kwargs.pop('subevent')
        current_addons = kwargs.pop('initial')
        quota_cache = kwargs.pop('quota_cache')
        item_cache = kwargs.pop('item_cache')
        self.price_included = kwargs.pop('price_included')

        super().__init__(*args, **kwargs)

        if subevent:
            item_price_override = subevent.item_price_overrides
            var_price_override = subevent.var_price_overrides
        else:
            item_price_override = {}
            var_price_override = {}

        ckey = '{}-{}'.format(subevent.pk if subevent else 0, category.pk)
        if ckey not in item_cache:
            # Get all items to possibly show
            items = category.items.filter(
                Q(active=True)
                & Q(
                    Q(available_from__isnull=True)
                    | Q(available_from__lte=now()))
                & Q(
                    Q(available_until__isnull=True)
                    | Q(available_until__gte=now()))
                & Q(hide_without_voucher=False)
            ).select_related('tax_rule').prefetch_related(
                Prefetch('quotas',
                         to_attr='_subevent_quotas',
                         queryset=event.quotas.filter(subevent=subevent)),
                Prefetch('variations',
                         to_attr='available_variations',
                         queryset=ItemVariation.objects.filter(
                             active=True,
                             quotas__isnull=False).prefetch_related(
                                 Prefetch('quotas',
                                          to_attr='_subevent_quotas',
                                          queryset=event.quotas.filter(
                                              subevent=subevent))).distinct()),
            ).annotate(quotac=Count('quotas'),
                       has_variations=Count('variations')).filter(
                           quotac__gt=0).order_by('category__position',
                                                  'category_id', 'position',
                                                  'name')
            item_cache[ckey] = items
        else:
            items = item_cache[ckey]

        for i in items:
            if i.has_variations:
                choices = [('', _('no selection'), '')]
                for v in i.available_variations:
                    cached_availability = v.check_quotas(subevent=subevent,
                                                         _cache=quota_cache)
                    if v._subevent_quotas:
                        choices.append(
                            (v.pk,
                             self._label(event,
                                         v,
                                         cached_availability,
                                         override_price=var_price_override.get(
                                             v.pk)), v.description))

                field = AddOnVariationField(
                    choices=choices,
                    label=i.name,
                    required=False,
                    widget=AddOnRadioSelect,
                    help_text=rich_text(str(i.description)),
                    initial=current_addons.get(i.pk),
                )
                if len(choices) > 1:
                    self.fields['item_%s' % i.pk] = field
            else:
                if not i._subevent_quotas:
                    continue
                cached_availability = i.check_quotas(subevent=subevent,
                                                     _cache=quota_cache)
                field = forms.BooleanField(
                    label=self._label(event,
                                      i,
                                      cached_availability,
                                      override_price=item_price_override.get(
                                          i.pk)),
                    required=False,
                    initial=i.pk in current_addons,
                    help_text=rich_text(str(i.description)),
                )
                self.fields['item_%s' % i.pk] = field