Fix #277 -- Embeddable shop (#622)

* Vendor vue.js

* Refactor item_group_by_category to support vouchers

* Widget: Show product list

* Widget: free prices

* Widget: pictures and loading indicator

* Widget: First iframe steps

* Widget: Do not rerender iframe

* Widget: Error handling

* Improve widget

* Widget: localization tech

* Fix invoice style

* Voucher attribute and waiting list

* Add some iframe chrome

* First step to namespaced carts

* More isolation steps

* More cart isolation things

* More cart isolation things

* Mobile stuff

* Show cart on checkout pages

* PayPal and Stripe support

* Enable downloads

* Locale handling

* change text "save URL to this exact page"

* Widget: voucher redemption

* Widget: CSS

* CSS: Responsive

* Widget: CSS improvements

* Widget: Add embedding code generator

* Widget: Error messages and SSL check

* First tests

* Widget: tests

* Don't use IDs in widgets

* Widget: static files caching
This commit is contained in:
Raphael Michel
2017-10-28 21:54:27 +02:00
committed by GitHub
parent df7fbe5a66
commit 9767243a6d
56 changed files with 12819 additions and 317 deletions

View File

@@ -1,7 +1,7 @@
{% load i18n %}
{% load safelink %}
{% safelink "https://pretix.eu" as pretixurl %}
{% with 'href="'|add:pretixurl|add:'"'|safe as a_attr %}
{% with 'target="_blank" href="'|add:pretixurl|add:'"'|safe as a_attr %}
{% blocktrans trimmed %}
powered by <a {{ a_attr }}>pretix</a>
{% endblocktrans %}

View File

@@ -8,6 +8,7 @@
{% block title %}{% endblock %}{% if url_name != "event.index" %} :: {% endif %}{{ event.name }}
{% endblock %}
{% block above %}
<script type="text/javascript" src="{% static "pretixpresale/js/ui/iframe.js" %}"></script>
{% if not event.live %}
<div class="offline-banner">
<div class="container">
@@ -24,12 +25,13 @@
<div class="page-header">
<div class="pull-left">
{% if event_logo %}
<a href="{% eventurl event "presale:event.index" %}" title="{{ event.name }}">
<a href="{% eventurl event "presale:event.index" cart_namespace=cart_namespace|default_if_none:"" %}"
title="{{ event.name }}">
<img src="{{ event_logo|thumbnail_url:'logo' }}" alt="{{ event.name }}" class="event-logo" />
</a>
{% else %}
<h1>
<a href="{% eventurl event "presale:event.index" %}">{{ event.name }}</a>
<a href="{% eventurl event "presale:event.index" cart_namespace=cart_namespace|default_if_none:"" %}">{{ event.name }}</a>
{% if not event.has_subevents %}
<small>{{ event.get_date_range_display }}</small>
{% endif %}

View File

@@ -1,9 +1,7 @@
{% extends "pretixpresale/event/base.html" %}
{% extends "pretixpresale/event/checkout_base.html" %}
{% load i18n %}
{% load bootstrap3 %}
{% block title %}{% trans "Checkout" %}{% endblock %}
{% block content %}
<h2>{% trans "Checkout" %}</h2>
{% block inner %}
<p>
{% trans "For some of the products in your cart, you can choose additional options before you continue." %}
</p>

View File

@@ -0,0 +1,45 @@
{% extends "pretixpresale/event/base.html" %}
{% load i18n %}
{% load bootstrap3 %}
{% block title %}{% trans "Checkout" %}{% endblock %}
{% block content %}
<div class="panel panel-default cart">
<div class="panel-heading">
<h3 class="panel-title">
<a class="collapsed" data-toggle="collapse" href="#cart">
<span>
<i class="fa fa-shopping-cart"></i>
<strong>{% trans "Your cart" %}</strong>
</span>
<span>
<strong id="cart-deadline-short" data-expires="{{ cart.first_expiry|date:"Y-m-d H:i:sO" }}">
{% if cart.minutes_left > 0 or cart.seconds_left > 0 %}
{{ cart.minutes_left|stringformat:"02d" }}:{{ cart.seconds_left|stringformat:"02d" }}
{% else %}
{% trans "Cart expired" %}
{% endif %}
</strong>
<i class="fa fa-angle-down collapse-indicator"></i>
</span>
</a>
</h3>
</div>
<div class="panel-collapse collapse" id="cart">
<div class="panel-body">
{% include "pretixpresale/event/fragment_cart.html" with cart=cart event=request.event %}
<em id="cart-deadline" data-expires="{{ cart.first_expiry|date:"Y-m-d H:i:sO" }}">
{% if cart.minutes_left > 0 or cart.seconds_left > 0 %}
{% blocktrans trimmed with minutes=cart.minutes_left %}
The items in your cart are reserved for you for {{ minutes }} minutes.
{% endblocktrans %}
{% else %}
{% trans "The items in your cart are no longer reserved for you." %}
{% endif %}
</em>
</div>
</div>
</div>
<h2>{% trans "Checkout" %}</h2>
{% block inner %}
{% endblock %}
{% endblock %}

