def _send_mail(order: Order, subject: LazyI18nString, message: LazyI18nString, subevent: SubEvent, refund_amount: Decimal, user: User, positions: list): with language(order.locale, order.event.settings.region): try: ia = order.invoice_address except InvoiceAddress.DoesNotExist: ia = InvoiceAddress(order=order) email_context = get_email_context(event_or_subevent=subevent or order.event, refund_amount=refund_amount, order=order, position_or_address=ia, event=order.event) real_subject = str(subject).format_map(TolerantDict(email_context)) try: order.send_mail( real_subject, message, email_context, 'pretix.event.order.email.event_canceled', user, ) except SendMailException: logger.exception('Order canceled email could not be sent') for p in positions: if subevent and p.subevent_id != subevent.id: continue if p.addon_to_id is None and p.attendee_email and p.attendee_email != order.email: real_subject = str(subject).format_map( TolerantDict(email_context)) email_context = get_email_context(event_or_subevent=p.subevent or order.event, event=order.event, refund_amount=refund_amount, position_or_address=p, order=order, position=p) try: order.send_mail(real_subject, message, email_context, 'pretix.event.order.email.event_canceled', position=p, user=user) except SendMailException: logger.exception( 'Order canceled email could not be sent to attendee')
def get_context_data(self, **kwargs): ctx = super().get_context_data(**kwargs) o = {} for lang in self.request.event.settings.locales: with language(lang, self.request.event.settings.region): placeholders = TolerantDict() for k, v in get_available_placeholders(self.request.event, ['event', 'order', 'position_or_address']).items(): placeholders[k] = '<span class="placeholder" title="{}">{}</span>'.format( _('This value will be replaced based on dynamic parameters.'), v.render_sample(self.request.event) ) subject = bleach.clean(self.object.subject.localize(lang), tags=[]) preview_subject = subject.format_map(placeholders) template = self.object.template.localize(lang) preview_text = markdown_compile_email(template.format_map(placeholders)) o[lang] = { 'subject': _('Subject: {subject}'.format(subject=preview_subject)), 'html': preview_text, } ctx['output'] = o return ctx
def _send_wle_mail(wle: WaitingListEntry, subject: LazyI18nString, message: LazyI18nString, subevent: SubEvent): with language(wle.locale, wle.event.settings.region): email_context = get_email_context(event_or_subevent=subevent or wle.event, event=wle.event) try: mail(wle.email, str(subject).format_map(TolerantDict(email_context)), message, email_context, wle.event, locale=wle.locale) except SendMailException: logger.exception('Waiting list canceled email could not be sent')
def form_valid(self, form): self.output = {} if self.request.POST.get("action") == "preview": for l in self.request.event.settings.locales: with language(l, self.request.event.settings.region): context_dict = TolerantDict() for k, v in get_available_placeholders( self.request.event, ['event', 'order', 'position_or_address']).items(): context_dict[ k] = '<span class="placeholder" title="{}">{}</span>'.format( _('This value will be replaced based on dynamic parameters.' ), v.render_sample(self.request.event)) subject = bleach.clean( form.cleaned_data['subject'].localize(l), tags=[]) preview_subject = subject.format_map(context_dict) template = form.cleaned_data['template'].localize(l) preview_text = markdown_compile_email( template.format_map(context_dict)) self.output[l] = { 'subject': _('Subject: {subject}').format( subject=preview_subject), 'html': preview_text, } return self.get(self.request, *self.args, **self.kwargs) messages.success(self.request, _('Your rule has been created.')) form.instance.event = self.request.event self.object = form.save() return redirect( 'plugins:sendmail:rule.update', event=self.request.event.slug, organizer=self.request.event.organizer.slug, rule=self.object.pk, )
def form_valid(self, form): qs = Order.objects.filter(event=self.request.event) statusq = Q(status__in=form.cleaned_data['sendto']) if 'overdue' in form.cleaned_data['sendto']: statusq |= Q(status=Order.STATUS_PENDING, expires__lt=now()) orders = qs.filter(statusq) opq = OrderPosition.objects.filter( order=OuterRef('pk'), canceled=False, item_id__in=[i.pk for i in form.cleaned_data.get('items')], ) if form.cleaned_data.get('filter_checkins'): ql = [] if form.cleaned_data.get('not_checked_in'): ql.append(Q(checkins__list_id=None)) if form.cleaned_data.get('checkin_lists'): ql.append( Q(checkins__list_id__in=[ i.pk for i in form.cleaned_data.get('checkin_lists', []) ], )) if len(ql) == 2: opq = opq.filter(ql[0] | ql[1]) elif ql: opq = opq.filter(ql[0]) else: opq = opq.none() if form.cleaned_data.get('subevent'): opq = opq.filter(subevent=form.cleaned_data.get('subevent')) orders = orders.annotate(match_pos=Exists(opq)).filter( match_pos=True).distinct() self.output = {} if not orders: messages.error(self.request, _('There are no orders matching this selection.')) return self.get(self.request, *self.args, **self.kwargs) if self.request.POST.get("action") == "preview": for l in self.request.event.settings.locales: with language(l): context_dict = TolerantDict() for k, v in get_available_placeholders( self.request.event, ['event', 'order', 'position_or_address']).items(): context_dict[ k] = '<span class="placeholder" title="{}">{}</span>'.format( _('This value will be replaced based on dynamic parameters.' ), v.render_sample(self.request.event)) subject = bleach.clean( form.cleaned_data['subject'].localize(l), tags=[]) preview_subject = subject.format_map(context_dict) message = form.cleaned_data['message'].localize(l) preview_text = markdown_compile_email( message.format_map(context_dict)) self.output[l] = { 'subject': _('Subject: {subject}').format( subject=preview_subject), 'html': preview_text, } return self.get(self.request, *self.args, **self.kwargs) send_mails.apply_async( kwargs={ 'recipients': form.cleaned_data['recipients'], 'event': self.request.event.pk, 'user': self.request.user.pk, 'subject': form.cleaned_data['subject'].data, 'message': form.cleaned_data['message'].data, 'orders': [o.pk for o in orders], 'items': [i.pk for i in form.cleaned_data.get('items')], 'not_checked_in': form.cleaned_data.get('not_checked_in'), 'checkin_lists': [i.pk for i in form.cleaned_data.get('checkin_lists')], 'filter_checkins': form.cleaned_data.get('filter_checkins'), }) self.request.event.log_action('pretix.plugins.sendmail.sent', user=self.request.user, data=dict(form.cleaned_data)) messages.success( self.request, _('Your message has been queued and will be sent to the contact addresses of %d ' 'orders in the next minutes.') % len(orders)) return redirect('plugins:sendmail:send', event=self.request.event.slug, organizer=self.request.event.organizer.slug)
def get_private_icals(event, positions): """ Return a list of ical objects based on a sequence of positions. Unlike get_public_ical, this will - Generate multiple ical files instead of one (but with deduplication applied) - Respect the mail_attach_ical_description setting It is private in the sense that mail_attach_ical_description may contain content not suited for public display. We however intentionally do not allow using placeholders based on the order and position specifically. This is for two reasons: - In reality, many people will add their invite to their calendar which is shared with a larger team. People are probably not aware that they're sharing sensitive information such as their secret ticket link with everyone they share their calendar with. - It would be pretty hard to implement it in a way that doesn't require us to use distinct settings fields for emails to customers and to attendees, which feels like an overcomplication. """ from pretix.base.services.mail import TolerantDict tz = pytz.timezone(event.settings.timezone) creation_time = datetime.datetime.now(pytz.utc) calobjects = [] evs = set(p.subevent or event for p in positions) for ev in evs: if isinstance(ev, Event): url = build_absolute_uri(event, 'presale:event.index') else: url = build_absolute_uri(event, 'presale:event.index', { 'subevent': ev.pk }) if event.settings.mail_attach_ical_description: ctx = get_email_context(event=event, event_or_subevent=ev) description = str(event.settings.mail_attach_ical_description).format_map(TolerantDict(ctx)) else: # Default description descr = [] descr.append(_('Tickets: {url}').format(url=url)) if ev.date_admission: descr.append(str(_('Admission: {datetime}')).format( datetime=date_format(ev.date_admission.astimezone(tz), 'SHORT_DATETIME_FORMAT') )) descr.append(_('Organizer: {organizer}').format(organizer=event.organizer.name)) description = '\n'.join(descr) cal = vobject.iCalendar() cal.add('prodid').value = '-//pretix//{}//'.format(settings.PRETIX_INSTANCE_NAME.replace(" ", "_")) vevent = cal.add('vevent') vevent.add('summary').value = str(ev.name) vevent.add('description').value = description vevent.add('dtstamp').value = creation_time if ev.location: vevent.add('location').value = str(ev.location) vevent.add('uid').value = 'pretix-{}-{}-{}@{}'.format( event.organizer.slug, event.organizer.slug, event.slug, ev.pk if not isinstance(ev, Event) else '0', urlparse(url).netloc ) if event.settings.show_times: vevent.add('dtstart').value = ev.date_from.astimezone(tz) else: vevent.add('dtstart').value = ev.date_from.astimezone(tz).date() if event.settings.show_date_to and ev.date_to: if event.settings.show_times: vevent.add('dtend').value = ev.date_to.astimezone(tz) else: # with full-day events date_to in pretix is included (e.g. last day) # whereas dtend in vcalendar is non-inclusive => add one day for export vevent.add('dtend').value = ev.date_to.astimezone(tz).date() + datetime.timedelta(days=1) calobjects.append(cal) return calobjects
def book_second_dose(*, op, item, variation, subevent, original_event): event = item.event with event.lock(), transaction.atomic(): avcode, avnr = item.check_quotas(subevent=subevent, fail_on_no_quotas=True) if avcode != Quota.AVAILABILITY_OK: logger.info( f"SECOND DOSE: cannot use slot {subevent.pk}, sold out") # sold out, look for next one return childorder = Order.objects.create( event=event, status=Order.STATUS_PAID, require_approval=False, testmode=op.order.testmode, email=op.order.email, phone=op.order.phone, locale=op.order.locale, expires=now() + timedelta(days=30), total=Decimal("0.00"), expiry_reminder_sent=True, sales_channel=op.order.sales_channel, comment="Auto-generated through scheduling from order {}".format( op.order.code), meta_info=op.order.meta_info, ) op.order.log_action( "pretix_vacc_autosched.scheduled", data={ "position": op.pk, "event": event.pk, "event_slug": event.slug, "order": childorder.code, }, ) childorder.log_action( "pretix_vacc_autosched.created", data={ "event": original_event.pk, "event_slug": original_event.slug, "order": op.order.code, }, ) childorder.log_action("pretix.event.order.placed", data={"source": "vacc_autosched"}) childpos = childorder.positions.create( positionid=1, tax_rate=Decimal("0.00"), tax_rule=None, tax_value=Decimal("0.00"), subevent=subevent, item=item, variation=variation, price=Decimal("0.00"), attendee_name_cached=op.attendee_name_cached, attendee_name_parts=op.attendee_name_parts, attendee_email=op.attendee_email, company=op.company, street=op.street, zipcode=op.zipcode, city=op.city, country=op.country, state=op.state, addon_to=None, voucher=None, meta_info=op.meta_info, ) for answ in op.answers.all(): q = childorder.event.questions.filter( identifier=answ.question.identifier).first() if not q: continue childansw = childpos.answers.create(question=q, answer=answ.answer, file=answ.file) if answ.options.all(): childopts = list( q.options.filter(identifier__in=[ o.identifier for o in answ.options.all() ])) if childopts: childansw.options.add(*childopts) LinkedOrderPosition.objects.create(base_position=op, child_position=childpos) childorder.create_transactions(is_new=True) order_placed.send(event, order=childorder) order_paid.send(event, order=childorder) if original_event.settings.vacc_autosched_mail: with language(childorder.locale, original_event.settings.region): email_template = original_event.settings.vacc_autosched_body email_subject = str(original_event.settings.vacc_autosched_subject) email_context = get_email_context(event=childorder.event, order=childorder) email_context["scheduled_datetime"] = date_format( subevent.date_from.astimezone(original_event.timezone), "SHORT_DATETIME_FORMAT", ) try: childorder.send_mail( email_subject, email_template, email_context, "pretix.event.order.email.order_placed", attach_tickets=True, attach_ical=event.settings.mail_attach_ical, ) except SendMailException: logger.exception("Order approved email could not be sent") if (original_event.settings.vacc_autosched_sms and can_use_juvare_api(original_event) and childorder.phone): with language(childorder.locale, original_event.settings.region): from pretix_juvare_notify.tasks import juvare_send_text template = original_event.settings.vacc_autosched_sms_text context = get_email_context(event=childorder.event, order=childorder) context["scheduled_datetime"] = date_format( subevent.date_from.astimezone(original_event.timezone), "SHORT_DATETIME_FORMAT", ) message = str(template).format_map(TolerantDict(context)) juvare_send_text.apply_async( kwargs={ "text": message, "to": childorder.phone, "event": original_event.pk, }) logger.info(f"SECOND DOSE: done, created order {childorder.code}") return childorder