def confirm_messages(sender, *args, **kwargs): if not sender.settings.confirm_text: return {} return { 'confirm_text': rich_text(str(sender.settings.confirm_text)) }
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)
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)
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, })
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, })
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
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, )
def payment_form_render(self, request) -> str: return rich_text( str(self.settings.get('checkout_description', as_type=LazyI18nString)) )
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)
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']
def __init__(self, *args, **kwargs): """ Takes two additional keyword arguments: :param cartpos: The cart position the form should be for :param event: The event this belongs to """ cartpos = self.cartpos = kwargs.pop('cartpos', None) orderpos = self.orderpos = kwargs.pop('orderpos', None) pos = cartpos or orderpos item = pos.item questions = pos.item.questions_to_ask event = kwargs.pop('event') self.all_optional = kwargs.pop('all_optional', False) super().__init__(*args, **kwargs) add_fields = {} if item.admission and event.settings.attendee_names_asked: add_fields['attendee_name_parts'] = NamePartsFormField( max_length=255, required=event.settings.attendee_names_required and not self.all_optional, scheme=event.settings.name_scheme, titles=event.settings.name_scheme_titles, label=_('Attendee name'), initial=(cartpos.attendee_name_parts if cartpos else orderpos.attendee_name_parts), ) if item.admission and event.settings.attendee_emails_asked: add_fields['attendee_email'] = forms.EmailField( required=event.settings.attendee_emails_required and not self.all_optional, label=_('Attendee email'), initial=(cartpos.attendee_email if cartpos else orderpos.attendee_email), widget=forms.EmailInput(attrs={'autocomplete': 'email'})) if item.admission and event.settings.attendee_company_asked: add_fields['company'] = forms.CharField( required=event.settings.attendee_company_required and not self.all_optional, label=_('Company'), max_length=255, initial=(cartpos.company if cartpos else orderpos.company), ) if item.admission and event.settings.attendee_addresses_asked: add_fields['street'] = forms.CharField( required=event.settings.attendee_addresses_required and not self.all_optional, label=_('Address'), widget=forms.Textarea( attrs={ 'rows': 2, 'placeholder': _('Street and Number'), 'autocomplete': 'street-address' }), initial=(cartpos.street if cartpos else orderpos.street), ) add_fields['zipcode'] = forms.CharField( required=event.settings.attendee_addresses_required and not self.all_optional, max_length=30, label=_('ZIP code'), initial=(cartpos.zipcode if cartpos else orderpos.zipcode), widget=forms.TextInput(attrs={ 'autocomplete': 'postal-code', }), ) add_fields['city'] = forms.CharField( required=event.settings.attendee_addresses_required and not self.all_optional, label=_('City'), max_length=255, initial=(cartpos.city if cartpos else orderpos.city), widget=forms.TextInput(attrs={ 'autocomplete': 'address-level2', }), ) country = (cartpos.country if cartpos else orderpos.country) or guess_country(event) add_fields['country'] = CountryField( countries=CachedCountries).formfield( required=event.settings.attendee_addresses_required and not self.all_optional, label=_('Country'), initial=country, widget=forms.Select(attrs={ 'autocomplete': 'country', }), ) c = [('', pgettext_lazy('address', 'Select state'))] fprefix = str( self.prefix ) + '-' if self.prefix is not None and self.prefix != '-' else '' cc = None if fprefix + 'country' in self.data: cc = str(self.data[fprefix + 'country']) elif country: cc = str(country) if cc and cc in COUNTRIES_WITH_STATE_IN_ADDRESS: types, form = COUNTRIES_WITH_STATE_IN_ADDRESS[cc] statelist = [ s for s in pycountry.subdivisions.get(country_code=cc) if s.type in types ] c += sorted([(s.code[3:], s.name) for s in statelist], key=lambda s: s[1]) elif fprefix + 'state' in self.data: self.data = self.data.copy() del self.data[fprefix + 'state'] add_fields['state'] = forms.ChoiceField( label=pgettext_lazy('address', 'State'), required=False, choices=c, widget=forms.Select(attrs={ 'autocomplete': 'address-level1', }), ) add_fields['state'].widget.is_required = True field_positions = list([(n, event.settings.system_question_order.get( n if n != 'state' else 'country', 0)) for n in add_fields.keys()]) for q in questions: # Do we already have an answer? Provide it as the initial value answers = [a for a in pos.answerlist if a.question_id == q.id] if answers: initial = answers[0] else: initial = None tz = pytz.timezone(event.settings.timezone) help_text = rich_text(q.help_text) label = escape(q.question) # django-bootstrap3 calls mark_safe required = q.required and not self.all_optional if q.type == Question.TYPE_BOOLEAN: if q.required: # For some reason, django-bootstrap3 does not set the required attribute # itself. widget = forms.CheckboxInput( attrs={'required': 'required'}) else: widget = forms.CheckboxInput() if initial: initialbool = (initial.answer == "True") else: initialbool = False field = forms.BooleanField( label=label, required=required, help_text=help_text, initial=initialbool, widget=widget, ) elif q.type == Question.TYPE_NUMBER: field = forms.DecimalField( label=label, required=required, min_value=q.valid_number_min or Decimal('0.00'), max_value=q.valid_number_max, help_text=q.help_text, initial=initial.answer if initial else None, ) elif q.type == Question.TYPE_STRING: field = forms.CharField( label=label, required=required, help_text=help_text, initial=initial.answer if initial else None, ) elif q.type == Question.TYPE_TEXT: field = forms.CharField( label=label, required=required, help_text=help_text, widget=forms.Textarea, initial=initial.answer if initial else None, ) elif q.type == Question.TYPE_COUNTRYCODE: field = CountryField( countries=CachedCountries, blank=True, null=True, blank_label=' ', ).formfield( label=label, required=required, help_text=help_text, widget=forms.Select, empty_label=' ', initial=initial.answer if initial else (guess_country(event) if required else None), ) elif q.type == Question.TYPE_CHOICE: field = forms.ModelChoiceField( queryset=q.options, label=label, required=required, help_text=help_text, widget=forms.Select, to_field_name='identifier', empty_label='', initial=initial.options.first() if initial else None, ) elif q.type == Question.TYPE_CHOICE_MULTIPLE: field = forms.ModelMultipleChoiceField( queryset=q.options, label=label, required=required, help_text=help_text, to_field_name='identifier', widget=QuestionCheckboxSelectMultiple, initial=initial.options.all() if initial else None, ) elif q.type == Question.TYPE_FILE: field = ExtFileField( label=label, required=required, help_text=help_text, initial=initial.file if initial else None, widget=UploadedFileWidget(position=pos, event=event, answer=initial), ext_whitelist=(".png", ".jpg", ".gif", ".jpeg", ".pdf", ".txt", ".docx", ".gif", ".svg", ".pptx", ".ppt", ".doc", ".xlsx", ".xls", ".jfif", ".heic", ".heif", ".pages", ".bmp", ".tif", ".tiff"), max_size=10 * 1024 * 1024, ) elif q.type == Question.TYPE_DATE: attrs = {} if q.valid_date_min: attrs['data-min'] = q.valid_date_min.isoformat() if q.valid_date_max: attrs['data-max'] = q.valid_date_max.isoformat() field = forms.DateField( label=label, required=required, help_text=help_text, initial=dateutil.parser.parse(initial.answer).date() if initial and initial.answer else None, widget=DatePickerWidget(attrs), ) if q.valid_date_min: field.validators.append(MinDateValidator(q.valid_date_min)) if q.valid_date_max: field.validators.append(MaxDateValidator(q.valid_date_max)) elif q.type == Question.TYPE_TIME: field = forms.TimeField( label=label, required=required, help_text=help_text, initial=dateutil.parser.parse(initial.answer).time() if initial and initial.answer else None, widget=TimePickerWidget( time_format=get_format_without_seconds( 'TIME_INPUT_FORMATS')), ) elif q.type == Question.TYPE_DATETIME: field = SplitDateTimeField( label=label, required=required, help_text=help_text, initial=dateutil.parser.parse( initial.answer).astimezone(tz) if initial and initial.answer else None, widget=SplitDateTimePickerWidget( time_format=get_format_without_seconds( 'TIME_INPUT_FORMATS'), min_date=q.valid_datetime_min, max_date=q.valid_datetime_max), ) if q.valid_datetime_min: field.validators.append( MinDateTimeValidator(q.valid_datetime_min)) if q.valid_datetime_max: field.validators.append( MaxDateTimeValidator(q.valid_datetime_max)) elif q.type == Question.TYPE_PHONENUMBER: babel_locale = 'en' # Babel, and therefore django-phonenumberfield, do not support our custom locales such das de_Informal if localedata.exists(get_language()): babel_locale = get_language() elif localedata.exists(get_language()[:2]): babel_locale = get_language()[:2] with language(babel_locale): default_country = guess_country(event) default_prefix = None for prefix, values in _COUNTRY_CODE_TO_REGION_CODE.items(): if str(default_country) in values: default_prefix = prefix try: initial = PhoneNumber().from_string( initial.answer) if initial else "+{}.".format( default_prefix) except NumberParseException: initial = None field = PhoneNumberField( label=label, required=required, help_text=help_text, # We now exploit an implementation detail in PhoneNumberPrefixWidget to allow us to pass just # a country code but no number as an initial value. It's a bit hacky, but should be stable for # the future. initial=initial, widget=WrappedPhoneNumberPrefixWidget()) field.question = q if answers: # Cache the answer object for later use field.answer = answers[0] if q.dependency_question_id: field.widget.attrs[ 'data-question-dependency'] = q.dependency_question_id field.widget.attrs[ 'data-question-dependency-values'] = escapejson_attr( json.dumps(q.dependency_values)) if q.type != 'M': field.widget.attrs[ 'required'] = q.required and not self.all_optional field._required = q.required and not self.all_optional field.required = False add_fields['question_%s' % q.id] = field field_positions.append(('question_%s' % q.id, q.position)) field_positions.sort(key=lambda e: e[1]) for fname, p in field_positions: self.fields[fname] = add_fields[fname] responses = question_form_fields.send(sender=event, position=pos) data = pos.meta_info_data for r, response in sorted(responses, key=lambda r: str(r[0])): for key, value in response.items(): # We need to be this explicit, since OrderedDict.update does not retain ordering self.fields[key] = value value.initial = data.get('question_form_data', {}).get(key) for k, v in self.fields.items(): if v.widget.attrs.get( 'autocomplete') or k == 'attendee_name_parts': v.widget.attrs['autocomplete'] = 'section-{} '.format( self.prefix) + v.widget.attrs.get('autocomplete', '')
def _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)
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
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>'
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
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
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)) )
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)))
def __init__(self, *args, **kwargs): """ Takes two additional keyword arguments: :param cartpos: The cart position the form should be for :param event: The event this belongs to """ cartpos = self.cartpos = kwargs.pop('cartpos', None) orderpos = self.orderpos = kwargs.pop('orderpos', None) pos = cartpos or orderpos item = pos.item questions = pos.item.questions_to_ask event = kwargs.pop('event') self.all_optional = kwargs.pop('all_optional', False) super().__init__(*args, **kwargs) if item.admission and event.settings.attendee_names_asked: self.fields['attendee_name_parts'] = NamePartsFormField( max_length=255, required=event.settings.attendee_names_required, scheme=event.settings.name_scheme, titles=event.settings.name_scheme_titles, label=_('Attendee name'), initial=(cartpos.attendee_name_parts if cartpos else orderpos.attendee_name_parts), ) if item.admission and event.settings.attendee_emails_asked: self.fields['attendee_email'] = forms.EmailField( required=event.settings.attendee_emails_required, label=_('Attendee email'), initial=(cartpos.attendee_email if cartpos else orderpos.attendee_email), widget=forms.EmailInput(attrs={'autocomplete': 'email'})) for q in questions: # Do we already have an answer? Provide it as the initial value answers = [a for a in pos.answerlist if a.question_id == q.id] if answers: initial = answers[0] else: initial = None tz = pytz.timezone(event.settings.timezone) help_text = rich_text(q.help_text) label = escape(q.question) # django-bootstrap3 calls mark_safe required = q.required and not self.all_optional if q.type == Question.TYPE_BOOLEAN: if q.required: # For some reason, django-bootstrap3 does not set the required attribute # itself. widget = forms.CheckboxInput( attrs={'required': 'required'}) else: widget = forms.CheckboxInput() if initial: initialbool = (initial.answer == "True") else: initialbool = False field = forms.BooleanField( label=label, required=required, help_text=help_text, initial=initialbool, widget=widget, ) elif q.type == Question.TYPE_NUMBER: field = forms.DecimalField( label=label, required=required, help_text=q.help_text, initial=initial.answer if initial else None, min_value=Decimal('0.00'), ) elif q.type == Question.TYPE_STRING: field = forms.CharField( label=label, required=required, help_text=help_text, initial=initial.answer if initial else None, ) elif q.type == Question.TYPE_TEXT: field = forms.CharField( label=label, required=required, help_text=help_text, widget=forms.Textarea, initial=initial.answer if initial else None, ) elif q.type == Question.TYPE_COUNTRYCODE: field = CountryField().formfield( label=label, required=required, help_text=help_text, widget=forms.Select, empty_label='', initial=initial.answer if initial else None, ) elif q.type == Question.TYPE_CHOICE: field = forms.ModelChoiceField( queryset=q.options, label=label, required=required, help_text=help_text, widget=forms.Select, to_field_name='identifier', empty_label='', initial=initial.options.first() if initial else None, ) elif q.type == Question.TYPE_CHOICE_MULTIPLE: field = forms.ModelMultipleChoiceField( queryset=q.options, label=label, required=required, help_text=help_text, to_field_name='identifier', widget=forms.CheckboxSelectMultiple, initial=initial.options.all() if initial else None, ) elif q.type == Question.TYPE_FILE: field = forms.FileField( label=label, required=required, help_text=help_text, initial=initial.file if initial else None, widget=UploadedFileWidget(position=pos, event=event, answer=initial), ) elif q.type == Question.TYPE_DATE: field = forms.DateField( label=label, required=required, help_text=help_text, initial=dateutil.parser.parse(initial.answer).date() if initial and initial.answer else None, widget=DatePickerWidget(), ) elif q.type == Question.TYPE_TIME: field = forms.TimeField( label=label, required=required, help_text=help_text, initial=dateutil.parser.parse(initial.answer).time() if initial and initial.answer else None, widget=TimePickerWidget( time_format=get_format_without_seconds( 'TIME_INPUT_FORMATS')), ) elif q.type == Question.TYPE_DATETIME: field = SplitDateTimeField( label=label, required=required, help_text=help_text, initial=dateutil.parser.parse( initial.answer).astimezone(tz) if initial and initial.answer else None, widget=SplitDateTimePickerWidget( time_format=get_format_without_seconds( 'TIME_INPUT_FORMATS')), ) elif q.type == Question.TYPE_PHONENUMBER: babel_locale = 'en' # Babel, and therefore django-phonenumberfield, do not support our custom locales such das de_Informal if localedata.exists(get_language()): babel_locale = get_language() elif localedata.exists(get_language()[:2]): babel_locale = get_language()[:2] with language(babel_locale): default_country = guess_country(event) default_prefix = None for prefix, values in _COUNTRY_CODE_TO_REGION_CODE.items(): if str(default_country) in values: default_prefix = prefix try: initial = PhoneNumber().from_string( initial.answer) if initial else "+{}.".format( default_prefix) except NumberParseException: initial = None field = PhoneNumberField( label=label, required=required, help_text=help_text, # We now exploit an implementation detail in PhoneNumberPrefixWidget to allow us to pass just # a country code but no number as an initial value. It's a bit hacky, but should be stable for # the future. initial=initial, widget=WrappedPhoneNumberPrefixWidget()) field.question = q if answers: # Cache the answer object for later use field.answer = answers[0] if q.dependency_question_id: field.widget.attrs[ 'data-question-dependency'] = q.dependency_question_id field.widget.attrs[ 'data-question-dependency-values'] = escapejson_attr( json.dumps(q.dependency_values)) if q.type != 'M': field.widget.attrs[ 'required'] = q.required and not self.all_optional field._required = q.required and not self.all_optional field.required = False self.fields['question_%s' % q.id] = field responses = question_form_fields.send(sender=event, position=pos) data = pos.meta_info_data for r, response in sorted(responses, key=lambda r: str(r[0])): for key, value in response.items(): # We need to be this explicit, since OrderedDict.update does not retain ordering self.fields[key] = value value.initial = data.get('question_form_data', {}).get(key) for k, v in self.fields.items(): if v.widget.attrs.get( 'autocomplete') or k == 'attendee_name_parts': v.widget.attrs['autocomplete'] = 'section-{} '.format( self.prefix) + v.widget.attrs.get('autocomplete', '')
def _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)
def payment_form_render(self, request) -> str: return rich_text( str( self.settings.get('checkout_description', as_type=LazyI18nString)))
def __init__(self, *args, **kwargs): """ Takes two additional keyword arguments: :param cartpos: The cart position the form should be for :param event: The event this belongs to """ cartpos = self.cartpos = kwargs.pop('cartpos', None) orderpos = self.orderpos = kwargs.pop('orderpos', None) pos = cartpos or orderpos item = pos.item questions = pos.item.questions_to_ask event = kwargs.pop('event') super().__init__(*args, **kwargs) if item.admission and event.settings.attendee_names_asked: self.fields['attendee_name_parts'] = NamePartsFormField( max_length=255, required=event.settings.attendee_names_required, scheme=event.settings.name_scheme, label=_('Attendee name'), initial=(cartpos.attendee_name_parts if cartpos else orderpos.attendee_name_parts), ) if item.admission and event.settings.attendee_emails_asked: self.fields['attendee_email'] = forms.EmailField( required=event.settings.attendee_emails_required, label=_('Attendee email'), initial=(cartpos.attendee_email if cartpos else orderpos.attendee_email) ) for q in questions: # Do we already have an answer? Provide it as the initial value answers = [a for a in pos.answerlist if a.question_id == q.id] if answers: initial = answers[0] else: initial = None tz = pytz.timezone(event.settings.timezone) help_text = rich_text(q.help_text) if q.type == Question.TYPE_BOOLEAN: if q.required: # For some reason, django-bootstrap3 does not set the required attribute # itself. widget = forms.CheckboxInput(attrs={'required': 'required'}) else: widget = forms.CheckboxInput() if initial: initialbool = (initial.answer == "True") else: initialbool = False field = forms.BooleanField( label=q.question, required=q.required, help_text=help_text, initial=initialbool, widget=widget, ) elif q.type == Question.TYPE_NUMBER: field = forms.DecimalField( label=q.question, required=q.required, help_text=q.help_text, initial=initial.answer if initial else None, min_value=Decimal('0.00'), ) elif q.type == Question.TYPE_STRING: field = forms.CharField( label=q.question, required=q.required, help_text=help_text, initial=initial.answer if initial else None, ) elif q.type == Question.TYPE_TEXT: field = forms.CharField( label=q.question, required=q.required, help_text=help_text, widget=forms.Textarea, initial=initial.answer if initial else None, ) elif q.type == Question.TYPE_CHOICE: field = forms.ModelChoiceField( queryset=q.options, label=q.question, required=q.required, help_text=help_text, widget=forms.Select, empty_label='', initial=initial.options.first() if initial else None, ) elif q.type == Question.TYPE_CHOICE_MULTIPLE: field = forms.ModelMultipleChoiceField( queryset=q.options, label=q.question, required=q.required, help_text=help_text, widget=forms.CheckboxSelectMultiple, initial=initial.options.all() if initial else None, ) elif q.type == Question.TYPE_FILE: field = forms.FileField( label=q.question, required=q.required, help_text=help_text, initial=initial.file if initial else None, widget=UploadedFileWidget(position=pos, event=event, answer=initial), ) elif q.type == Question.TYPE_DATE: field = forms.DateField( label=q.question, required=q.required, help_text=help_text, initial=dateutil.parser.parse(initial.answer).date() if initial and initial.answer else None, widget=DatePickerWidget(), ) elif q.type == Question.TYPE_TIME: field = forms.TimeField( label=q.question, required=q.required, help_text=help_text, initial=dateutil.parser.parse(initial.answer).time() if initial and initial.answer else None, widget=TimePickerWidget(time_format=get_format_without_seconds('TIME_INPUT_FORMATS')), ) elif q.type == Question.TYPE_DATETIME: field = SplitDateTimeField( label=q.question, required=q.required, help_text=help_text, initial=dateutil.parser.parse(initial.answer).astimezone(tz) if initial and initial.answer else None, widget=SplitDateTimePickerWidget(time_format=get_format_without_seconds('TIME_INPUT_FORMATS')), ) field.question = q if answers: # Cache the answer object for later use field.answer = answers[0] self.fields['question_%s' % q.id] = field responses = question_form_fields.send(sender=event, position=pos) data = pos.meta_info_data for r, response in sorted(responses, key=lambda r: str(r[0])): for key, value in response.items(): # We need to be this explicit, since OrderedDict.update does not retain ordering self.fields[key] = value value.initial = data.get('question_form_data', {}).get(key)
def _get_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)
def confirm_messages(sender, *args, **kwargs): if not sender.settings.confirm_text: return {} return {'confirm_text': rich_text(str(sender.settings.confirm_text))}
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)
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