View File

@@ -11,14 +11,22 @@
{% csrf_token %}
<div class="panel panel-primary cart">
<div class="panel-heading">
<div class="pull-right">
<a href="
{% eventurl request.event "presale:event.index" %}">
<div class="pull-right cart-modify">
<a href="{% eventurl request.event "presale:event.index" cart_namespace=cart_namespace|default_if_none:"" %}">
<span class="fa fa-edit"></span>
{% trans "Modify" %}
</a>
</div>
<strong id="cart-deadline-short" data-expires="{{ cart.first_expiry|date:"Y-m-d H:i:sO" }}"
class="pull-right">
{% if cart.minutes_left > 0 or cart.seconds_left > 0 %}
{{ cart.minutes_left|stringformat:"02d" }}:{{ cart.seconds_left|stringformat:"02d" }}
{% else %}
{% trans "Cart expired" %}
{% endif %}
</strong>
<h3 class="panel-title">
<i class="fa fa-shopping-cart"></i>
{% trans "Your cart" %}
</h3>
</div>
@@ -27,7 +35,7 @@
<div class="cart-row row">
<div class="col-md-6 col-xs-12">
<em id="cart-deadline" data-expires="{{ cart.first_expiry|date:"Y-m-d H:i:sO" }}">
{% if cart.minutes_left > 0 %}
{% if cart.minutes_left > 0 or cart.seconds_left > 0 %}
{% blocktrans trimmed with minutes=cart.minutes_left %}
The items in your cart are reserved for you for {{ minutes }} minutes.
{% endblocktrans %}

View File

@@ -1,9 +1,7 @@
{% extends "pretixpresale/event/base.html" %}
{% extends "pretixpresale/event/checkout_base.html" %}
{% load i18n %}
{% load bootstrap3 %}
{% block title %}{% trans "Checkout" %}{% endblock %}
{% block content %}
<h2>{% trans "Checkout" %}</h2>
{% block inner %}
<p>{% trans "Please select how you want to pay." %}</p>
<form method="post">
{% csrf_token %}

View File

@@ -1,9 +1,7 @@
{% extends "pretixpresale/event/base.html" %}
{% extends "pretixpresale/event/checkout_base.html" %}
{% load i18n %}
{% load bootstrap3 %}
{% block title %}{% trans "Checkout" %}{% endblock %}
{% block content %}
<h2>{% trans "Checkout" %}</h2>
{% block inner %}
<p>{% trans "Before we continue, we need you to answer some questions." %}</p>
<p class="required-legend">
{% blocktrans trimmed %}

View File

@@ -5,7 +5,7 @@
<strong>{% trans "SOLD OUT" %}</strong>
{% if event.settings.waiting_list_enabled %}
<br/>
<a href="{% eventurl event "presale:event.waitinglist" %}?item={{ item.pk }}{% if var %}&var={{ var.pk }}{% endif %}{% if subevent %}&subevent={{ subevent.pk }}{% endif %}">
<a href="{% eventurl event "presale:event.waitinglist" cart_namespace=cart_namespace|default_if_none:"" %}?item={{ item.pk }}{% if var %}&var={{ var.pk }}{% endif %}{% if subevent %}&subevent={{ subevent.pk }}{% endif %}">
<span class="fa fa-plus-circle"></span>
{% trans "Waiting list" %}
</a>
@@ -19,7 +19,7 @@
</strong>
{% if event.settings.waiting_list_enabled %}
<br/>
<a href="{% eventurl event "presale:event.waitinglist" %}?item={{ item.pk }}{% if var %}&var={{ var.pk }}{% endif %}{% if subevent %}&subevent={{ subevent.pk }}{% endif %}">
<a href="{% eventurl event "presale:event.waitinglist" cart_namespace=cart_namespace|default_if_none:"" %}?item={{ item.pk }}{% if var %}&var={{ var.pk }}{% endif %}{% if subevent %}&subevent={{ subevent.pk }}{% endif %}">
<span class="fa fa-plus-circle"></span>
{% trans "Waiting list" %}
</a>

