diff --git a/doc/user/events/widget.rst b/doc/user/events/widget.rst
index 5b2035d0c5..d5c0d1c4b8 100644
--- a/doc/user/events/widget.rst
+++ b/doc/user/events/widget.rst
@@ -114,6 +114,17 @@ If you want to disable voucher input in the widget, you can pass the ``disable-v
+Filtering products
+------------------
+
+You can filter the products shown in the widget by passing in a list of product IDs::
+
+
+
+Alternatively, you can select one or more categories to be shown::
+
+
+
Multi-event selection
---------------------
diff --git a/src/pretix/presale/views/event.py b/src/pretix/presale/views/event.py
index 5f045aab9f..6d8bcdb41f 100644
--- a/src/pretix/presale/views/event.py
+++ b/src/pretix/presale/views/event.py
@@ -51,8 +51,9 @@ def item_group_by_category(items):
)
-def get_grouped_items(event, subevent=None, voucher=None, channel='web', require_seat=0):
- items = event.items.using(settings.DATABASE_REPLICA).filter_available(channel=channel, voucher=voucher).select_related(
+def get_grouped_items(event, subevent=None, voucher=None, channel='web', require_seat=0, base_qs=None):
+ base_qs = base_qs if base_qs is not None else event.items
+ items = base_qs.using(settings.DATABASE_REPLICA).filter_available(channel=channel, voucher=voucher).select_related(
'category', 'tax_rule', # for re-grouping
'hidden_if_available',
).prefetch_related(
diff --git a/src/pretix/presale/views/widget.py b/src/pretix/presale/views/widget.py
index 5d88849db6..92a2d2d2ec 100644
--- a/src/pretix/presale/views/widget.py
+++ b/src/pretix/presale/views/widget.py
@@ -184,9 +184,17 @@ def get_picture(event, picture):
class WidgetAPIProductList(EventListMixin, View):
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='web'
+ self.request.event, subevent=self.subevent, voucher=self.voucher, channel='web',
+ base_qs=qs
)
+
grps = []
for cat, g in item_group_by_category(items):
grps.append({
diff --git a/src/pretix/static/pretixpresale/js/widget/widget.js b/src/pretix/static/pretixpresale/js/widget/widget.js
index d61abf1aaa..0fcc2b00b8 100644
--- a/src/pretix/static/pretixpresale/js/widget/widget.js
+++ b/src/pretix/static/pretixpresale/js/widget/widget.js
@@ -1094,6 +1094,12 @@ var shared_root_methods = {
if (this.$root.filter) {
url += '&' + this.$root.filter;
}
+ if (this.$root.item_filter) {
+ url += '&items=' + escape(this.$root.item_filter);
+ }
+ if (this.$root.category_filter) {
+ url += '&categories=' + escape(this.$root.category_filter);
+ }
var cart_id = getCookie(this.cookieName);
if (this.$root.voucher_code) {
url += '&voucher=' + escape(this.$root.voucher_code);
@@ -1308,6 +1314,8 @@ var create_widget = function (element) {
var disable_vouchers = element.attributes["disable-vouchers"] ? true : false;
var widget_data = JSON.parse(JSON.stringify(window.PretixWidget.widget_data));
var filter = element.attributes.filter ? element.attributes.filter.value : null;
+ var items = element.attributes.items ? element.attributes.items.value : null;
+ var categories = element.attributes.categories ? element.attributes.categories.value : null;
for (var i = 0; i < element.attributes.length; i++) {
var attrib = element.attributes[i];
if (attrib.name.match(/^data-.*$/)) {
@@ -1331,6 +1339,8 @@ var create_widget = function (element) {
currency: null,
name: null,
filter: filter,
+ item_filter: items,
+ category_filter: categories,
voucher_code: voucher,
display_net_prices: false,
voucher_explanation_text: null,
diff --git a/src/tests/presale/test_widget.py b/src/tests/presale/test_widget.py
index e9c519c7ba..9b5d022653 100644
--- a/src/tests/presale/test_widget.py
+++ b/src/tests/presale/test_widget.py
@@ -205,6 +205,50 @@ class WidgetCartTest(CartTestMixin, TestCase):
"voucher_explanation_text": "",
}
+ def test_product_list_view_filter(self):
+ response = self.client.get('/%s/%s/widget/product_list?items=%s' % (self.orga.slug, self.event.slug,
+ self.ticket.pk))
+ assert response['Access-Control-Allow-Origin'] == '*'
+ data = json.loads(response.content.decode())
+ assert data['items_by_category'] == [
+ {
+ "items": [
+ {
+ "require_voucher": False,
+ "order_min": None,
+ "max_price": None,
+ "price": {"gross": "23.00", "net": "19.33", "tax": "3.67", "name": "", "rate": "19.00",
+ "includes_mixed_tax_rate": False},
+ "picture": None,
+ "has_variations": 0,
+ "description": None,
+ "min_price": None,
+ "avail": [100, None],
+ "variations": [],
+ "id": self.ticket.pk,
+ "free_price": False,
+ "original_price": None,
+ "name": "Early-bird ticket",
+ "order_max": 4
+ }
+ ],
+ "description": None,
+ "id": self.category.pk,
+ "name": "Everything"
+ }
+ ]
+
+ response = self.client.get('/%s/%s/widget/product_list?categories=0,%s' % (self.orga.slug, self.event.slug,
+ self.category.pk))
+ assert response['Access-Control-Allow-Origin'] == '*'
+ data = json.loads(response.content.decode())
+ assert len(data['items_by_category']) == 1
+
+ response = self.client.get('/%s/%s/widget/product_list?categories=0' % (self.orga.slug, self.event.slug))
+ assert response['Access-Control-Allow-Origin'] == '*'
+ data = json.loads(response.content.decode())
+ assert len(data['items_by_category']) == 0
+
def test_product_list_view_with_voucher(self):
with scopes_disabled():
self.event.vouchers.create(item=self.ticket, code="ABCDE")