View File

@@ -79,7 +79,7 @@
{% else %}
<div class="count">
{% if editable %}
<form action="{% eventurl event "presale:event.cart.remove" %}"
<form action="{% eventurl event "presale:event.cart.remove" cart_namespace=cart_namespace|default_if_none:"" %}"
method="post" data-asynctask>
{% csrf_token %}
<input type="hidden" name="id" value="{{ line.id }}" />
@@ -90,7 +90,7 @@
{% endif %}
{{ line.count }}
{% if editable %}
<form action="{% eventurl event "presale:event.cart.add" %}"
<form action="{% eventurl event "presale:event.cart.add" cart_namespace=cart_namespace|default_if_none:"" %}"
method="post" data-asynctask>
<input type="hidden" name="subevent" value="{{ line.subevent_id|default_if_none:"" }}" />
{% csrf_token %}

View File

@@ -17,45 +17,64 @@
{% if show_cart %}
<div class="panel panel-primary cart">
<div class="panel-heading">
<h3 class="panel-title">{% trans "Your cart" %}</h3>
<h3 class="panel-title">
<a class="collapsed" data-toggle="collapse" href="#cart">
<span>
<i class="fa fa-shopping-cart"></i>
<strong>{% trans "Your cart" %}</strong>
</span>
<span>
<strong id="cart-deadline-short" data-expires="{{ cart.first_expiry|date:"Y-m-d H:i:sO" }}">
{% if cart.minutes_left > 0 or cart.seconds_left > 0 %}
{{ cart.minutes_left|stringformat:"02d" }}:{{ cart.seconds_left|stringformat:"02d" }}
{% else %}
{% trans "Cart expired" %}
{% endif %}
</strong>
<i class="fa fa-angle-down collapse-indicator"></i>
</span>
</a>
</h3>
</div>
<div class="panel-body">
{% include "pretixpresale/event/fragment_cart.html" with cart=cart event=request.event editable=True %}
<em id="cart-deadline" data-expires="{{ cart.first_expiry|date:"Y-m-d H:i:sO" }}">
{% if cart.minutes_left > 0 %}
{% blocktrans trimmed with minutes=cart.minutes_left %}
The items in your cart are reserved for you for {{ minutes }} minutes.
{% endblocktrans %}
{% else %}
{% trans "The items in your cart are no longer reserved for you." %}
{% endif %}
</em>
<div class="row checkout-button-row">
<div class="col-md-4 col-xs-12">
<form method="post" data-asynctask action="{% eventurl request.event "presale:event.cart.clear" %}">
{% csrf_token %}
<button class="btn btn-block btn-default btn-lg" type="submit">
<i class="fa fa-close"></i> {% trans "Empty cart" %}</button>
</form>
<div class="panel-collapse collapse in" id="cart">
<div class="panel-body">
{% include "pretixpresale/event/fragment_cart.html" with cart=cart event=request.event editable=True %}
<em id="cart-deadline" data-expires="{{ cart.first_expiry|date:"Y-m-d H:i:sO" }}">
{% if cart.minutes_left > 0 or cart.seconds_left > 0 %}
{% blocktrans trimmed with minutes=cart.minutes_left %}
The items in your cart are reserved for you for {{ minutes }} minutes.
{% endblocktrans %}
{% else %}
{% trans "The items in your cart are no longer reserved for you." %}
{% endif %}
</em>
<div class="row checkout-button-row">
<div class="col-md-4 col-xs-12">
<form method="post" data-asynctask action="{% eventurl request.event "presale:event.cart.clear" cart_namespace=cart_namespace %}">
{% csrf_token %}
<button class="btn btn-block btn-default btn-lg" type="submit">
<i class="fa fa-close"></i> {% trans "Empty cart" %}</button>
</form>
</div>
<div class="col-md-4 col-md-offset-4 col-xs-12">
<a class="btn btn-block btn-primary btn-lg"
href="{% eventurl request.event "presale:event.checkout.start" cart_namespace=cart_namespace %}">
{% if has_addon_choices %}
<i class="fa fa-shopping-cart"></i> {% trans "Continue" %}
</a>
{% else %}
<i class="fa fa-shopping-cart"></i> {% trans "Proceed with checkout" %}
{% endif %}
</a>
</div>
<div class="clearfix"></div>
</div>
<div class="col-md-4 col-md-offset-4 col-xs-12">
<a class="btn btn-block btn-primary btn-lg"
href="{% eventurl request.event "presale:event.checkout.start" %}">
{% if has_addon_choices %}
<i class="fa fa-shopping-cart"></i> {% trans "Continue" %}
</a>
{% else %}
<i class="fa fa-shopping-cart"></i> {% trans "Proceed with checkout" %}
{% endif %}
</a>
</div>
<div class="clearfix"></div>
</div>
</div>
</div>
{% endif %}
{% if event.has_subevents %}
{% if event.has_subevents and not cart_namespace %}
{% if subevent %}
<a class="subevent-toggle">
{% trans "View other date" %}
@@ -75,7 +94,7 @@
{% endif %}
{% endif %}
{% if frontpage_text %}
{% if frontpage_text and not cart_namespace %}
<div>
{{ frontpage_text|rich_text }}
</div>
@@ -99,60 +118,63 @@
{% endif %}
</div>
{% endif %}
<div>
{% if ev.location %}
{% if not cart_namespace %}
<div>
{% if ev.location %}
<div class="info-row">
<span class="fa fa-map-marker fa-fw"></span>
<p>
{{ ev.location|linebreaksbr }}
</p>
</div>
{% endif %}
<div class="info-row">
<span class="fa fa-map-marker fa-fw"></span>
<span class="fa fa-clock-o fa-fw"></span>
<p>
{{ ev.location|linebreaksbr }}
{{ ev.get_date_range_display }}
{% if event.settings.show_times %}
<br>
{% blocktrans trimmed with time=ev.date_from|date:"TIME_FORMAT" %}
Begin: {{ time }}
{% endblocktrans %}
{% if event.settings.show_date_to %}
<br>
{% blocktrans trimmed with time=ev.date_to|date:"TIME_FORMAT" %}
End: {{ time }}
{% endblocktrans %}
{% endif %}
{% endif %}
{% if ev.date_admission %}
<br>
{% if ev.date_admission|date:"SHORT_DATE_FORMAT" == ev.date_from|date:"SHORT_DATE_FORMAT" %}
{% blocktrans trimmed with time=ev.date_admission|date:"TIME_FORMAT" %}
Admission: {{ time }}
{% endblocktrans %}
{% else %}
{% blocktrans trimmed with datetime=ev.date_admission|date:"SHORT_DATETIME_FORMAT" %}
Admission: {{ datetime }}
{% endblocktrans %}
{% endif %}
{% endif %}
<br>
{% if subevent %}
<a href="{% eventurl event "presale:event.ical.download" subevent=subevent.pk %}">
{% else %}
<a href="{% eventurl event "presale:event.ical.download" %}">
{% endif %}
{% trans "Add to Calendar" %}
</a>
</p>
</div>
{% endif %}
<div class="info-row">
<span class="fa fa-clock-o fa-fw"></span>
<p>
{{ ev.get_date_range_display }}
{% if event.settings.show_times %}
<br>
{% blocktrans trimmed with time=ev.date_from|date:"TIME_FORMAT" %}
Begin: {{ time }}
{% endblocktrans %}
{% if event.settings.show_date_to %}
<br>
{% blocktrans trimmed with time=ev.date_to|date:"TIME_FORMAT" %}
End: {{ time }}
{% endblocktrans %}
{% endif %}
{% endif %}
{% if ev.date_admission %}
<br>
{% if ev.date_admission|date:"SHORT_DATE_FORMAT" == ev.date_from|date:"SHORT_DATE_FORMAT" %}
{% blocktrans trimmed with time=ev.date_admission|date:"TIME_FORMAT" %}
Admission: {{ time }}
{% endblocktrans %}
{% else %}
{% blocktrans trimmed with datetime=ev.date_admission|date:"SHORT_DATETIME_FORMAT" %}
Admission: {{ datetime }}
{% endblocktrans %}
{% endif %}
{% endif %}
<br>
{% if subevent %}
<a href="{% eventurl event "presale:event.ical.download" subevent=subevent.pk %}">
{% else %}
<a href="{% eventurl event "presale:event.ical.download" %}">
{% endif %}
{% trans "Add to Calendar" %}
</a>
</p>
</div>
</div>
{% eventsignal event "pretix.presale.signals.front_page_top" %}
{% endif %}
{% eventsignal event "pretix.presale.signals.front_page_top" %}
{% if ev.presale_is_running or event.settings.show_items_outside_presale_period %}
<form method="post" data-asynctask
action="{% eventurl request.event "presale:event.cart.add" %}?next={{ request.path|urlencode }}">
action="{% eventurl request.event "presale:event.cart.add" cart_namespace=cart_namespace %}?next={{ request.path|urlencode }}">
{% csrf_token %}
<input type="hidden" name="subevent" value="{{ subevent.id|default_if_none:"" }}" />
{% for tup in items_by_category %}
@@ -392,7 +414,7 @@
{% if vouchers_exist %}
<section class="front-page">
<h3>{% trans "Redeem a voucher" %}</h3>
<form method="get" action="{% eventurl event "presale:event.redeem" %}">
<form method="get" action="{% eventurl event "presale:event.redeem" cart_namespace=cart_namespace %}">
<div class="row-voucher">
<div class="col-md-8 col-sm-6 col-xs-12">
<div class="input-group">
@@ -412,25 +434,27 @@
</form>
</section>
{% endif %}
{% eventsignal event "pretix.presale.signals.front_page_bottom" %}
<section class="front-page">
<h3>{% trans "If you already ordered a ticket" %}</h3>
<div>
<div class="col-md-8 col-xs-12">
<p>
{% blocktrans trimmed %}
If you want to see or change the status and details of your order, click on the link in one of the
emails we sent you during the order process. If you cannot find the link, click on the
following button to request the link to your order to be sent to you again.
{% endblocktrans %}
</p>
{% if not cart_namespace %}
{% eventsignal event "pretix.presale.signals.front_page_bottom" %}
<section class="front-page">
<h3>{% trans "If you already ordered a ticket" %}</h3>
<div>
<div class="col-md-8 col-xs-12">
<p>
{% blocktrans trimmed %}
If you want to see or change the status and details of your order, click on the link in one of the
emails we sent you during the order process. If you cannot find the link, click on the
following button to request the link to your order to be sent to you again.
{% endblocktrans %}
</p>
</div>
<div class="col-md-4 col-xs-12 text-right">
<a class="btn btn-block btn-primary" href="{% eventurl event "presale:event.resend_link" %}">
{% trans "Resend order links" %}
</a>
</div>
<div class="clearfix"></div>
</div>
<div class="col-md-4 col-xs-12 text-right">
<a class="btn btn-block btn-primary" href="{% eventurl event "presale:event.resend_link" %}">
{% trans "Resend order links" %}
</a>
</div>
<div class="clearfix"></div>
</div>
</section>
</section>
{% endif %}
{% endblock %}

View File

@@ -22,10 +22,15 @@
{% else %}
<p>{% trans "We successfully received your payment. See below for details." %}</p>
{% endif %}
<p>{% blocktrans trimmed %}
<p class="iframe-hidden">{% blocktrans trimmed %}
Please bookmark or save the link to this exact page if you want to download your ticket or change
your details later. We also sent you an email containing the link to the address you specified.
{% endblocktrans %}</p>
<p class="iframe-only">{% blocktrans trimmed %}
Please save the following link if you want to download your ticket or change your details later. We
also sent you an email containing the link to the address you specified.
{% endblocktrans %}<br>
<code>{{ url }}</code></p>
<div class="clearfix"></div>
</div>
{% endif %}

View File

@@ -19,7 +19,7 @@
</p>
{% if event.presale_is_running or event.settings.show_items_outside_presale_period %}
<form method="post" data-asynctask
action="{% eventurl request.event "presale:event.cart.add" %}?next={{ request.path|urlencode }}">
action="{% eventurl request.event "presale:event.cart.add" cart_namespace=cart_namespace %}?next={% eventurl request.event "presale:event.index" cart_namespace=cart_namespace %}">
{% csrf_token %}
<input type="hidden" name="subevent" value="{{ subevent.id|default_if_none:"" }}" />
<input type="hidden" name="_voucher_code" value="{{ voucher.code }}">

View File

@@ -0,0 +1,5 @@
{% load compress %}
{% load staticfiles %}
{% compress css %}
<link rel="stylesheet" type="text/x-scss" href="{% static "pretixpresale/scss/widget.scss" %}"/>
{% endcompress %}