mirror of
https://github.com/pretix/pretix.git
synced 2025-12-12 04:42:28 +00:00
Compare commits
46 Commits
custom-man
...
v1.4.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3acc29f51a | ||
|
|
4bae0d1b81 | ||
|
|
c38a850294 | ||
|
|
a7ec475c40 | ||
|
|
ac1467bd4b | ||
|
|
3f0af67345 | ||
|
|
4d14b6c096 | ||
|
|
cb789bc06c | ||
|
|
17dad33f8b | ||
|
|
1d5c160e1d | ||
|
|
f04b7fa365 | ||
|
|
fa011fbdce | ||
|
|
759c5374d9 | ||
|
|
f631acdf18 | ||
|
|
b2dfd8ab11 | ||
|
|
43f4803da7 | ||
|
|
019d8220b8 | ||
|
|
b946010bdb | ||
|
|
c3097b12c3 | ||
|
|
12a53710c3 | ||
|
|
983326e610 | ||
|
|
b4eb707b38 | ||
|
|
e44f34f0a9 | ||
|
|
3c762adbf4 | ||
|
|
ebabd20d09 | ||
|
|
8694e1901a | ||
|
|
0f2875e89a | ||
|
|
74d9921be1 | ||
|
|
41e56adfdb | ||
|
|
4ff1d302d9 | ||
|
|
d6e213d51a | ||
|
|
2a4deeba55 | ||
|
|
24b1d2afcb | ||
|
|
5cea3d824a | ||
|
|
78a8a7d744 | ||
|
|
a3bf85754a | ||
|
|
006b6fd5e8 | ||
|
|
8ff2c42070 | ||
|
|
c5d18c6884 | ||
|
|
48b3621f1e | ||
|
|
fb716eb498 | ||
|
|
5d8e294350 | ||
|
|
8a96d8c24e | ||
|
|
6396d2f922 | ||
|
|
ed7e90451b | ||
|
|
f5990dd5c4 |
@@ -5,7 +5,6 @@ tests:
|
|||||||
- virtualenv env
|
- virtualenv env
|
||||||
- source env/bin/activate
|
- source env/bin/activate
|
||||||
- pip install -U pip wheel setuptools
|
- pip install -U pip wheel setuptools
|
||||||
- XDG_CACHE_HOME=/cache bash .travis.sh style
|
|
||||||
- XDG_CACHE_HOME=/cache bash .travis.sh tests
|
- XDG_CACHE_HOME=/cache bash .travis.sh tests
|
||||||
tags:
|
tags:
|
||||||
- python3
|
- python3
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ If there is a problem, a status code in the ``5xx`` range will be returned.
|
|||||||
Performance monitoring
|
Performance monitoring
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
If you to generate detailled performance statistics of your pretix installation, there is an
|
If you want to generate detailed performance statistics of your pretix installation, there is an
|
||||||
endpoint at ``https://pretix.mydomain.com/metrics`` (no slash at the end) which returns a
|
endpoint at ``https://pretix.mydomain.com/metrics`` (no slash at the end) which returns a
|
||||||
number of values in the text format understood by monitoring tools like Prometheus_. This data
|
number of values in the text format understood by monitoring tools like Prometheus_. This data
|
||||||
is only collected and exposed if you enable it in the :ref:`metrics-settings` section of your
|
is only collected and exposed if you enable it in the :ref:`metrics-settings` section of your
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ Frontend
|
|||||||
--------
|
--------
|
||||||
|
|
||||||
.. automodule:: pretix.presale.signals
|
.. automodule:: pretix.presale.signals
|
||||||
:members: html_head, html_footer, footer_links, front_page_top, front_page_bottom, checkout_confirm_messages
|
:members: html_head, html_footer, footer_links, front_page_top, front_page_bottom, contact_form_fields, checkout_confirm_messages
|
||||||
|
|
||||||
|
|
||||||
.. automodule:: pretix.presale.signals
|
.. automodule:: pretix.presale.signals
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ automatically). If you are working on Ubuntu or Debian, we strongly recommend up
|
|||||||
your pip and setuptools installation inside the virtual environment, otherwise some of
|
your pip and setuptools installation inside the virtual environment, otherwise some of
|
||||||
the dependencies might fail::
|
the dependencies might fail::
|
||||||
|
|
||||||
pip3 install -U pip setuptools==28.6.1
|
pip3 install -U pip setuptools
|
||||||
|
|
||||||
Working with the code
|
Working with the code
|
||||||
---------------------
|
---------------------
|
||||||
@@ -81,7 +81,7 @@ and head to http://localhost:8000/
|
|||||||
|
|
||||||
As we did not implement an overall front page yet, you need to go directly to
|
As we did not implement an overall front page yet, you need to go directly to
|
||||||
http://localhost:8000/control/ for the admin view or, if you imported the test
|
http://localhost:8000/control/ for the admin view or, if you imported the test
|
||||||
data as suggested above, to the event page at http://localhost:8000/bigevents/2017/
|
data as suggested above, to the event page at http://localhost:8000/bigevents/2018/
|
||||||
|
|
||||||
.. note:: If you want the development server to listen on a different interface or
|
.. note:: If you want the development server to listen on a different interface or
|
||||||
port (for example because you develop on `pretixdroid`_), you can check
|
port (for example because you develop on `pretixdroid`_), you can check
|
||||||
|
|||||||
@@ -15,7 +15,8 @@ ways that pretix itself is:
|
|||||||
* PDF ticket output
|
* PDF ticket output
|
||||||
|
|
||||||
The following plugins are not shipped with pretix but are maintained by the
|
The following plugins are not shipped with pretix but are maintained by the
|
||||||
same team:
|
same team. We update them regularly to make them compatible with the latest
|
||||||
|
pretix releases:
|
||||||
|
|
||||||
* `SEPA direct debit`_
|
* `SEPA direct debit`_
|
||||||
* `Pages`_
|
* `Pages`_
|
||||||
@@ -23,8 +24,17 @@ same team:
|
|||||||
* `Cartshare`_
|
* `Cartshare`_
|
||||||
* `Fontpack Free fonts`_
|
* `Fontpack Free fonts`_
|
||||||
|
|
||||||
|
The following closed-source plugins are available to customers of the hosted pretix.eu platform.
|
||||||
|
Please get in touch with the pretix team if you want to have them for your self-hosted
|
||||||
|
pretix installation:
|
||||||
|
|
||||||
|
* Campaign tracking
|
||||||
|
* Integration with Google Analytics and Facebook Pixel
|
||||||
|
* Integration with Slack
|
||||||
|
* Integration with MailChimp
|
||||||
|
|
||||||
The following plugins are from independent third-party authors, so we can make
|
The following plugins are from independent third-party authors, so we can make
|
||||||
no statements about their stability:
|
no statements about their stability or compatibility:
|
||||||
|
|
||||||
* `esPass ticket output`_
|
* `esPass ticket output`_
|
||||||
* `IcePay integration`_
|
* `IcePay integration`_
|
||||||
|
|||||||
@@ -36,11 +36,11 @@ t = Team.objects.get_or_create(
|
|||||||
can_change_organizer_settings=True, can_change_event_settings=True, can_change_items=True,
|
can_change_organizer_settings=True, can_change_event_settings=True, can_change_items=True,
|
||||||
can_view_orders=True, can_change_orders=True, can_view_vouchers=True, can_change_vouchers=True
|
can_view_orders=True, can_change_orders=True, can_view_vouchers=True, can_change_vouchers=True
|
||||||
)
|
)
|
||||||
t.members.add(user)
|
t[0].members.add(user)
|
||||||
cat_tickets = ItemCategory.all.create(
|
cat_tickets = ItemCategory.objects.create(
|
||||||
event=event, name='Tickets'
|
event=event, name='Tickets'
|
||||||
)
|
)
|
||||||
cat_merch = ItemCategory.all.create(
|
cat_merch = ItemCategory.objects.create(
|
||||||
event=event, name='Merchandise'
|
event=event, name='Merchandise'
|
||||||
)
|
)
|
||||||
question = Question.objects.create(
|
question = Question.objects.create(
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
__version__ = "1.4.0-dev0"
|
__version__ = "1.4.1"
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import csv
|
|
||||||
import io
|
import io
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
|
||||||
import pytz
|
import pytz
|
||||||
|
from defusedcsv import csv
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.db.models import Sum
|
from django.db.models import Sum
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
|
|||||||
27
src/pretix/base/migrations/0061_auto_20170521_0942.py
Normal file
27
src/pretix/base/migrations/0061_auto_20170521_0942.py
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.10.7 on 2017-05-21 09:42
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.core.cache import cache
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
def rename_placeholder(app, schema_editor):
|
||||||
|
EventSettingsStore = app.get_model('pretixbase', 'Event_SettingsStore')
|
||||||
|
|
||||||
|
for setting in EventSettingsStore.objects.all():
|
||||||
|
if setting.key == 'mail_text_order_placed':
|
||||||
|
new_value = setting.value.replace('{paymentinfo}', '{payment_info}')
|
||||||
|
setting.value = new_value
|
||||||
|
cache.delete('hierarkey_{}_{}'.format('event', setting.object_id))
|
||||||
|
setting.save()
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
('pretixbase', '0060_auto_20170510_1027'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RunPython(rename_placeholder, migrations.RunPython.noop)
|
||||||
|
]
|
||||||
@@ -2,7 +2,6 @@ import json
|
|||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.core import checks
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models.signals import post_delete
|
from django.db.models.signals import post_delete
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
@@ -35,6 +34,7 @@ def cached_file_delete(sender, instance, **kwargs):
|
|||||||
|
|
||||||
|
|
||||||
class LoggingMixin:
|
class LoggingMixin:
|
||||||
|
|
||||||
def log_action(self, action, data=None, user=None):
|
def log_action(self, action, data=None, user=None):
|
||||||
"""
|
"""
|
||||||
Create a LogEntry object that is related to this object.
|
Create a LogEntry object that is related to this object.
|
||||||
@@ -59,6 +59,7 @@ class LoggingMixin:
|
|||||||
|
|
||||||
|
|
||||||
class LoggedModel(models.Model, LoggingMixin):
|
class LoggedModel(models.Model, LoggingMixin):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
abstract = True
|
abstract = True
|
||||||
|
|
||||||
@@ -73,38 +74,3 @@ class LoggedModel(models.Model, LoggingMixin):
|
|||||||
return LogEntry.objects.filter(
|
return LogEntry.objects.filter(
|
||||||
content_type=ContentType.objects.get_for_model(type(self)), object_id=self.pk
|
content_type=ContentType.objects.get_for_model(type(self)), object_id=self.pk
|
||||||
).select_related('user', 'event')
|
).select_related('user', 'event')
|
||||||
|
|
||||||
|
|
||||||
class EventBoundModelMixin:
|
|
||||||
event_lookup = 'event'
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def for_event(cls, event):
|
|
||||||
return cls.all.filter(**{
|
|
||||||
cls.event_lookup: event
|
|
||||||
})
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def check(cls, **kwargs):
|
|
||||||
try:
|
|
||||||
errors = super(cls).check(**kwargs)
|
|
||||||
except AttributeError:
|
|
||||||
errors = []
|
|
||||||
if hasattr(cls, 'objects'):
|
|
||||||
errors.append(
|
|
||||||
checks.Error(
|
|
||||||
'Default model manager "objects" defined.',
|
|
||||||
hint='Replace the objects manager by a manager called "all".',
|
|
||||||
obj=cls,
|
|
||||||
id='pretixbase.E001',
|
|
||||||
)
|
|
||||||
)
|
|
||||||
if not hasattr(cls, 'all'):
|
|
||||||
errors.append(
|
|
||||||
checks.Error(
|
|
||||||
'Model manager "all" not defined.',
|
|
||||||
obj=cls,
|
|
||||||
id='pretixbase.E002',
|
|
||||||
)
|
|
||||||
)
|
|
||||||
return errors
|
|
||||||
|
|||||||
@@ -234,14 +234,14 @@ class Event(LoggedModel):
|
|||||||
), tz)
|
), tz)
|
||||||
|
|
||||||
def copy_data_from(self, other):
|
def copy_data_from(self, other):
|
||||||
from . import ItemAddOn, Item, Question, Quota
|
from . import ItemAddOn, ItemCategory, Item, Question, Quota
|
||||||
from ..signals import event_copy_data
|
from ..signals import event_copy_data
|
||||||
|
|
||||||
self.plugins = other.plugins
|
self.plugins = other.plugins
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
category_map = {}
|
category_map = {}
|
||||||
for c in other.categories.all():
|
for c in ItemCategory.objects.filter(event=other):
|
||||||
category_map[c.pk] = c
|
category_map[c.pk] = c
|
||||||
c.pk = None
|
c.pk = None
|
||||||
c.event = self
|
c.event = self
|
||||||
|
|||||||
@@ -14,12 +14,12 @@ from django.utils.translation import ugettext_lazy as _
|
|||||||
from i18nfield.fields import I18nCharField, I18nTextField
|
from i18nfield.fields import I18nCharField, I18nTextField
|
||||||
|
|
||||||
from pretix.base.decimal import round_decimal
|
from pretix.base.decimal import round_decimal
|
||||||
from pretix.base.models.base import EventBoundModelMixin, LoggedModel
|
from pretix.base.models.base import LoggedModel
|
||||||
|
|
||||||
from .event import Event
|
from .event import Event
|
||||||
|
|
||||||
|
|
||||||
class ItemCategory(EventBoundModelMixin, LoggedModel):
|
class ItemCategory(LoggedModel):
|
||||||
"""
|
"""
|
||||||
Items can be sorted into these categories.
|
Items can be sorted into these categories.
|
||||||
|
|
||||||
@@ -53,8 +53,6 @@ class ItemCategory(EventBoundModelMixin, LoggedModel):
|
|||||||
'source for add-ons.')
|
'source for add-ons.')
|
||||||
)
|
)
|
||||||
|
|
||||||
all = models.Manager()
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _("Product category")
|
verbose_name = _("Product category")
|
||||||
verbose_name_plural = _("Product categories")
|
verbose_name_plural = _("Product categories")
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ from django.contrib.contenttypes.models import ContentType
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.functional import cached_property
|
from django.utils.functional import cached_property
|
||||||
|
from django.utils.html import escape
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
|
||||||
@@ -66,7 +67,7 @@ class LogEntry(models.Model):
|
|||||||
'organizer': self.event.organizer.slug,
|
'organizer': self.event.organizer.slug,
|
||||||
'code': co.code
|
'code': co.code
|
||||||
}),
|
}),
|
||||||
'val': co.code,
|
'val': escape(co.code),
|
||||||
}
|
}
|
||||||
elif isinstance(co, Voucher):
|
elif isinstance(co, Voucher):
|
||||||
a_text = _('Voucher {val}…')
|
a_text = _('Voucher {val}…')
|
||||||
@@ -76,7 +77,7 @@ class LogEntry(models.Model):
|
|||||||
'organizer': self.event.organizer.slug,
|
'organizer': self.event.organizer.slug,
|
||||||
'voucher': co.id
|
'voucher': co.id
|
||||||
}),
|
}),
|
||||||
'val': co.code[:6],
|
'val': escape(co.code[:6]),
|
||||||
}
|
}
|
||||||
elif isinstance(co, Item):
|
elif isinstance(co, Item):
|
||||||
a_text = _('Product {val}')
|
a_text = _('Product {val}')
|
||||||
@@ -86,7 +87,7 @@ class LogEntry(models.Model):
|
|||||||
'organizer': self.event.organizer.slug,
|
'organizer': self.event.organizer.slug,
|
||||||
'item': co.id
|
'item': co.id
|
||||||
}),
|
}),
|
||||||
'val': co.name,
|
'val': escape(co.name),
|
||||||
}
|
}
|
||||||
elif isinstance(co, Quota):
|
elif isinstance(co, Quota):
|
||||||
a_text = _('Quota {val}')
|
a_text = _('Quota {val}')
|
||||||
@@ -96,7 +97,7 @@ class LogEntry(models.Model):
|
|||||||
'organizer': self.event.organizer.slug,
|
'organizer': self.event.organizer.slug,
|
||||||
'quota': co.id
|
'quota': co.id
|
||||||
}),
|
}),
|
||||||
'val': co.name,
|
'val': escape(co.name),
|
||||||
}
|
}
|
||||||
elif isinstance(co, ItemCategory):
|
elif isinstance(co, ItemCategory):
|
||||||
a_text = _('Category {val}')
|
a_text = _('Category {val}')
|
||||||
@@ -106,7 +107,7 @@ class LogEntry(models.Model):
|
|||||||
'organizer': self.event.organizer.slug,
|
'organizer': self.event.organizer.slug,
|
||||||
'category': co.id
|
'category': co.id
|
||||||
}),
|
}),
|
||||||
'val': co.name,
|
'val': escape(co.name),
|
||||||
}
|
}
|
||||||
elif isinstance(co, Question):
|
elif isinstance(co, Question):
|
||||||
a_text = _('Question {val}')
|
a_text = _('Question {val}')
|
||||||
@@ -116,7 +117,7 @@ class LogEntry(models.Model):
|
|||||||
'organizer': self.event.organizer.slug,
|
'organizer': self.event.organizer.slug,
|
||||||
'question': co.id
|
'question': co.id
|
||||||
}),
|
}),
|
||||||
'val': co.question,
|
'val': escape(co.question),
|
||||||
}
|
}
|
||||||
|
|
||||||
if a_text and a_map:
|
if a_text and a_map:
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import copy
|
import copy
|
||||||
|
import json
|
||||||
import os
|
import os
|
||||||
import string
|
import string
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
@@ -183,6 +184,10 @@ class Order(LoggedModel):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.full_code
|
return self.full_code
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def meta_info_data(self):
|
||||||
|
return json.loads(self.meta_info)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def full_code(self):
|
def full_code(self):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -414,7 +414,7 @@ def _perform_order(event: str, payment_provider: str, position_ids: List[str],
|
|||||||
'order': order.code,
|
'order': order.code,
|
||||||
'secret': order.secret
|
'secret': order.secret
|
||||||
}),
|
}),
|
||||||
'paymentinfo': str(pprov.order_pending_mail_render(order)),
|
'payment_info': str(pprov.order_pending_mail_render(order)),
|
||||||
'invoice_name': invoice_name,
|
'invoice_name': invoice_name,
|
||||||
'invoice_company': invoice_company,
|
'invoice_company': invoice_company,
|
||||||
},
|
},
|
||||||
@@ -512,7 +512,7 @@ class OrderChangeManager:
|
|||||||
if (not variation and item.has_variations) or (variation and variation.item_id != item.pk):
|
if (not variation and item.has_variations) or (variation and variation.item_id != item.pk):
|
||||||
raise OrderError(self.error_messages['product_without_variation'])
|
raise OrderError(self.error_messages['product_without_variation'])
|
||||||
price = item.default_price if variation is None else variation.price
|
price = item.default_price if variation is None else variation.price
|
||||||
if not price:
|
if price is None:
|
||||||
raise OrderError(self.error_messages['product_invalid'])
|
raise OrderError(self.error_messages['product_invalid'])
|
||||||
self._totaldiff = price - position.price
|
self._totaldiff = price - position.price
|
||||||
self._quotadiff.update(variation.quotas.all() if variation else item.quotas.all())
|
self._quotadiff.update(variation.quotas.all() if variation else item.quotas.all())
|
||||||
|
|||||||
@@ -240,7 +240,7 @@ Your {event} team"""))
|
|||||||
we successfully received your order for {event} with a total value
|
we successfully received your order for {event} with a total value
|
||||||
of {total} {currency}. Please complete your payment before {date}.
|
of {total} {currency}. Please complete your payment before {date}.
|
||||||
|
|
||||||
{paymentinfo}
|
{payment_info}
|
||||||
|
|
||||||
You can change your order details and view the status of your order at
|
You can change your order details and view the status of your order at
|
||||||
{url}
|
{url}
|
||||||
|
|||||||
13
src/pretix/base/templatetags/escapejson.py
Normal file
13
src/pretix/base/templatetags/escapejson.py
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
from django import template
|
||||||
|
from django.template.defaultfilters import stringfilter
|
||||||
|
|
||||||
|
from pretix.helpers.escapejson import escapejson
|
||||||
|
|
||||||
|
register = template.Library()
|
||||||
|
|
||||||
|
|
||||||
|
@register.filter("escapejson")
|
||||||
|
@stringfilter
|
||||||
|
def escapejs_filter(value):
|
||||||
|
"""Hex encodes characters for use in a application/json type script."""
|
||||||
|
return escapejson(value)
|
||||||
@@ -1,6 +1,12 @@
|
|||||||
|
import urllib.parse
|
||||||
|
|
||||||
import bleach
|
import bleach
|
||||||
import markdown
|
import markdown
|
||||||
|
from bleach import DEFAULT_CALLBACKS
|
||||||
from django import template
|
from django import template
|
||||||
|
from django.core import signing
|
||||||
|
from django.urls import reverse
|
||||||
|
from django.utils.http import is_safe_url
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
|
|
||||||
register = template.Library()
|
register = template.Library()
|
||||||
@@ -42,6 +48,15 @@ ALLOWED_ATTRIBUTES = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def safelink_callback(attrs, new=False):
|
||||||
|
url = attrs.get((None, 'href'), '/')
|
||||||
|
if not is_safe_url(url):
|
||||||
|
signer = signing.Signer(salt='safe-redirect')
|
||||||
|
attrs[None, 'href'] = reverse('redirect') + '?url=' + urllib.parse.quote(signer.sign(url))
|
||||||
|
attrs[None, 'target'] = '_blank'
|
||||||
|
return attrs
|
||||||
|
|
||||||
|
|
||||||
@register.filter
|
@register.filter
|
||||||
def rich_text(text: str, **kwargs):
|
def rich_text(text: str, **kwargs):
|
||||||
"""
|
"""
|
||||||
@@ -52,5 +67,5 @@ def rich_text(text: str, **kwargs):
|
|||||||
markdown.markdown(text),
|
markdown.markdown(text),
|
||||||
tags=ALLOWED_TAGS,
|
tags=ALLOWED_TAGS,
|
||||||
attributes=ALLOWED_ATTRIBUTES,
|
attributes=ALLOWED_ATTRIBUTES,
|
||||||
))
|
), callbacks=DEFAULT_CALLBACKS + [safelink_callback])
|
||||||
return mark_safe(body_md)
|
return mark_safe(body_md)
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ def contextprocessor(request):
|
|||||||
|
|
||||||
_js_payment_weekdays_disabled = '[]'
|
_js_payment_weekdays_disabled = '[]'
|
||||||
_nav_event = []
|
_nav_event = []
|
||||||
if hasattr(request, 'event'):
|
if getattr(request, 'event', None) and hasattr(request, 'organizer'):
|
||||||
for receiver, response in nav_event.send(request.event, request=request):
|
for receiver, response in nav_event.send(request.event, request=request):
|
||||||
_nav_event += response
|
_nav_event += response
|
||||||
if request.event.settings.get('payment_term_weekdays'):
|
if request.event.settings.get('payment_term_weekdays'):
|
||||||
|
|||||||
@@ -259,10 +259,12 @@ class EventSettingsForm(SettingsForm):
|
|||||||
)
|
)
|
||||||
attendee_emails_asked = forms.BooleanField(
|
attendee_emails_asked = forms.BooleanField(
|
||||||
label=_("Ask for email addresses per ticket"),
|
label=_("Ask for email addresses per ticket"),
|
||||||
help_text=_("Normally, pretix asks for one email address per order and the order confirmation will be send "
|
help_text=_("Normally, pretix asks for one email address per order and the order confirmation will be sent "
|
||||||
"to that email address. If you enable this option, the system will additionally ask for "
|
"only to that email address. If you enable this option, the system will additionally ask for "
|
||||||
"individual email addresses for every admission ticket. This might be useful if you want to "
|
"individual email addresses for every admission ticket. This might be useful if you want to "
|
||||||
"obtain individual addresses for every attendee even in case of group orders."),
|
"obtain individual addresses for every attendee even in case of group orders. However, "
|
||||||
|
"pretix will send the order confirmation only to the one primary email address, not to the "
|
||||||
|
"per-attendee addresses."),
|
||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
attendee_emails_required = forms.BooleanField(
|
attendee_emails_required = forms.BooleanField(
|
||||||
@@ -490,9 +492,9 @@ class MailSettingsForm(SettingsForm):
|
|||||||
label=_("Text"),
|
label=_("Text"),
|
||||||
required=False,
|
required=False,
|
||||||
widget=I18nTextarea,
|
widget=I18nTextarea,
|
||||||
help_text=_("Available placeholders: {event}, {total}, {currency}, {date}, {paymentinfo}, {url}, "
|
help_text=_("Available placeholders: {event}, {total}, {currency}, {date}, {payment_info}, {url}, "
|
||||||
"{invoice_name}, {invoice_company}"),
|
"{invoice_name}, {invoice_company}"),
|
||||||
validators=[PlaceholderValidator(['{event}', '{total}', '{currency}', '{date}', '{paymentinfo}',
|
validators=[PlaceholderValidator(['{event}', '{total}', '{currency}', '{date}', '{payment_info}',
|
||||||
'{url}', '{invoice_name}', '{invoice_company}'])]
|
'{url}', '{invoice_name}', '{invoice_company}'])]
|
||||||
)
|
)
|
||||||
mail_text_order_paid = I18nFormField(
|
mail_text_order_paid = I18nFormField(
|
||||||
@@ -610,7 +612,7 @@ class DisplaySettingsForm(SettingsForm):
|
|||||||
)
|
)
|
||||||
logo_image = ExtFileField(
|
logo_image = ExtFileField(
|
||||||
label=_('Logo image'),
|
label=_('Logo image'),
|
||||||
ext_whitelist=(".png", ".jpg", ".svg", ".gif", ".jpeg"),
|
ext_whitelist=(".png", ".jpg", ".gif", ".jpeg"),
|
||||||
required=False,
|
required=False,
|
||||||
help_text=_('If you provide a logo image, we will by default not show your events name and date '
|
help_text=_('If you provide a logo image, we will by default not show your events name and date '
|
||||||
'in the page header. We will show your logo with a maximal height of 120 pixels.')
|
'in the page header. We will show your logo with a maximal height of 120 pixels.')
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ class OrganizerSettingsForm(SettingsForm):
|
|||||||
|
|
||||||
organizer_logo_image = ExtFileField(
|
organizer_logo_image = ExtFileField(
|
||||||
label=_('Logo image'),
|
label=_('Logo image'),
|
||||||
ext_whitelist=(".png", ".jpg", ".svg", ".gif", ".jpeg"),
|
ext_whitelist=(".png", ".jpg", ".gif", ".jpeg"),
|
||||||
required=False,
|
required=False,
|
||||||
help_text=_('If you provide a logo image, we will by default not show your organization name '
|
help_text=_('If you provide a logo image, we will by default not show your organization name '
|
||||||
'in the page header. We will show your logo with a maximal height of 120 pixels.')
|
'in the page header. We will show your logo with a maximal height of 120 pixels.')
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
{% extends "pretixcontrol/base.html" %}
|
{% extends "pretixcontrol/base.html" %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
{% load staticfiles %}
|
||||||
{% block title %}{{ request.event.name }}{% endblock %}
|
{% block title %}{{ request.event.name }}{% endblock %}
|
||||||
|
|
||||||
{% block nav %}
|
{% block nav %}
|
||||||
@@ -112,7 +113,9 @@
|
|||||||
<li>
|
<li>
|
||||||
<a href="{{ nav.url }}" {% if nav.active %}class="active"{% endif %}
|
<a href="{{ nav.url }}" {% if nav.active %}class="active"{% endif %}
|
||||||
{% if nav.children %}class="has-children"{% endif %}>
|
{% if nav.children %}class="has-children"{% endif %}>
|
||||||
{% if nav.icon %}
|
{% if nav.icon and "." in nav.icon %}
|
||||||
|
<img src="{% static nav.icon %}" class="fa-img">
|
||||||
|
{% elif nav.icon %}
|
||||||
<i class="fa fa-{{ nav.icon }} fa-fw"></i>
|
<i class="fa fa-{{ nav.icon }} fa-fw"></i>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{{ nav.label }}
|
{{ nav.label }}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
{% extends "pretixcontrol/items/base.html" %}
|
{% extends "pretixcontrol/items/base.html" %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% load bootstrap3 %}
|
{% load bootstrap3 %}
|
||||||
|
{% load escapejson %}
|
||||||
{% load formset_tags %}
|
{% load formset_tags %}
|
||||||
{% block title %}{% blocktrans with name=question.question %}Question: {{ name }}{% endblocktrans %}{% endblock %}
|
{% block title %}{% blocktrans with name=question.question %}Question: {{ name }}{% endblocktrans %}{% endblock %}
|
||||||
{% block inside %}
|
{% block inside %}
|
||||||
@@ -58,7 +59,7 @@
|
|||||||
<div class="chart" id="question_chart" data-type="{{ question.type }}">
|
<div class="chart" id="question_chart" data-type="{{ question.type }}">
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<script type="application/json" id="question-chart-data">{{ stats_json|safe }}</script>
|
<script type="application/json" id="question-chart-data">{{ stats_json|escapejson }}</script>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-5 col-xs-12">
|
<div class="col-md-5 col-xs-12">
|
||||||
<table class="table table-bordered table-hover">
|
<table class="table table-bordered table-hover">
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
{% extends "pretixcontrol/items/base.html" %}
|
{% extends "pretixcontrol/items/base.html" %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% load bootstrap3 %}
|
{% load bootstrap3 %}
|
||||||
|
{% load escapejson %}
|
||||||
{% load eventsignal %}
|
{% load eventsignal %}
|
||||||
{% block title %}{% blocktrans with name=quota.name %}Quota: {{ name }}{% endblocktrans %}{% endblock %}
|
{% block title %}{% blocktrans with name=quota.name %}Quota: {{ name }}{% endblocktrans %}{% endblock %}
|
||||||
{% block inside %}
|
{% block inside %}
|
||||||
@@ -20,7 +21,7 @@
|
|||||||
<div class="chart" id="quota_chart">
|
<div class="chart" id="quota_chart">
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<script type="application/json" id="quota-chart-data">{{ quota_chart_data|safe }}</script>
|
<script type="application/json" id="quota-chart-data">{{ quota_chart_data|escapejson }}</script>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-5 col-xs-12">
|
<div class="col-md-5 col-xs-12">
|
||||||
<legend>{% trans "Availability calculation" %}</legend>
|
<legend>{% trans "Availability calculation" %}</legend>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
from django.db.models import Prefetch, Q
|
from django.db.models import F, Prefetch, Q
|
||||||
from django.db.models.functions import Coalesce
|
from django.db.models.functions import Coalesce
|
||||||
from django.views.generic import ListView
|
from django.views.generic import ListView
|
||||||
|
|
||||||
@@ -68,12 +68,15 @@ class CheckInView(EventPermissionRequiredMixin, ListView):
|
|||||||
'-code': '-order__code',
|
'-code': '-order__code',
|
||||||
'email': 'order__email',
|
'email': 'order__email',
|
||||||
'-email': '-order__email',
|
'-email': '-order__email',
|
||||||
'status': 'checkins__id',
|
# Set nulls_first to be consistent over databases
|
||||||
'-status': '-checkins__id',
|
'status': F('checkins__id').asc(nulls_first=True),
|
||||||
'timestamp': 'checkins__datetime',
|
'-status': F('checkins__id').desc(nulls_last=True),
|
||||||
'-timestamp': '-checkins__datetime',
|
'timestamp': F('checkins__datetime').asc(nulls_first=True),
|
||||||
|
'-timestamp': F('checkins__datetime').desc(nulls_last=True),
|
||||||
'item': 'item__name',
|
'item': 'item__name',
|
||||||
'-item': '-item__name',
|
'-item': '-item__name',
|
||||||
'name': ('display_name', {'display_name': Coalesce('attendee_name', 'addon_to__attendee_name')}),
|
'name': (F('display_name').asc(nulls_first=True),
|
||||||
'-name': ('-display_name', {'display_name': Coalesce('attendee_name', 'addon_to__attendee_name')}),
|
{'display_name': Coalesce('attendee_name', 'addon_to__attendee_name')}),
|
||||||
|
'-name': (F('display_name').desc(nulls_last=True),
|
||||||
|
{'display_name': Coalesce('attendee_name', 'addon_to__attendee_name')}),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ from django.shortcuts import render
|
|||||||
from django.template.loader import get_template
|
from django.template.loader import get_template
|
||||||
from django.utils import formats
|
from django.utils import formats
|
||||||
from django.utils.formats import date_format
|
from django.utils.formats import date_format
|
||||||
|
from django.utils.html import escape
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from pretix.base.models import (
|
from pretix.base.models import (
|
||||||
@@ -138,7 +139,7 @@ def quota_widgets(sender, **kwargs):
|
|||||||
status, left = q.availability()
|
status, left = q.availability()
|
||||||
widgets.append({
|
widgets.append({
|
||||||
'content': NUM_WIDGET.format(num='{}/{}'.format(left, q.size) if q.size is not None else '\u221e',
|
'content': NUM_WIDGET.format(num='{}/{}'.format(left, q.size) if q.size is not None else '\u221e',
|
||||||
text=_('{quota} left').format(quota=q.name)),
|
text=_('{quota} left').format(quota=escape(q.name))),
|
||||||
'display_size': 'small',
|
'display_size': 'small',
|
||||||
'priority': 50,
|
'priority': 50,
|
||||||
'url': reverse('control:event.items.quotas.show', kwargs={
|
'url': reverse('control:event.items.quotas.show', kwargs={
|
||||||
@@ -269,7 +270,8 @@ def user_event_widgets(**kwargs):
|
|||||||
for event in events:
|
for event in events:
|
||||||
widgets.append({
|
widgets.append({
|
||||||
'content': '<div class="event">{event}<span class="from">{df}</span><span class="to">{dt}</span></div>'.format(
|
'content': '<div class="event">{event}<span class="from">{df}</span><span class="to">{dt}</span></div>'.format(
|
||||||
event=event.name, df=date_format(event.date_from, 'SHORT_DATE_FORMAT') if event.date_from else '',
|
event=escape(event.name),
|
||||||
|
df=date_format(event.date_from, 'SHORT_DATE_FORMAT') if event.date_from else '',
|
||||||
dt=date_format(event.date_to, 'SHORT_DATE_FORMAT') if event.date_to else ''
|
dt=date_format(event.date_to, 'SHORT_DATE_FORMAT') if event.date_to else ''
|
||||||
),
|
),
|
||||||
'display_size': 'small',
|
'display_size': 'small',
|
||||||
|
|||||||
@@ -316,8 +316,7 @@ class InvoicePreview(EventPermissionRequiredMixin, View):
|
|||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
pdf = build_preview_invoice_pdf(request.event)
|
pdf = build_preview_invoice_pdf(request.event)
|
||||||
resp = HttpResponse(pdf, content_type='application/pdf')
|
resp = HttpResponse(pdf, content_type='application/pdf')
|
||||||
resp['Content-Security-Policy'] = "style-src 'unsafe-inline'; script-src 'unsafe-inline'; object-src 'self'"
|
resp['Content-Disposition'] = 'attachment; filename="invoice-preview.pdf"'
|
||||||
resp['Content-Disposition'] = 'inline; filename="invoice-preview.pdf"'
|
|
||||||
return resp
|
return resp
|
||||||
|
|
||||||
|
|
||||||
@@ -437,7 +436,7 @@ class MailSettingsPreview(EventPermissionRequiredMixin, View):
|
|||||||
def items(self):
|
def items(self):
|
||||||
return {
|
return {
|
||||||
'mail_text_order_placed': ['total', 'currency', 'date', 'invoice_company',
|
'mail_text_order_placed': ['total', 'currency', 'date', 'invoice_company',
|
||||||
'event', 'paymentinfo', 'url', 'invoice_name'],
|
'event', 'payment_info', 'url', 'invoice_name'],
|
||||||
'mail_text_order_paid': ['event', 'url', 'invoice_name', 'invoice_company', 'payment_info'],
|
'mail_text_order_paid': ['event', 'url', 'invoice_name', 'invoice_company', 'payment_info'],
|
||||||
'mail_text_order_free': ['event', 'url', 'invoice_name', 'invoice_company'],
|
'mail_text_order_free': ['event', 'url', 'invoice_name', 'invoice_company'],
|
||||||
'mail_text_resend_link': ['event', 'url', 'invoice_name', 'invoice_company'],
|
'mail_text_resend_link': ['event', 'url', 'invoice_name', 'invoice_company'],
|
||||||
@@ -468,7 +467,7 @@ class MailSettingsPreview(EventPermissionRequiredMixin, View):
|
|||||||
'code': '68CYU2H6ZTP3WLK5',
|
'code': '68CYU2H6ZTP3WLK5',
|
||||||
'invoice_name': _('John Doe'),
|
'invoice_name': _('John Doe'),
|
||||||
'invoice_company': _('Sample Corporation'),
|
'invoice_company': _('Sample Corporation'),
|
||||||
'paymentinfo': _('Please transfer money to this bank account: 9999-9999-9999-9999')
|
'payment_info': _('Please transfer money to this bank account: 9999-9999-9999-9999')
|
||||||
}
|
}
|
||||||
|
|
||||||
def generate_order_url(self, code, secret):
|
def generate_order_url(self, code, secret):
|
||||||
@@ -528,11 +527,7 @@ class TicketSettingsPreview(EventPermissionRequiredMixin, View):
|
|||||||
fname, mimet, data = tickets.preview(self.request.event.pk, self.output.identifier)
|
fname, mimet, data = tickets.preview(self.request.event.pk, self.output.identifier)
|
||||||
resp = HttpResponse(data, content_type=mimet)
|
resp = HttpResponse(data, content_type=mimet)
|
||||||
ftype = fname.split(".")[-1]
|
ftype = fname.split(".")[-1]
|
||||||
if mimet == "application/pdf":
|
resp['Content-Disposition'] = 'attachment; filename="ticket-preview.{}"'.format(ftype)
|
||||||
resp['Content-Security-Policy'] = "style-src 'unsafe-inline'; script-src 'unsafe-inline'; object-src 'self'"
|
|
||||||
resp['Content-Disposition'] = 'inline; filename="ticket-preview.{}"'.format(ftype)
|
|
||||||
else:
|
|
||||||
resp['Content-Disposition'] = 'attachment; filename="ticket-preview.{}"'.format(ftype)
|
|
||||||
return resp
|
return resp
|
||||||
|
|
||||||
def get_error_url(self) -> str:
|
def get_error_url(self) -> str:
|
||||||
|
|||||||
@@ -674,7 +674,7 @@ class ExportDoView(EventPermissionRequiredMixin, ExportMixin, AsyncAction, View)
|
|||||||
|
|
||||||
if not self.exporter.form.is_valid():
|
if not self.exporter.form.is_valid():
|
||||||
messages.error(self.request, _('There was a problem processing your input. See below for error details.'))
|
messages.error(self.request, _('There was a problem processing your input. See below for error details.'))
|
||||||
return self.get(*args, **kwargs)
|
return self.get(request, *args, **kwargs)
|
||||||
|
|
||||||
cf = CachedFile()
|
cf = CachedFile()
|
||||||
cf.date = now()
|
cf.date = now()
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import csv
|
|
||||||
import io
|
import io
|
||||||
|
|
||||||
|
from defusedcsv import csv
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.core.urlresolvers import resolve, reverse
|
from django.core.urlresolvers import resolve, reverse
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
from debug_toolbar.middleware import DebugToolbarMiddleware
|
|
||||||
from django.utils.deprecation import MiddlewareMixin
|
|
||||||
|
|
||||||
|
|
||||||
class DebugMiddlewareCompatibilityShim(MiddlewareMixin, DebugToolbarMiddleware):
|
|
||||||
pass
|
|
||||||
16
src/pretix/helpers/escapejson.py
Normal file
16
src/pretix/helpers/escapejson.py
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
from django.utils import six
|
||||||
|
from django.utils.encoding import force_text
|
||||||
|
from django.utils.functional import keep_lazy
|
||||||
|
from django.utils.safestring import SafeText, mark_safe
|
||||||
|
|
||||||
|
_json_escapes = {
|
||||||
|
ord('>'): '\\u003E',
|
||||||
|
ord('<'): '\\u003C',
|
||||||
|
ord('&'): '\\u0026',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@keep_lazy(six.text_type, SafeText)
|
||||||
|
def escapejson(value):
|
||||||
|
"""Hex encodes characters for use in a application/json type script."""
|
||||||
|
return mark_safe(force_text(value).translate(_json_escapes))
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -7,8 +7,8 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: \n"
|
"Project-Id-Version: \n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2017-05-18 09:17+0000\n"
|
"POT-Creation-Date: 2017-06-01 10:54+0000\n"
|
||||||
"PO-Revision-Date: 2017-05-11 12:13+0200\n"
|
"PO-Revision-Date: 2017-06-01 12:56+0200\n"
|
||||||
"Last-Translator: Raphael Michel <michel@rami.io>\n"
|
"Last-Translator: Raphael Michel <michel@rami.io>\n"
|
||||||
"Language-Team: \n"
|
"Language-Team: \n"
|
||||||
"Language: de\n"
|
"Language: de\n"
|
||||||
@@ -79,59 +79,71 @@ msgid "May 31st, 2017"
|
|||||||
msgstr "31. Mai 2017"
|
msgstr "31. Mai 2017"
|
||||||
|
|
||||||
#: pretix/plugins/ticketoutputpdf/static/pretixplugins/ticketoutputpdf/editor.js:164
|
#: pretix/plugins/ticketoutputpdf/static/pretixplugins/ticketoutputpdf/editor.js:164
|
||||||
|
msgid "May 31st – June 4th, 2017"
|
||||||
|
msgstr "31. Mai – 4. Juni 2017"
|
||||||
|
|
||||||
|
#: pretix/plugins/ticketoutputpdf/static/pretixplugins/ticketoutputpdf/editor.js:165
|
||||||
msgid "20:00"
|
msgid "20:00"
|
||||||
msgstr "20:00"
|
msgstr "20:00"
|
||||||
|
|
||||||
#: pretix/plugins/ticketoutputpdf/static/pretixplugins/ticketoutputpdf/editor.js:165
|
#: pretix/plugins/ticketoutputpdf/static/pretixplugins/ticketoutputpdf/editor.js:166
|
||||||
msgid "19:00"
|
msgid "19:00"
|
||||||
msgstr "19:00"
|
msgstr "19:00"
|
||||||
|
|
||||||
#: pretix/plugins/ticketoutputpdf/static/pretixplugins/ticketoutputpdf/editor.js:166
|
#: pretix/plugins/ticketoutputpdf/static/pretixplugins/ticketoutputpdf/editor.js:167
|
||||||
msgid "2017-05-31 20:00"
|
msgid "2017-05-31 20:00"
|
||||||
msgstr "31.05.2016 20:00"
|
msgstr "31.05.2016 20:00"
|
||||||
|
|
||||||
#: pretix/plugins/ticketoutputpdf/static/pretixplugins/ticketoutputpdf/editor.js:167
|
#: pretix/plugins/ticketoutputpdf/static/pretixplugins/ticketoutputpdf/editor.js:168
|
||||||
msgid "2017-05-31 19:00"
|
msgid "2017-05-31 19:00"
|
||||||
msgstr "31.05.2016 19:00"
|
msgstr "31.05.2016 19:00"
|
||||||
|
|
||||||
#: pretix/plugins/ticketoutputpdf/static/pretixplugins/ticketoutputpdf/editor.js:168
|
#: pretix/plugins/ticketoutputpdf/static/pretixplugins/ticketoutputpdf/editor.js:169
|
||||||
msgid "Random City"
|
msgid "Random City"
|
||||||
msgstr "Musterstadt"
|
msgstr "Musterstadt"
|
||||||
|
|
||||||
#: pretix/plugins/ticketoutputpdf/static/pretixplugins/ticketoutputpdf/editor.js:213
|
#: pretix/plugins/ticketoutputpdf/static/pretixplugins/ticketoutputpdf/editor.js:170
|
||||||
|
msgid ""
|
||||||
|
"Addon 1\n"
|
||||||
|
"Addon 2"
|
||||||
|
msgstr ""
|
||||||
|
"Workshop 1\n"
|
||||||
|
"Workshop 2"
|
||||||
|
|
||||||
|
#: pretix/plugins/ticketoutputpdf/static/pretixplugins/ticketoutputpdf/editor.js:215
|
||||||
msgid "The PDF background file could not be loaded for the following reason:"
|
msgid "The PDF background file could not be loaded for the following reason:"
|
||||||
msgstr "Die Hintergrund-PDF-Datei konnte nicht geladen werden:"
|
msgstr "Die Hintergrund-PDF-Datei konnte nicht geladen werden:"
|
||||||
|
|
||||||
#: pretix/plugins/ticketoutputpdf/static/pretixplugins/ticketoutputpdf/editor.js:362
|
#: pretix/plugins/ticketoutputpdf/static/pretixplugins/ticketoutputpdf/editor.js:364
|
||||||
msgid "Group of objects"
|
msgid "Group of objects"
|
||||||
msgstr "Gruppe von Objekten"
|
msgstr "Gruppe von Objekten"
|
||||||
|
|
||||||
#: pretix/plugins/ticketoutputpdf/static/pretixplugins/ticketoutputpdf/editor.js:368
|
#: pretix/plugins/ticketoutputpdf/static/pretixplugins/ticketoutputpdf/editor.js:370
|
||||||
msgid "Text object"
|
msgid "Text object"
|
||||||
msgstr "Text-Objekt"
|
msgstr "Text-Objekt"
|
||||||
|
|
||||||
#: pretix/plugins/ticketoutputpdf/static/pretixplugins/ticketoutputpdf/editor.js:370
|
#: pretix/plugins/ticketoutputpdf/static/pretixplugins/ticketoutputpdf/editor.js:372
|
||||||
msgid "Barcode area"
|
msgid "Barcode area"
|
||||||
msgstr "QR-Code-Bereich"
|
msgstr "QR-Code-Bereich"
|
||||||
|
|
||||||
#: pretix/plugins/ticketoutputpdf/static/pretixplugins/ticketoutputpdf/editor.js:372
|
#: pretix/plugins/ticketoutputpdf/static/pretixplugins/ticketoutputpdf/editor.js:374
|
||||||
msgid "Object"
|
msgid "Object"
|
||||||
msgstr "Objekt"
|
msgstr "Objekt"
|
||||||
|
|
||||||
#: pretix/plugins/ticketoutputpdf/static/pretixplugins/ticketoutputpdf/editor.js:376
|
#: pretix/plugins/ticketoutputpdf/static/pretixplugins/ticketoutputpdf/editor.js:378
|
||||||
msgid "Ticket design"
|
msgid "Ticket design"
|
||||||
msgstr "Ticket-Design"
|
msgstr "Ticket-Design"
|
||||||
|
|
||||||
#: pretix/plugins/ticketoutputpdf/static/pretixplugins/ticketoutputpdf/editor.js:612
|
#: pretix/plugins/ticketoutputpdf/static/pretixplugins/ticketoutputpdf/editor.js:614
|
||||||
msgid "Saving failed."
|
msgid "Saving failed."
|
||||||
msgstr "Speichern fehlgeschlagen."
|
msgstr "Speichern fehlgeschlagen."
|
||||||
|
|
||||||
#: pretix/plugins/ticketoutputpdf/static/pretixplugins/ticketoutputpdf/editor.js:644
|
#: pretix/plugins/ticketoutputpdf/static/pretixplugins/ticketoutputpdf/editor.js:646
|
||||||
msgid "Do you really want to leave the editor without saving your changes?"
|
msgid "Do you really want to leave the editor without saving your changes?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Möchten Sie den Editor wirklich schließen ohne Ihre Änderungen zu speichern?"
|
"Möchten Sie den Editor wirklich schließen ohne Ihre Änderungen zu speichern?"
|
||||||
|
|
||||||
#: pretix/plugins/ticketoutputpdf/static/pretixplugins/ticketoutputpdf/editor.js:657
|
#: pretix/plugins/ticketoutputpdf/static/pretixplugins/ticketoutputpdf/editor.js:659
|
||||||
msgid "Error while uploading your PDF file, please try again."
|
msgid "Error while uploading your PDF file, please try again."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Es gab ein Problem beim Hochladen der PDF-Datei, bitte erneut versuchen."
|
"Es gab ein Problem beim Hochladen der PDF-Datei, bitte erneut versuchen."
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -7,8 +7,8 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: \n"
|
"Project-Id-Version: \n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2017-05-18 09:17+0000\n"
|
"POT-Creation-Date: 2017-06-01 10:54+0000\n"
|
||||||
"PO-Revision-Date: 2017-05-11 12:13+0200\n"
|
"PO-Revision-Date: 2017-06-01 12:56+0200\n"
|
||||||
"Last-Translator: Raphael Michel <michel@rami.io>\n"
|
"Last-Translator: Raphael Michel <michel@rami.io>\n"
|
||||||
"Language-Team: \n"
|
"Language-Team: \n"
|
||||||
"Language: de\n"
|
"Language: de\n"
|
||||||
@@ -79,59 +79,71 @@ msgid "May 31st, 2017"
|
|||||||
msgstr "31. Mai 2017"
|
msgstr "31. Mai 2017"
|
||||||
|
|
||||||
#: pretix/plugins/ticketoutputpdf/static/pretixplugins/ticketoutputpdf/editor.js:164
|
#: pretix/plugins/ticketoutputpdf/static/pretixplugins/ticketoutputpdf/editor.js:164
|
||||||
|
msgid "May 31st – June 4th, 2017"
|
||||||
|
msgstr "31. Mai – 4. Juni 2017"
|
||||||
|
|
||||||
|
#: pretix/plugins/ticketoutputpdf/static/pretixplugins/ticketoutputpdf/editor.js:165
|
||||||
msgid "20:00"
|
msgid "20:00"
|
||||||
msgstr "20:00"
|
msgstr "20:00"
|
||||||
|
|
||||||
#: pretix/plugins/ticketoutputpdf/static/pretixplugins/ticketoutputpdf/editor.js:165
|
#: pretix/plugins/ticketoutputpdf/static/pretixplugins/ticketoutputpdf/editor.js:166
|
||||||
msgid "19:00"
|
msgid "19:00"
|
||||||
msgstr "19:00"
|
msgstr "19:00"
|
||||||
|
|
||||||
#: pretix/plugins/ticketoutputpdf/static/pretixplugins/ticketoutputpdf/editor.js:166
|
#: pretix/plugins/ticketoutputpdf/static/pretixplugins/ticketoutputpdf/editor.js:167
|
||||||
msgid "2017-05-31 20:00"
|
msgid "2017-05-31 20:00"
|
||||||
msgstr "31.05.2016 20:00"
|
msgstr "31.05.2016 20:00"
|
||||||
|
|
||||||
#: pretix/plugins/ticketoutputpdf/static/pretixplugins/ticketoutputpdf/editor.js:167
|
#: pretix/plugins/ticketoutputpdf/static/pretixplugins/ticketoutputpdf/editor.js:168
|
||||||
msgid "2017-05-31 19:00"
|
msgid "2017-05-31 19:00"
|
||||||
msgstr "31.05.2016 19:00"
|
msgstr "31.05.2016 19:00"
|
||||||
|
|
||||||
#: pretix/plugins/ticketoutputpdf/static/pretixplugins/ticketoutputpdf/editor.js:168
|
#: pretix/plugins/ticketoutputpdf/static/pretixplugins/ticketoutputpdf/editor.js:169
|
||||||
msgid "Random City"
|
msgid "Random City"
|
||||||
msgstr "Musterstadt"
|
msgstr "Musterstadt"
|
||||||
|
|
||||||
#: pretix/plugins/ticketoutputpdf/static/pretixplugins/ticketoutputpdf/editor.js:213
|
#: pretix/plugins/ticketoutputpdf/static/pretixplugins/ticketoutputpdf/editor.js:170
|
||||||
|
msgid ""
|
||||||
|
"Addon 1\n"
|
||||||
|
"Addon 2"
|
||||||
|
msgstr ""
|
||||||
|
"Workshop 1\n"
|
||||||
|
"Workshop 2"
|
||||||
|
|
||||||
|
#: pretix/plugins/ticketoutputpdf/static/pretixplugins/ticketoutputpdf/editor.js:215
|
||||||
msgid "The PDF background file could not be loaded for the following reason:"
|
msgid "The PDF background file could not be loaded for the following reason:"
|
||||||
msgstr "Die Hintergrund-PDF-Datei konnte nicht geladen werden:"
|
msgstr "Die Hintergrund-PDF-Datei konnte nicht geladen werden:"
|
||||||
|
|
||||||
#: pretix/plugins/ticketoutputpdf/static/pretixplugins/ticketoutputpdf/editor.js:362
|
#: pretix/plugins/ticketoutputpdf/static/pretixplugins/ticketoutputpdf/editor.js:364
|
||||||
msgid "Group of objects"
|
msgid "Group of objects"
|
||||||
msgstr "Gruppe von Objekten"
|
msgstr "Gruppe von Objekten"
|
||||||
|
|
||||||
#: pretix/plugins/ticketoutputpdf/static/pretixplugins/ticketoutputpdf/editor.js:368
|
#: pretix/plugins/ticketoutputpdf/static/pretixplugins/ticketoutputpdf/editor.js:370
|
||||||
msgid "Text object"
|
msgid "Text object"
|
||||||
msgstr "Text-Objekt"
|
msgstr "Text-Objekt"
|
||||||
|
|
||||||
#: pretix/plugins/ticketoutputpdf/static/pretixplugins/ticketoutputpdf/editor.js:370
|
#: pretix/plugins/ticketoutputpdf/static/pretixplugins/ticketoutputpdf/editor.js:372
|
||||||
msgid "Barcode area"
|
msgid "Barcode area"
|
||||||
msgstr "QR-Code-Bereich"
|
msgstr "QR-Code-Bereich"
|
||||||
|
|
||||||
#: pretix/plugins/ticketoutputpdf/static/pretixplugins/ticketoutputpdf/editor.js:372
|
#: pretix/plugins/ticketoutputpdf/static/pretixplugins/ticketoutputpdf/editor.js:374
|
||||||
msgid "Object"
|
msgid "Object"
|
||||||
msgstr "Objekt"
|
msgstr "Objekt"
|
||||||
|
|
||||||
#: pretix/plugins/ticketoutputpdf/static/pretixplugins/ticketoutputpdf/editor.js:376
|
#: pretix/plugins/ticketoutputpdf/static/pretixplugins/ticketoutputpdf/editor.js:378
|
||||||
msgid "Ticket design"
|
msgid "Ticket design"
|
||||||
msgstr "Ticket-Design"
|
msgstr "Ticket-Design"
|
||||||
|
|
||||||
#: pretix/plugins/ticketoutputpdf/static/pretixplugins/ticketoutputpdf/editor.js:612
|
#: pretix/plugins/ticketoutputpdf/static/pretixplugins/ticketoutputpdf/editor.js:614
|
||||||
msgid "Saving failed."
|
msgid "Saving failed."
|
||||||
msgstr "Speichern fehlgeschlagen."
|
msgstr "Speichern fehlgeschlagen."
|
||||||
|
|
||||||
#: pretix/plugins/ticketoutputpdf/static/pretixplugins/ticketoutputpdf/editor.js:644
|
#: pretix/plugins/ticketoutputpdf/static/pretixplugins/ticketoutputpdf/editor.js:646
|
||||||
msgid "Do you really want to leave the editor without saving your changes?"
|
msgid "Do you really want to leave the editor without saving your changes?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Möchtest du den Editor wirklich schließen ohne Ihre Änderungen zu speichern?"
|
"Möchtest du den Editor wirklich schließen ohne Ihre Änderungen zu speichern?"
|
||||||
|
|
||||||
#: pretix/plugins/ticketoutputpdf/static/pretixplugins/ticketoutputpdf/editor.js:657
|
#: pretix/plugins/ticketoutputpdf/static/pretixplugins/ticketoutputpdf/editor.js:659
|
||||||
msgid "Error while uploading your PDF file, please try again."
|
msgid "Error while uploading your PDF file, please try again."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Es gab ein Problem beim Hochladen der PDF-Datei, bitte erneut versuchen."
|
"Es gab ein Problem beim Hochladen der PDF-Datei, bitte erneut versuchen."
|
||||||
|
|||||||
@@ -2,8 +2,9 @@ import time
|
|||||||
from urllib.parse import urlparse
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.sessions.middleware import \
|
from django.contrib.sessions.middleware import (
|
||||||
SessionMiddleware as BaseSessionMiddleware
|
SessionMiddleware as BaseSessionMiddleware,
|
||||||
|
)
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
from django.core.exceptions import DisallowedHost
|
from django.core.exceptions import DisallowedHost
|
||||||
from django.core.urlresolvers import set_urlconf
|
from django.core.urlresolvers import set_urlconf
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ def parse(data, hint):
|
|||||||
raise HintMismatchError('Invalid hint')
|
raise HintMismatchError('Invalid hint')
|
||||||
if len(data[0]) != hint['cols']:
|
if len(data[0]) != hint['cols']:
|
||||||
raise HintMismatchError('Wrong column count')
|
raise HintMismatchError('Wrong column count')
|
||||||
|
good_hint = False
|
||||||
for row in data:
|
for row in data:
|
||||||
resrow = {}
|
resrow = {}
|
||||||
if None in row or len(row) != hint['cols']:
|
if None in row or len(row) != hint['cols']:
|
||||||
@@ -31,8 +32,10 @@ def parse(data, hint):
|
|||||||
or len(resrow['reference']) == 0 or resrow['date'] == '':
|
or len(resrow['reference']) == 0 or resrow['date'] == '':
|
||||||
# This is probably a headline or something other special.
|
# This is probably a headline or something other special.
|
||||||
continue
|
continue
|
||||||
|
if resrow['reference'] or resrow['payer']:
|
||||||
|
good_hint = True
|
||||||
result.append(resrow)
|
result.append(resrow)
|
||||||
return result
|
return result, good_hint
|
||||||
|
|
||||||
|
|
||||||
def get_rows_from_file(file):
|
def get_rows_from_file(file):
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ def parse(file):
|
|||||||
result.append({
|
result.append({
|
||||||
'reference': "\n".join([
|
'reference': "\n".join([
|
||||||
t.data.get(f) for f in ('transaction_details', 'customer_reference', 'bank_reference',
|
t.data.get(f) for f in ('transaction_details', 'customer_reference', 'bank_reference',
|
||||||
'extra_details') if t.data.get(f, '')]),
|
'extra_details', 'non_swift_text') if t.data.get(f, '')]),
|
||||||
'amount': str(round_decimal(t.data['amount'].amount)),
|
'amount': str(round_decimal(t.data['amount'].amount)),
|
||||||
'date': t.data['date'].isoformat()
|
'date': t.data['date'].isoformat()
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ def _get_unknown_transactions(event: Event, job: BankImportJob, data: list):
|
|||||||
amount = Decimal("0.00")
|
amount = Decimal("0.00")
|
||||||
|
|
||||||
trans = BankTransaction(event=event, import_job=job,
|
trans = BankTransaction(event=event, import_job=job,
|
||||||
payer=row['payer'],
|
payer=row.get('payer', ''),
|
||||||
reference=row['reference'],
|
reference=row['reference'],
|
||||||
amount=amount,
|
amount=amount,
|
||||||
date=row['date'])
|
date=row['date'])
|
||||||
|
|||||||
@@ -14,9 +14,15 @@
|
|||||||
<dd>{{ payment_info.reference }}</dd>
|
<dd>{{ payment_info.reference }}</dd>
|
||||||
</dl>
|
</dl>
|
||||||
{% else %}
|
{% else %}
|
||||||
<p>{% blocktrans trimmed %}
|
{% if order.status == "p" %}
|
||||||
This order has been planned to be paid via bank transfer, but no payment has been received yet.
|
<p>{% blocktrans trimmed %}
|
||||||
{% endblocktrans %}</p>
|
This order has been marked as paid via bank transfer manually.
|
||||||
|
{% endblocktrans %}</p>
|
||||||
|
{% else %}
|
||||||
|
<p>{% blocktrans trimmed %}
|
||||||
|
This order has been planned to be paid via bank transfer, but no payment has been received yet.
|
||||||
|
{% endblocktrans %}</p>
|
||||||
|
{% endif %}
|
||||||
<dl class="dl-horizontal">
|
<dl class="dl-horizontal">
|
||||||
<dt>{% trans "Reference code" %}</dt>
|
<dt>{% trans "Reference code" %}</dt>
|
||||||
<dd>{{ order.full_code }}</dd>
|
<dd>{{ order.full_code }}</dd>
|
||||||
|
|||||||
@@ -275,12 +275,14 @@ class ImportView(EventPermissionRequiredMixin, ListView):
|
|||||||
|
|
||||||
if self.request.event.settings.get('banktransfer_csvhint') is not None:
|
if self.request.event.settings.get('banktransfer_csvhint') is not None:
|
||||||
hint = self.request.event.settings.get('banktransfer_csvhint', as_type=dict)
|
hint = self.request.event.settings.get('banktransfer_csvhint', as_type=dict)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
parsed = csvimport.parse(data, hint)
|
parsed, good = csvimport.parse(data, hint)
|
||||||
except csvimport.HintMismatchError: # TODO: narrow down
|
except csvimport.HintMismatchError: # TODO: narrow down
|
||||||
logger.exception('Import using stored hint failed')
|
logger.exception('Import using stored hint failed')
|
||||||
else:
|
else:
|
||||||
return self.start_processing(parsed)
|
if good:
|
||||||
|
return self.start_processing(parsed)
|
||||||
|
|
||||||
return self.assign_view(data)
|
return self.assign_view(data)
|
||||||
|
|
||||||
@@ -308,7 +310,7 @@ class ImportView(EventPermissionRequiredMixin, ListView):
|
|||||||
logger.error('Import using stored hint failed: ' + str(e))
|
logger.error('Import using stored hint failed: ' + str(e))
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
parsed = csvimport.parse(data, hint)
|
parsed, __ = csvimport.parse(data, hint)
|
||||||
return self.start_processing(parsed)
|
return self.start_processing(parsed)
|
||||||
|
|
||||||
def process_csv(self):
|
def process_csv(self):
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import csv
|
|
||||||
import io
|
import io
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
|
||||||
|
from defusedcsv import csv
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.db.models.functions import Coalesce
|
from django.db.models.functions import Coalesce
|
||||||
from django.utils.translation import ugettext as _, ugettext_lazy
|
from django.utils.translation import ugettext as _, ugettext_lazy
|
||||||
@@ -110,7 +110,7 @@ class CSVCheckinList(BaseCheckinList):
|
|||||||
if form_data['secrets']:
|
if form_data['secrets']:
|
||||||
row.append(op.secret)
|
row.append(op.secret)
|
||||||
if self.event.settings.attendee_emails_asked:
|
if self.event.settings.attendee_emails_asked:
|
||||||
row.append(op.attendee_email or (op.addon_to.attendee_name if op.addon_to else ''))
|
row.append(op.attendee_email or (op.addon_to.attendee_email if op.addon_to else ''))
|
||||||
acache = {}
|
acache = {}
|
||||||
for a in op.answers.all():
|
for a in op.answers.all():
|
||||||
acache[a.question_id] = str(a)
|
acache[a.question_id] = str(a)
|
||||||
|
|||||||
@@ -17,8 +17,9 @@ from django.views.generic import TemplateView, View
|
|||||||
from pretix.base.models import Checkin, Event, Order, OrderPosition
|
from pretix.base.models import Checkin, Event, Order, OrderPosition
|
||||||
from pretix.control.permissions import EventPermissionRequiredMixin
|
from pretix.control.permissions import EventPermissionRequiredMixin
|
||||||
from pretix.helpers.urls import build_absolute_uri
|
from pretix.helpers.urls import build_absolute_uri
|
||||||
from pretix.multidomain.urlreverse import \
|
from pretix.multidomain.urlreverse import (
|
||||||
build_absolute_uri as event_absolute_uri
|
build_absolute_uri as event_absolute_uri,
|
||||||
|
)
|
||||||
|
|
||||||
logger = logging.getLogger('pretix.plugins.pretixdroid')
|
logger = logging.getLogger('pretix.plugins.pretixdroid')
|
||||||
API_VERSION = 3
|
API_VERSION = 3
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% load compress %}
|
{% load compress %}
|
||||||
{% load staticfiles %}
|
{% load staticfiles %}
|
||||||
|
{% load escapejson %}
|
||||||
{% block title %}{% trans "Statistics" %}{% endblock %}
|
{% block title %}{% trans "Statistics" %}{% endblock %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h1>{% trans "Statistics" %}</h1>
|
<h1>{% trans "Statistics" %}</h1>
|
||||||
@@ -30,9 +31,9 @@
|
|||||||
<div id="obp_chart" class="chart"></div>
|
<div id="obp_chart" class="chart"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script type="application/json" id="obd-data">{{ obd_data|safe }}</script>
|
<script type="application/json" id="obd-data">{{ obd_data|escapejson }}</script>
|
||||||
<script type="application/json" id="rev-data">{{ rev_data|safe }}</script>
|
<script type="application/json" id="rev-data">{{ rev_data|escapejson }}</script>
|
||||||
<script type="application/json" id="obp-data">{{ obp_data|safe }}</script>
|
<script type="application/json" id="obp-data">{{ obp_data|escapejson }}</script>
|
||||||
<script type="application/text" id="currency">{{ request.event.currency }}</script>
|
<script type="application/text" id="currency">{{ request.event.currency }}</script>
|
||||||
<script type="application/javascript" src="{% static "pretixplugins/statistics/statistics.js" %}"></script>
|
<script type="application/javascript" src="{% static "pretixplugins/statistics/statistics.js" %}"></script>
|
||||||
{% else %}
|
{% else %}
|
||||||
|
|||||||
@@ -161,11 +161,13 @@ var editor = {
|
|||||||
"attendee_name": gettext("John Doe"),
|
"attendee_name": gettext("John Doe"),
|
||||||
"event_name": gettext("Sample event name"),
|
"event_name": gettext("Sample event name"),
|
||||||
"event_date": gettext("May 31st, 2017"),
|
"event_date": gettext("May 31st, 2017"),
|
||||||
|
"event_date_range": gettext("May 31st – June 4th, 2017"),
|
||||||
"event_begin_time": gettext("20:00"),
|
"event_begin_time": gettext("20:00"),
|
||||||
"event_admission_time": gettext("19:00"),
|
"event_admission_time": gettext("19:00"),
|
||||||
"event_begin": gettext("2017-05-31 20:00"),
|
"event_begin": gettext("2017-05-31 20:00"),
|
||||||
"event_admission": gettext("2017-05-31 19:00"),
|
"event_admission": gettext("2017-05-31 19:00"),
|
||||||
"event_location": gettext("Random City")
|
"event_location": gettext("Random City"),
|
||||||
|
"addons": gettext("Addon 1\nAddon 2"),
|
||||||
},
|
},
|
||||||
|
|
||||||
_load_pdf: function (dump) {
|
_load_pdf: function (dump) {
|
||||||
|
|||||||
@@ -267,11 +267,13 @@
|
|||||||
<option value="attendee_name">{% trans "Attendee name" %}</option>
|
<option value="attendee_name">{% trans "Attendee name" %}</option>
|
||||||
<option value="event_name">{% trans "Event name" %}</option>
|
<option value="event_name">{% trans "Event name" %}</option>
|
||||||
<option value="event_date">{% trans "Event date" %}</option>
|
<option value="event_date">{% trans "Event date" %}</option>
|
||||||
|
<option value="event_date_range">{% trans "Event date range" %}</option>
|
||||||
<option value="event_begin">{% trans "Event begin date and time" %}</option>
|
<option value="event_begin">{% trans "Event begin date and time" %}</option>
|
||||||
<option value="event_begin_time">{% trans "Event begin time" %}</option>
|
<option value="event_begin_time">{% trans "Event begin time" %}</option>
|
||||||
<option value="event_admission">{% trans "Event admission date and time" %}</option>
|
<option value="event_admission">{% trans "Event admission date and time" %}</option>
|
||||||
<option value="event_admission_time">{% trans "Event admission time" %}</option>
|
<option value="event_admission_time">{% trans "Event admission time" %}</option>
|
||||||
<option value="event_location">{% trans "Event location" %}</option>
|
<option value="event_location">{% trans "Event location" %}</option>
|
||||||
|
<option value="addons">{% trans "List of Add-Ons" %}</option>
|
||||||
<option value="other">{% trans "Other…" %}</option>
|
<option value="other">{% trans "Other…" %}</option>
|
||||||
</select>
|
</select>
|
||||||
<textarea type="text" value="" class="input-block-level form-control"
|
<textarea type="text" value="" class="input-block-level form-control"
|
||||||
|
|||||||
@@ -85,6 +85,8 @@ class PdfTicketOutput(BaseTicketOutput):
|
|||||||
return str(order.event.location).replace("\n", "<br/>\n")
|
return str(order.event.location).replace("\n", "<br/>\n")
|
||||||
elif o['content'] == 'event_date':
|
elif o['content'] == 'event_date':
|
||||||
return order.event.get_date_from_display(show_times=False)
|
return order.event.get_date_from_display(show_times=False)
|
||||||
|
elif o['content'] == 'event_date_range':
|
||||||
|
return order.event.get_date_range_display()
|
||||||
elif o['content'] == 'event_begin':
|
elif o['content'] == 'event_begin':
|
||||||
return order.event.get_date_from_display(show_times=True)
|
return order.event.get_date_from_display(show_times=True)
|
||||||
elif o['content'] == 'event_begin_time':
|
elif o['content'] == 'event_begin_time':
|
||||||
@@ -97,6 +99,11 @@ class PdfTicketOutput(BaseTicketOutput):
|
|||||||
if order.event.date_admission:
|
if order.event.date_admission:
|
||||||
tz = timezone(order.event.settings.timezone)
|
tz = timezone(order.event.settings.timezone)
|
||||||
return date_format(order.event.date_admission.astimezone(tz), "TIME_FORMAT")
|
return date_format(order.event.date_admission.astimezone(tz), "TIME_FORMAT")
|
||||||
|
elif o['content'] == 'addons':
|
||||||
|
return "<br/>".join([
|
||||||
|
'{} - {}'.format(p.item, p.variation) if p.variation else str(p.item)
|
||||||
|
for p in op.addons.select_related('item', 'variation')
|
||||||
|
])
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
def _draw_textarea(self, canvas: Canvas, op: OrderPosition, order: Order, o: dict):
|
def _draw_textarea(self, canvas: Canvas, op: OrderPosition, order: Order, o: dict):
|
||||||
@@ -226,9 +233,9 @@ class PdfTicketOutput(BaseTicketOutput):
|
|||||||
"fontfamily": "Open Sans", "bold": False, "italic": False, "width": "110.00", "content": "attendee_name",
|
"fontfamily": "Open Sans", "bold": False, "italic": False, "width": "110.00", "content": "attendee_name",
|
||||||
"text": "John Doe", "align": "left"},
|
"text": "John Doe", "align": "left"},
|
||||||
{"type": "textarea", "left": "17.50", "bottom": "242.10", "fontsize": "13.0", "color": [0, 0, 0, 1],
|
{"type": "textarea", "left": "17.50", "bottom": "242.10", "fontsize": "13.0", "color": [0, 0, 0, 1],
|
||||||
"fontfamily": "Open Sans", "bold": False, "italic": False, "width": "110.00", "content": "event_date",
|
"fontfamily": "Open Sans", "bold": False, "italic": False, "width": "110.00",
|
||||||
"text": "May 31st, 2017", "align": "left"},
|
"content": "event_date_range", "text": "May 31st, 2017", "align": "left"},
|
||||||
{"type": "textarea", "left": "17.50", "bottom": "234.30", "fontsize": "13.0", "color": [0, 0, 0, 1],
|
{"type": "textarea", "left": "17.50", "bottom": "204.80", "fontsize": "13.0", "color": [0, 0, 0, 1],
|
||||||
"fontfamily": "Open Sans", "bold": False, "italic": False, "width": "110.00", "content": "event_location",
|
"fontfamily": "Open Sans", "bold": False, "italic": False, "width": "110.00", "content": "event_location",
|
||||||
"text": "Random City", "align": "left"},
|
"text": "Random City", "align": "left"},
|
||||||
{"type": "textarea", "left": "17.50", "bottom": "194.50", "fontsize": "13.0", "color": [0, 0, 0, 1],
|
{"type": "textarea", "left": "17.50", "bottom": "194.50", "fontsize": "13.0", "color": [0, 0, 0, 1],
|
||||||
|
|||||||
@@ -86,6 +86,7 @@ class EditorView(EventPermissionRequiredMixin, ChartContainingView, TemplateView
|
|||||||
if "preview" in request.POST:
|
if "preview" in request.POST:
|
||||||
with rolledback_transaction(), language(request.event.settings.locale):
|
with rolledback_transaction(), language(request.event.settings.locale):
|
||||||
item = request.event.items.create(name=_("Sample product"), default_price=42.23)
|
item = request.event.items.create(name=_("Sample product"), default_price=42.23)
|
||||||
|
item2 = request.event.items.create(name=_("Sample workshop"), default_price=23.40)
|
||||||
|
|
||||||
from pretix.base.models import Order
|
from pretix.base.models import Order
|
||||||
order = request.event.orders.create(status=Order.STATUS_PENDING, datetime=now(),
|
order = request.event.orders.create(status=Order.STATUS_PENDING, datetime=now(),
|
||||||
@@ -93,6 +94,8 @@ class EditorView(EventPermissionRequiredMixin, ChartContainingView, TemplateView
|
|||||||
expires=now(), code="PREVIEW1234", total=119)
|
expires=now(), code="PREVIEW1234", total=119)
|
||||||
|
|
||||||
p = order.positions.create(item=item, attendee_name=_("John Doe"), price=item.default_price)
|
p = order.positions.create(item=item, attendee_name=_("John Doe"), price=item.default_price)
|
||||||
|
order.positions.create(item=item2, attendee_name=_("John Doe"), price=item.default_price, addon_to=p)
|
||||||
|
order.positions.create(item=item2, attendee_name=_("John Doe"), price=item.default_price, addon_to=p)
|
||||||
|
|
||||||
prov = PdfTicketOutput(request.event,
|
prov = PdfTicketOutput(request.event,
|
||||||
override_layout=(json.loads(request.POST.get("data"))
|
override_layout=(json.loads(request.POST.get("data"))
|
||||||
@@ -102,8 +105,7 @@ class EditorView(EventPermissionRequiredMixin, ChartContainingView, TemplateView
|
|||||||
|
|
||||||
resp = HttpResponse(data, content_type=mimet)
|
resp = HttpResponse(data, content_type=mimet)
|
||||||
ftype = fname.split(".")[-1]
|
ftype = fname.split(".")[-1]
|
||||||
resp['Content-Security-Policy'] = "style-src 'unsafe-inline'; script-src 'unsafe-inline'; object-src 'self'"
|
resp['Content-Disposition'] = 'attachment; filename="ticket-preview.{}"'.format(ftype)
|
||||||
resp['Content-Disposition'] = 'inline; filename="ticket-preview.{}"'.format(ftype)
|
|
||||||
return resp
|
return resp
|
||||||
elif "data" in request.POST:
|
elif "data" in request.POST:
|
||||||
if cf:
|
if cf:
|
||||||
|
|||||||
@@ -19,7 +19,8 @@ from pretix.presale.forms.checkout import (
|
|||||||
AddOnsForm, ContactForm, InvoiceAddressForm,
|
AddOnsForm, ContactForm, InvoiceAddressForm,
|
||||||
)
|
)
|
||||||
from pretix.presale.signals import (
|
from pretix.presale.signals import (
|
||||||
checkout_confirm_messages, checkout_flow_steps, order_meta_from_request,
|
checkout_confirm_messages, checkout_flow_steps, contact_form_fields,
|
||||||
|
order_meta_from_request,
|
||||||
)
|
)
|
||||||
from pretix.presale.views import CartMixin, get_cart, get_cart_total
|
from pretix.presale.views import CartMixin, get_cart, get_cart_total
|
||||||
from pretix.presale.views.async import AsyncAction
|
from pretix.presale.views.async import AsyncAction
|
||||||
@@ -255,10 +256,13 @@ class QuestionsStep(QuestionsViewMixin, CartMixin, TemplateFlowStep):
|
|||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def contact_form(self):
|
def contact_form(self):
|
||||||
|
initial = {
|
||||||
|
'email': self.request.session.get('email', '')
|
||||||
|
}
|
||||||
|
initial.update(self.request.session.get('contact_form_data', {}))
|
||||||
return ContactForm(data=self.request.POST if self.request.method == "POST" else None,
|
return ContactForm(data=self.request.POST if self.request.method == "POST" else None,
|
||||||
initial={
|
event=self.request.event,
|
||||||
'email': self.request.session.get('email', '')
|
initial=initial)
|
||||||
})
|
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def invoice_address(self):
|
def invoice_address(self):
|
||||||
@@ -290,6 +294,7 @@ class QuestionsStep(QuestionsViewMixin, CartMixin, TemplateFlowStep):
|
|||||||
if request.event.settings.invoice_address_asked:
|
if request.event.settings.invoice_address_asked:
|
||||||
addr = self.invoice_form.save()
|
addr = self.invoice_form.save()
|
||||||
request.session['invoice_address'] = addr.pk
|
request.session['invoice_address'] = addr.pk
|
||||||
|
request.session['contact_form_data'] = self.contact_form.cleaned_data
|
||||||
|
|
||||||
return redirect(self.get_next_url(request))
|
return redirect(self.get_next_url(request))
|
||||||
|
|
||||||
@@ -435,6 +440,18 @@ class ConfirmStep(CartMixin, AsyncAction, TemplateFlowStep):
|
|||||||
ctx['payment_provider'] = self.payment_provider
|
ctx['payment_provider'] = self.payment_provider
|
||||||
ctx['addr'] = self.invoice_address
|
ctx['addr'] = self.invoice_address
|
||||||
ctx['confirm_messages'] = self.confirm_messages
|
ctx['confirm_messages'] = self.confirm_messages
|
||||||
|
|
||||||
|
ctx['contact_info'] = []
|
||||||
|
responses = contact_form_fields.send(self.event)
|
||||||
|
for r, response in sorted(responses, key=lambda r: str(r[0])):
|
||||||
|
for key, value in response.items():
|
||||||
|
v = self.request.session.get('contact_form_data', {}).get(key)
|
||||||
|
if v is True:
|
||||||
|
v = _('Yes')
|
||||||
|
elif v is False:
|
||||||
|
v = _('No')
|
||||||
|
ctx['contact_info'].append((value.label, v))
|
||||||
|
|
||||||
return ctx
|
return ctx
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
@@ -485,9 +502,12 @@ class ConfirmStep(CartMixin, AsyncAction, TemplateFlowStep):
|
|||||||
})
|
})
|
||||||
return redirect(self.get_error_url())
|
return redirect(self.get_error_url())
|
||||||
|
|
||||||
meta_info = {}
|
meta_info = {
|
||||||
|
'contact_form_data': self.request.session.get('contact_form_data', {})
|
||||||
|
}
|
||||||
for receiver, response in order_meta_from_request.send(sender=request.event, request=request):
|
for receiver, response in order_meta_from_request.send(sender=request.event, request=request):
|
||||||
meta_info.update(response)
|
meta_info.update(response)
|
||||||
|
|
||||||
return self.do(self.request.event.id, self.payment_provider.identifier,
|
return self.do(self.request.event.id, self.payment_provider.identifier,
|
||||||
[p.id for p in self.positions], request.session.get('email'),
|
[p.id for p in self.positions], request.session.get('email'),
|
||||||
translation.get_language(), self.invoice_address.pk, meta_info)
|
translation.get_language(), self.invoice_address.pk, meta_info)
|
||||||
|
|||||||
@@ -1,19 +1,18 @@
|
|||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
from itertools import chain
|
||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.db.models import Count, Prefetch, Q
|
from django.db.models import Count, Prefetch, Q
|
||||||
from django.forms.widgets import RadioChoiceInput, RadioFieldRenderer
|
|
||||||
from django.utils.encoding import force_text
|
from django.utils.encoding import force_text
|
||||||
from django.utils.formats import number_format
|
from django.utils.formats import number_format
|
||||||
from django.utils.html import format_html
|
|
||||||
from django.utils.safestring import mark_safe
|
|
||||||
from django.utils.timezone import now
|
from django.utils.timezone import now
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from pretix.base.models import ItemVariation, Question
|
from pretix.base.models import ItemVariation, Question
|
||||||
from pretix.base.models.orders import InvoiceAddress
|
from pretix.base.models.orders import InvoiceAddress
|
||||||
from pretix.base.templatetags.rich_text import rich_text
|
from pretix.base.templatetags.rich_text import rich_text
|
||||||
|
from pretix.presale.signals import contact_form_fields
|
||||||
|
|
||||||
|
|
||||||
class ContactForm(forms.Form):
|
class ContactForm(forms.Form):
|
||||||
@@ -23,6 +22,16 @@ class ContactForm(forms.Form):
|
|||||||
'modifications to your order or download your ticket later.'),
|
'modifications to your order or download your ticket later.'),
|
||||||
widget=forms.EmailInput(attrs={'data-typocheck-target': '1'}))
|
widget=forms.EmailInput(attrs={'data-typocheck-target': '1'}))
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.event = kwargs.pop('event')
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
responses = contact_form_fields.send(self.event)
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
class InvoiceAddressForm(forms.ModelForm):
|
class InvoiceAddressForm(forms.ModelForm):
|
||||||
|
|
||||||
@@ -155,51 +164,35 @@ class QuestionsForm(forms.Form):
|
|||||||
self.fields['question_%s' % q.id] = field
|
self.fields['question_%s' % q.id] = field
|
||||||
|
|
||||||
|
|
||||||
# The following will get totally different once Django 1.11 is integrated
|
class AddOnRadioSelect(forms.RadioSelect):
|
||||||
class AddOnVariationSelectInput(RadioChoiceInput):
|
option_template_name = 'pretixpresale/forms/addon_choice_option.html'
|
||||||
|
|
||||||
def __init__(self, name, value, attrs, choice, index):
|
def optgroups(self, name, value, attrs=None):
|
||||||
super().__init__(name, value, attrs, choice, index)
|
attrs = attrs or {}
|
||||||
self.description = force_text(choice[2])
|
groups = []
|
||||||
|
has_selected = False
|
||||||
|
for index, (option_value, option_label, option_desc) in enumerate(chain(self.choices)):
|
||||||
|
if option_value is None:
|
||||||
|
option_value = ''
|
||||||
|
if isinstance(option_label, (list, tuple)):
|
||||||
|
raise TypeError('Choice groups are not supported here')
|
||||||
|
group_name = None
|
||||||
|
subgroup = []
|
||||||
|
groups.append((group_name, subgroup, index))
|
||||||
|
|
||||||
def render(self, name=None, value=None, attrs=None):
|
selected = (
|
||||||
if self.id_for_label:
|
force_text(option_value) in value and
|
||||||
label_for = format_html(' for="{}"', self.id_for_label)
|
(has_selected is False or self.allow_multiple_selected)
|
||||||
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,
|
|
||||||
)
|
)
|
||||||
|
if selected is True and has_selected is False:
|
||||||
|
has_selected = True
|
||||||
|
attrs['description'] = option_desc
|
||||||
|
subgroup.append(self.create_option(
|
||||||
|
name, option_value, option_label, selected, index,
|
||||||
|
subindex=None, attrs=attrs,
|
||||||
|
))
|
||||||
|
|
||||||
|
return groups
|
||||||
class AddOnVariationSelectRenderer(RadioFieldRenderer):
|
|
||||||
choice_input_class = AddOnVariationSelectInput
|
|
||||||
|
|
||||||
def render(self):
|
|
||||||
id_ = self.attrs.get('id')
|
|
||||||
output = []
|
|
||||||
for i, choice in enumerate(self.choices):
|
|
||||||
w = self.choice_input_class(self.name, self.value, self.attrs.copy(), choice, i)
|
|
||||||
output.append(format_html(self.inner_html, choice_value=force_text(w), sub_widgets=''))
|
|
||||||
return format_html(
|
|
||||||
self.outer_html,
|
|
||||||
id_attr=format_html(' id="{}"', id_) if id_ else '',
|
|
||||||
content=mark_safe('\n'.join(output)),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class AddOnVariationSelect(forms.RadioSelect):
|
|
||||||
renderer = AddOnVariationSelectRenderer
|
|
||||||
|
|
||||||
|
|
||||||
class AddOnVariationField(forms.ChoiceField):
|
class AddOnVariationField(forms.ChoiceField):
|
||||||
@@ -307,7 +300,7 @@ class AddOnsForm(forms.Form):
|
|||||||
choices=choices,
|
choices=choices,
|
||||||
label=i.name,
|
label=i.name,
|
||||||
required=False,
|
required=False,
|
||||||
widget=AddOnVariationSelect,
|
widget=AddOnRadioSelect,
|
||||||
help_text=rich_text(str(i.description)),
|
help_text=rich_text(str(i.description)),
|
||||||
initial=current_addons.get(i.pk),
|
initial=current_addons.get(i.pk),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -69,6 +69,18 @@ You will recieve the request triggering the order creation as the ``request`` ke
|
|||||||
As with all event-plugin signals, the ``sender`` keyword argument will contain the event.
|
As with all event-plugin signals, the ``sender`` keyword argument will contain the event.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
contact_form_fields = EventPluginSignal(
|
||||||
|
providing_args=[]
|
||||||
|
)
|
||||||
|
"""
|
||||||
|
This signals allows you to add form fields to the contact form that is presented during checkout
|
||||||
|
and by default only asks for the email address. You are supposed to return a dictionary of
|
||||||
|
form fields with globally unique keys. The validated form results will be saved into the
|
||||||
|
``contact_form_data`` entry of the order metadata dictionary.
|
||||||
|
|
||||||
|
As with all plugin signals, the ``sender`` keyword argument will contain the event.
|
||||||
|
"""
|
||||||
|
|
||||||
order_info = EventPluginSignal(
|
order_info = EventPluginSignal(
|
||||||
providing_args=["order"]
|
providing_args=["order"]
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<title>{% block thetitle %}{% endblock %}</title>
|
<title>{% block thetitle %}{% endblock %}</title>
|
||||||
{% compress css %}
|
{% compress css %}
|
||||||
<link rel="stylesheet" type="text/css" href="{% static "lightbox/css/lightbox.css" %}" />
|
<link rel="stylesheet" type="text/x-scss" href="{% static "lightbox/css/lightbox.scss" %}" />
|
||||||
{% endcompress %}
|
{% endcompress %}
|
||||||
{% if css_file %}
|
{% if css_file %}
|
||||||
<link rel="stylesheet" type="text/css" href="{{ css_file }}"/>
|
<link rel="stylesheet" type="text/css" href="{{ css_file }}"/>
|
||||||
|
|||||||
@@ -111,6 +111,12 @@
|
|||||||
<dt>{% trans "E-mail address" %}</dt>
|
<dt>{% trans "E-mail address" %}</dt>
|
||||||
<dd>{{ request.session.email }}</dd>
|
<dd>{{ request.session.email }}</dd>
|
||||||
</dl>
|
</dl>
|
||||||
|
{% for l, v in contact_info %}
|
||||||
|
<dl class="dl-horizontal">
|
||||||
|
<dt>{{ l }}</dt>
|
||||||
|
<dd>{{ v }}</dd>
|
||||||
|
</dl>
|
||||||
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -132,7 +132,8 @@
|
|||||||
<div class="col-md-8 col-xs-12">
|
<div class="col-md-8 col-xs-12">
|
||||||
{% if item.picture %}
|
{% if item.picture %}
|
||||||
<a href="{{ item.picture.url }}" class="productpicture"
|
<a href="{{ item.picture.url }}" class="productpicture"
|
||||||
data-title="{{ item.name }}"
|
data-title="{{ item.name|force_escape|force_escape }}"
|
||||||
|
{# Yes, double-escape to prevent XSS in lightbox #}
|
||||||
data-lightbox="{{ item.id }}">
|
data-lightbox="{{ item.id }}">
|
||||||
<img src="{{ item.picture|thumbnail_url:'productlist' }}"
|
<img src="{{ item.picture|thumbnail_url:'productlist' }}"
|
||||||
alt="{{ item.name }}"/>
|
alt="{{ item.name }}"/>
|
||||||
@@ -243,7 +244,7 @@
|
|||||||
<div class="col-md-8 col-xs-12">
|
<div class="col-md-8 col-xs-12">
|
||||||
{% if item.picture %}
|
{% if item.picture %}
|
||||||
<a href="{{ item.picture.url }}" class="productpicture"
|
<a href="{{ item.picture.url }}" class="productpicture"
|
||||||
data-title="{{ item.name }}"
|
data-title="{{ item.name|force_escape|force_escape }}"
|
||||||
data-lightbox="{{ item.id }}">
|
data-lightbox="{{ item.id }}">
|
||||||
<img src="{{ item.picture|thumbnail_url:'productlist' }}"
|
<img src="{{ item.picture|thumbnail_url:'productlist' }}"
|
||||||
alt="{{ item.name }}"/>
|
alt="{{ item.name }}"/>
|
||||||
|
|||||||
@@ -29,7 +29,8 @@
|
|||||||
<div class="col-md-8 col-xs-12">
|
<div class="col-md-8 col-xs-12">
|
||||||
{% if item.picture %}
|
{% if item.picture %}
|
||||||
<a href="{{ item.picture.url }}" class="productpicture"
|
<a href="{{ item.picture.url }}" class="productpicture"
|
||||||
data-title="{{ item.name }}"
|
data-title="{{ item.name|force_escape|force_escape }}"
|
||||||
|
{# Yes, double-escape to prevent XSS in lightbox #}
|
||||||
data-lightbox="{{ item.id }}">
|
data-lightbox="{{ item.id }}">
|
||||||
<img src="{{ item.picture|thumbnail_url:'productlist' }}"
|
<img src="{{ item.picture|thumbnail_url:'productlist' }}"
|
||||||
alt="{{ item.name }}"/>
|
alt="{{ item.name }}"/>
|
||||||
@@ -116,7 +117,8 @@
|
|||||||
<div class="col-md-8 col-xs-12">
|
<div class="col-md-8 col-xs-12">
|
||||||
{% if item.picture %}
|
{% if item.picture %}
|
||||||
<a href="{{ item.picture.url }}" class="productpicture"
|
<a href="{{ item.picture.url }}" class="productpicture"
|
||||||
data-title="{{ item.name }}"
|
data-title="{{ item.name|force_escape|force_escape }}"
|
||||||
|
{# Yes, double-escape to prevent XSS in lightbox #}
|
||||||
data-lightbox="{{ item.id }}">
|
data-lightbox="{{ item.id }}">
|
||||||
<img src="{{ item.picture|thumbnail_url:'productlist' }}"
|
<img src="{{ item.picture|thumbnail_url:'productlist' }}"
|
||||||
alt="{{ item.name }}"/>
|
alt="{{ item.name }}"/>
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{% load rich_text %}
|
||||||
|
<label{% if widget.attrs.id %} for="{{ widget.attrs.id }}"{% endif %}>{% include "django/forms/widgets/input.html" %} {{ widget.label }}</label> {% if widget.attrs.description %}<span class="fa fa-info-circle toggle-variation-description"></span>
|
||||||
|
<div class="variation-description addon-variation-description">{{ widget.attrs.description|rich_text }}</div>{% endif %}
|
||||||
@@ -581,15 +581,9 @@ class OrderDownload(EventViewMixin, OrderDetailMixin, View):
|
|||||||
return render(self.request, "pretixbase/cachedfiles/pending.html", {})
|
return render(self.request, "pretixbase/cachedfiles/pending.html", {})
|
||||||
else:
|
else:
|
||||||
resp = FileResponse(ct.file.file, content_type=ct.type)
|
resp = FileResponse(ct.file.file, content_type=ct.type)
|
||||||
if ct.type == "application/pdf":
|
resp['Content-Disposition'] = 'attachment; filename="{}-{}-{}{}"'.format(
|
||||||
resp['Content-Security-Policy'] = "style-src 'unsafe-inline'; script-src 'unsafe-inline'; object-src 'self'"
|
self.request.event.slug.upper(), self.order.code, self.output.identifier, ct.extension
|
||||||
resp['Content-Disposition'] = 'inline; filename="{}-{}-{}{}"'.format(
|
)
|
||||||
self.request.event.slug.upper(), self.order.code, self.output.identifier, ct.extension
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
resp['Content-Disposition'] = 'attachment; filename="{}-{}-{}{}"'.format(
|
|
||||||
self.request.event.slug.upper(), self.order.code, self.output.identifier, ct.extension
|
|
||||||
)
|
|
||||||
return resp
|
return resp
|
||||||
|
|
||||||
def _download_position(self):
|
def _download_position(self):
|
||||||
@@ -620,17 +614,10 @@ class OrderDownload(EventViewMixin, OrderDetailMixin, View):
|
|||||||
return render(self.request, "pretixbase/cachedfiles/pending.html", {})
|
return render(self.request, "pretixbase/cachedfiles/pending.html", {})
|
||||||
else:
|
else:
|
||||||
resp = FileResponse(ct.file.file, content_type=ct.type)
|
resp = FileResponse(ct.file.file, content_type=ct.type)
|
||||||
if ct.type == "application/pdf":
|
resp['Content-Disposition'] = 'attachment; filename="{}-{}-{}-{}{}"'.format(
|
||||||
resp['Content-Security-Policy'] = "style-src 'unsafe-inline'; script-src 'unsafe-inline'; object-src 'self'"
|
self.request.event.slug.upper(), self.order.code, self.order_position.positionid,
|
||||||
resp['Content-Disposition'] = 'inline; filename="{}-{}-{}-{}{}"'.format(
|
self.output.identifier, ct.extension
|
||||||
self.request.event.slug.upper(), self.order.code, self.order_position.positionid,
|
)
|
||||||
self.output.identifier, ct.extension
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
resp['Content-Disposition'] = 'attachment; filename="{}-{}-{}-{}{}"'.format(
|
|
||||||
self.request.event.slug.upper(), self.order.code, self.order_position.positionid,
|
|
||||||
self.output.identifier, ct.extension
|
|
||||||
)
|
|
||||||
return resp
|
return resp
|
||||||
|
|
||||||
|
|
||||||
@@ -660,6 +647,5 @@ class InvoiceDownload(EventViewMixin, OrderDetailMixin, View):
|
|||||||
return redirect(self.get_order_url())
|
return redirect(self.get_order_url())
|
||||||
|
|
||||||
resp = FileResponse(invoice.file.file, content_type='application/pdf')
|
resp = FileResponse(invoice.file.file, content_type='application/pdf')
|
||||||
resp['Content-Security-Policy'] = "style-src 'unsafe-inline'; script-src 'unsafe-inline'; object-src 'self'"
|
resp['Content-Disposition'] = 'attachment; filename="{}.pdf"'.format(invoice.number)
|
||||||
resp['Content-Disposition'] = 'inline; filename="{}.pdf"'.format(invoice.number)
|
|
||||||
return resp
|
return resp
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ PRETIX_PASSWORD_RESET = config.getboolean('pretix', 'password_reset', fallback=T
|
|||||||
SITE_URL = config.get('pretix', 'url', fallback='http://localhost')
|
SITE_URL = config.get('pretix', 'url', fallback='http://localhost')
|
||||||
|
|
||||||
PRETIX_PLUGINS_DEFAULT = config.get('pretix', 'plugins_default',
|
PRETIX_PLUGINS_DEFAULT = config.get('pretix', 'plugins_default',
|
||||||
fallback='pretix.plugins.sendmail,pretix.plugins.statistics')
|
fallback='pretix.plugins.sendmail,pretix.plugins.statistics,pretix.plugins.checkinlists')
|
||||||
|
|
||||||
DEFAULT_CURRENCY = config.get('pretix', 'currency', fallback='EUR')
|
DEFAULT_CURRENCY = config.get('pretix', 'currency', fallback='EUR')
|
||||||
CURRENCIES = list(currencies)
|
CURRENCIES = list(currencies)
|
||||||
@@ -258,7 +258,7 @@ try:
|
|||||||
import debug_toolbar # noqa
|
import debug_toolbar # noqa
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
INSTALLED_APPS.append('debug_toolbar.apps.DebugToolbarConfig')
|
INSTALLED_APPS.append('debug_toolbar.apps.DebugToolbarConfig')
|
||||||
MIDDLEWARE.insert(0, 'pretix.helpers.debug.DebugMiddlewareCompatibilityShim')
|
MIDDLEWARE.insert(0, 'debug_toolbar.middleware.DebugToolbarMiddleware')
|
||||||
except ImportError:
|
except ImportError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -376,7 +376,9 @@ COMPRESS_PRECOMPILERS = (
|
|||||||
COMPRESS_ENABLED = COMPRESS_OFFLINE = not debug_fallback
|
COMPRESS_ENABLED = COMPRESS_OFFLINE = not debug_fallback
|
||||||
|
|
||||||
COMPRESS_CSS_FILTERS = (
|
COMPRESS_CSS_FILTERS = (
|
||||||
'compressor.filters.css_default.CssAbsoluteFilter',
|
# CssAbsoluteFilter is incredibly slow, especially when dealing with our _flags.scss
|
||||||
|
# However, we don't need it if we consequently use the static() function in Sass
|
||||||
|
# 'compressor.filters.css_default.CssAbsoluteFilter',
|
||||||
'compressor.filters.cssmin.CSSCompressorFilter',
|
'compressor.filters.cssmin.CSSCompressorFilter',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/* @license
|
/* @license
|
||||||
morris.js v0.5.0
|
morris.js v0.5.1
|
||||||
Copyright 2014 Olly Smith All rights reserved.
|
Copyright 2014 Olly Smith All rights reserved.
|
||||||
Licensed under the BSD-2-Clause License.
|
Licensed under the BSD-2-Clause License.
|
||||||
*/
|
*/
|
||||||
@@ -74,6 +74,7 @@ Licensed under the BSD-2-Clause License.
|
|||||||
__extends(Grid, _super);
|
__extends(Grid, _super);
|
||||||
|
|
||||||
function Grid(options) {
|
function Grid(options) {
|
||||||
|
this.hasToShow = __bind(this.hasToShow, this);
|
||||||
this.resizeHandler = __bind(this.resizeHandler, this);
|
this.resizeHandler = __bind(this.resizeHandler, this);
|
||||||
var _this = this;
|
var _this = this;
|
||||||
if (typeof options.element === 'string') {
|
if (typeof options.element === 'string') {
|
||||||
@@ -197,7 +198,7 @@ Licensed under the BSD-2-Clause License.
|
|||||||
};
|
};
|
||||||
|
|
||||||
Grid.prototype.setData = function(data, redraw) {
|
Grid.prototype.setData = function(data, redraw) {
|
||||||
var e, idx, index, maxGoal, minGoal, ret, row, step, total, y, ykey, ymax, ymin, yval, _ref;
|
var e, flatEvents, from, idx, index, maxGoal, minGoal, ret, row, step, to, total, y, ykey, ymax, ymin, yval, _i, _len, _ref, _ref1;
|
||||||
if (redraw == null) {
|
if (redraw == null) {
|
||||||
redraw = true;
|
redraw = true;
|
||||||
}
|
}
|
||||||
@@ -254,7 +255,7 @@ Licensed under the BSD-2-Clause License.
|
|||||||
if ((yval != null) && typeof yval !== 'number') {
|
if ((yval != null) && typeof yval !== 'number') {
|
||||||
yval = null;
|
yval = null;
|
||||||
}
|
}
|
||||||
if (yval != null) {
|
if ((yval != null) && this.hasToShow(idx)) {
|
||||||
if (this.cumulative) {
|
if (this.cumulative) {
|
||||||
total += yval;
|
total += yval;
|
||||||
} else {
|
} else {
|
||||||
@@ -288,21 +289,24 @@ Licensed under the BSD-2-Clause License.
|
|||||||
this.events = [];
|
this.events = [];
|
||||||
if (this.options.events.length > 0) {
|
if (this.options.events.length > 0) {
|
||||||
if (this.options.parseTime) {
|
if (this.options.parseTime) {
|
||||||
this.events = (function() {
|
_ref = this.options.events;
|
||||||
var _i, _len, _ref, _results;
|
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||||
_ref = this.options.events;
|
e = _ref[_i];
|
||||||
_results = [];
|
if (e instanceof Array) {
|
||||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
from = e[0], to = e[1];
|
||||||
e = _ref[_i];
|
this.events.push([Morris.parseDate(from), Morris.parseDate(to)]);
|
||||||
_results.push(Morris.parseDate(e));
|
} else {
|
||||||
|
this.events.push(Morris.parseDate(e));
|
||||||
}
|
}
|
||||||
return _results;
|
}
|
||||||
}).call(this);
|
|
||||||
} else {
|
} else {
|
||||||
this.events = this.options.events;
|
this.events = this.options.events;
|
||||||
}
|
}
|
||||||
this.xmax = Math.max(this.xmax, Math.max.apply(Math, this.events));
|
flatEvents = $.map(this.events, function(e) {
|
||||||
this.xmin = Math.min(this.xmin, Math.min.apply(Math, this.events));
|
return e;
|
||||||
|
});
|
||||||
|
this.xmax = Math.max(this.xmax, Math.max.apply(Math, flatEvents));
|
||||||
|
this.xmin = Math.min(this.xmin, Math.min.apply(Math, flatEvents));
|
||||||
}
|
}
|
||||||
if (this.xmin === this.xmax) {
|
if (this.xmin === this.xmax) {
|
||||||
this.xmin -= 1;
|
this.xmin -= 1;
|
||||||
@@ -316,7 +320,7 @@ Licensed under the BSD-2-Clause License.
|
|||||||
}
|
}
|
||||||
this.ymax += 1;
|
this.ymax += 1;
|
||||||
}
|
}
|
||||||
if (((_ref = this.options.axes) === true || _ref === 'both' || _ref === 'y') || this.options.grid === true) {
|
if (((_ref1 = this.options.axes) === true || _ref1 === 'both' || _ref1 === 'y') || this.options.grid === true) {
|
||||||
if (this.options.ymax === this.gridDefaults.ymax && this.options.ymin === this.gridDefaults.ymin) {
|
if (this.options.ymax === this.gridDefaults.ymax && this.options.ymin === this.gridDefaults.ymin) {
|
||||||
this.grid = this.autoGridLines(this.ymin, this.ymax, this.options.numLines);
|
this.grid = this.autoGridLines(this.ymin, this.ymax, this.options.numLines);
|
||||||
this.ymin = Math.min(this.ymin, this.grid[0]);
|
this.ymin = Math.min(this.ymin, this.grid[0]);
|
||||||
@@ -324,9 +328,9 @@ Licensed under the BSD-2-Clause License.
|
|||||||
} else {
|
} else {
|
||||||
step = (this.ymax - this.ymin) / (this.options.numLines - 1);
|
step = (this.ymax - this.ymin) / (this.options.numLines - 1);
|
||||||
this.grid = (function() {
|
this.grid = (function() {
|
||||||
var _i, _ref1, _ref2, _results;
|
var _j, _ref2, _ref3, _results;
|
||||||
_results = [];
|
_results = [];
|
||||||
for (y = _i = _ref1 = this.ymin, _ref2 = this.ymax; step > 0 ? _i <= _ref2 : _i >= _ref2; y = _i += step) {
|
for (y = _j = _ref2 = this.ymin, _ref3 = this.ymax; step > 0 ? _j <= _ref3 : _j >= _ref3; y = _j += step) {
|
||||||
_results.push(y);
|
_results.push(y);
|
||||||
}
|
}
|
||||||
return _results;
|
return _results;
|
||||||
@@ -405,7 +409,7 @@ Licensed under the BSD-2-Clause License.
|
|||||||
};
|
};
|
||||||
|
|
||||||
Grid.prototype._calc = function() {
|
Grid.prototype._calc = function() {
|
||||||
var bottomOffsets, gridLine, h, i, w, yLabelWidths, _ref, _ref1;
|
var angle, bottomOffsets, gridLine, h, i, w, yLabelWidths, _ref, _ref1;
|
||||||
w = this.el.width();
|
w = this.el.width();
|
||||||
h = this.el.height();
|
h = this.el.height();
|
||||||
if (this.elementWidth !== w || this.elementHeight !== h || this.dirty) {
|
if (this.elementWidth !== w || this.elementHeight !== h || this.dirty) {
|
||||||
@@ -427,23 +431,53 @@ Licensed under the BSD-2-Clause License.
|
|||||||
}
|
}
|
||||||
return _results;
|
return _results;
|
||||||
}).call(this);
|
}).call(this);
|
||||||
this.left += Math.max.apply(Math, yLabelWidths);
|
if (!this.options.horizontal) {
|
||||||
|
this.left += Math.max.apply(Math, yLabelWidths);
|
||||||
|
} else {
|
||||||
|
this.bottom -= Math.max.apply(Math, yLabelWidths);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if ((_ref1 = this.options.axes) === true || _ref1 === 'both' || _ref1 === 'x') {
|
if ((_ref1 = this.options.axes) === true || _ref1 === 'both' || _ref1 === 'x') {
|
||||||
|
if (!this.options.horizontal) {
|
||||||
|
angle = -this.options.xLabelAngle;
|
||||||
|
} else {
|
||||||
|
angle = -90;
|
||||||
|
}
|
||||||
bottomOffsets = (function() {
|
bottomOffsets = (function() {
|
||||||
var _i, _ref2, _results;
|
var _i, _ref2, _results;
|
||||||
_results = [];
|
_results = [];
|
||||||
for (i = _i = 0, _ref2 = this.data.length; 0 <= _ref2 ? _i < _ref2 : _i > _ref2; i = 0 <= _ref2 ? ++_i : --_i) {
|
for (i = _i = 0, _ref2 = this.data.length; 0 <= _ref2 ? _i < _ref2 : _i > _ref2; i = 0 <= _ref2 ? ++_i : --_i) {
|
||||||
_results.push(this.measureText(this.data[i].text, -this.options.xLabelAngle).height);
|
_results.push(this.measureText(this.data[i].label, angle).height);
|
||||||
}
|
}
|
||||||
return _results;
|
return _results;
|
||||||
}).call(this);
|
}).call(this);
|
||||||
this.bottom -= Math.max.apply(Math, bottomOffsets);
|
if (!this.options.horizontal) {
|
||||||
|
this.bottom -= Math.max.apply(Math, bottomOffsets);
|
||||||
|
} else {
|
||||||
|
this.left += Math.max.apply(Math, bottomOffsets);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this.width = Math.max(1, this.right - this.left);
|
this.width = Math.max(1, this.right - this.left);
|
||||||
this.height = Math.max(1, this.bottom - this.top);
|
this.height = Math.max(1, this.bottom - this.top);
|
||||||
this.dx = this.width / (this.xmax - this.xmin);
|
if (!this.options.horizontal) {
|
||||||
this.dy = this.height / (this.ymax - this.ymin);
|
this.dx = this.width / (this.xmax - this.xmin);
|
||||||
|
this.dy = this.height / (this.ymax - this.ymin);
|
||||||
|
this.yStart = this.bottom;
|
||||||
|
this.yEnd = this.top;
|
||||||
|
this.xStart = this.left;
|
||||||
|
this.xEnd = this.right;
|
||||||
|
this.xSize = this.width;
|
||||||
|
this.ySize = this.height;
|
||||||
|
} else {
|
||||||
|
this.dx = this.height / (this.xmax - this.xmin);
|
||||||
|
this.dy = this.width / (this.ymax - this.ymin);
|
||||||
|
this.yStart = this.left;
|
||||||
|
this.yEnd = this.right;
|
||||||
|
this.xStart = this.top;
|
||||||
|
this.xEnd = this.bottom;
|
||||||
|
this.xSize = this.height;
|
||||||
|
this.ySize = this.width;
|
||||||
|
}
|
||||||
if (this.calc) {
|
if (this.calc) {
|
||||||
return this.calc();
|
return this.calc();
|
||||||
}
|
}
|
||||||
@@ -451,14 +485,18 @@ Licensed under the BSD-2-Clause License.
|
|||||||
};
|
};
|
||||||
|
|
||||||
Grid.prototype.transY = function(y) {
|
Grid.prototype.transY = function(y) {
|
||||||
return this.bottom - (y - this.ymin) * this.dy;
|
if (!this.options.horizontal) {
|
||||||
|
return this.bottom - (y - this.ymin) * this.dy;
|
||||||
|
} else {
|
||||||
|
return this.left + (y - this.ymin) * this.dy;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Grid.prototype.transX = function(x) {
|
Grid.prototype.transX = function(x) {
|
||||||
if (this.data.length === 1) {
|
if (this.data.length === 1) {
|
||||||
return (this.left + this.right) / 2;
|
return (this.xStart + this.xEnd) / 2;
|
||||||
} else {
|
} else {
|
||||||
return this.left + (x - this.xmin) * this.dx;
|
return this.xStart + (x - this.xmin) * this.dx;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -485,32 +523,50 @@ Licensed under the BSD-2-Clause License.
|
|||||||
};
|
};
|
||||||
|
|
||||||
Grid.prototype.yAxisFormat = function(label) {
|
Grid.prototype.yAxisFormat = function(label) {
|
||||||
return this.yLabelFormat(label);
|
return this.yLabelFormat(label, 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
Grid.prototype.yLabelFormat = function(label) {
|
Grid.prototype.yLabelFormat = function(label, i) {
|
||||||
if (typeof this.options.yLabelFormat === 'function') {
|
if (typeof this.options.yLabelFormat === 'function') {
|
||||||
return this.options.yLabelFormat(label);
|
return this.options.yLabelFormat(label, i);
|
||||||
} else {
|
} else {
|
||||||
return "" + this.options.preUnits + (Morris.commas(label)) + this.options.postUnits;
|
return "" + this.options.preUnits + (Morris.commas(label)) + this.options.postUnits;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Grid.prototype.getYAxisLabelX = function() {
|
||||||
|
return this.left - this.options.padding / 2;
|
||||||
|
};
|
||||||
|
|
||||||
Grid.prototype.drawGrid = function() {
|
Grid.prototype.drawGrid = function() {
|
||||||
var lineY, y, _i, _len, _ref, _ref1, _ref2, _results;
|
var basePos, lineY, pos, _i, _len, _ref, _ref1, _ref2, _results;
|
||||||
if (this.options.grid === false && ((_ref = this.options.axes) !== true && _ref !== 'both' && _ref !== 'y')) {
|
if (this.options.grid === false && ((_ref = this.options.axes) !== true && _ref !== 'both' && _ref !== 'y')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (!this.options.horizontal) {
|
||||||
|
basePos = this.getYAxisLabelX();
|
||||||
|
} else {
|
||||||
|
basePos = this.getXAxisLabelY();
|
||||||
|
}
|
||||||
_ref1 = this.grid;
|
_ref1 = this.grid;
|
||||||
_results = [];
|
_results = [];
|
||||||
for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
|
for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
|
||||||
lineY = _ref1[_i];
|
lineY = _ref1[_i];
|
||||||
y = this.transY(lineY);
|
pos = this.transY(lineY);
|
||||||
if ((_ref2 = this.options.axes) === true || _ref2 === 'both' || _ref2 === 'y') {
|
if ((_ref2 = this.options.axes) === true || _ref2 === 'both' || _ref2 === 'y') {
|
||||||
this.drawYAxisLabel(this.left - this.options.padding / 2, y, this.yAxisFormat(lineY));
|
if (!this.options.horizontal) {
|
||||||
|
this.drawYAxisLabel(basePos, pos, this.yAxisFormat(lineY));
|
||||||
|
} else {
|
||||||
|
this.drawXAxisLabel(pos, basePos, this.yAxisFormat(lineY));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (this.options.grid) {
|
if (this.options.grid) {
|
||||||
_results.push(this.drawGridLine("M" + this.left + "," + y + "H" + (this.left + this.width)));
|
pos = Math.floor(pos) + 0.5;
|
||||||
|
if (!this.options.horizontal) {
|
||||||
|
_results.push(this.drawGridLine("M" + this.xStart + "," + pos + "H" + this.xEnd));
|
||||||
|
} else {
|
||||||
|
_results.push(this.drawGridLine("M" + pos + "," + this.xStart + "V" + this.xEnd));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
_results.push(void 0);
|
_results.push(void 0);
|
||||||
}
|
}
|
||||||
@@ -543,11 +599,42 @@ Licensed under the BSD-2-Clause License.
|
|||||||
};
|
};
|
||||||
|
|
||||||
Grid.prototype.drawGoal = function(goal, color) {
|
Grid.prototype.drawGoal = function(goal, color) {
|
||||||
return this.raphael.path("M" + this.left + "," + (this.transY(goal)) + "H" + this.right).attr('stroke', color).attr('stroke-width', this.options.goalStrokeWidth);
|
var path, y;
|
||||||
|
y = Math.floor(this.transY(goal)) + 0.5;
|
||||||
|
if (!this.options.horizontal) {
|
||||||
|
path = "M" + this.xStart + "," + y + "H" + this.xEnd;
|
||||||
|
} else {
|
||||||
|
path = "M" + y + "," + this.xStart + "V" + this.xEnd;
|
||||||
|
}
|
||||||
|
return this.raphael.path(path).attr('stroke', color).attr('stroke-width', this.options.goalStrokeWidth);
|
||||||
};
|
};
|
||||||
|
|
||||||
Grid.prototype.drawEvent = function(event, color) {
|
Grid.prototype.drawEvent = function(event, color) {
|
||||||
return this.raphael.path("M" + (this.transX(event)) + "," + this.bottom + "V" + this.top).attr('stroke', color).attr('stroke-width', this.options.eventStrokeWidth);
|
var from, path, to, x;
|
||||||
|
if (event instanceof Array) {
|
||||||
|
from = event[0], to = event[1];
|
||||||
|
from = Math.floor(this.transX(from)) + 0.5;
|
||||||
|
to = Math.floor(this.transX(to)) + 0.5;
|
||||||
|
if (!this.options.horizontal) {
|
||||||
|
return this.raphael.rect(from, this.yEnd, to - from, this.yStart - this.yEnd).attr({
|
||||||
|
fill: color,
|
||||||
|
stroke: false
|
||||||
|
}).toBack();
|
||||||
|
} else {
|
||||||
|
return this.raphael.rect(this.yStart, from, this.yEnd - this.yStart, to - from).attr({
|
||||||
|
fill: color,
|
||||||
|
stroke: false
|
||||||
|
}).toBack();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
x = Math.floor(this.transX(event)) + 0.5;
|
||||||
|
if (!this.options.horizontal) {
|
||||||
|
path = "M" + x + "," + this.yStart + "V" + this.yEnd;
|
||||||
|
} else {
|
||||||
|
path = "M" + this.yStart + "," + x + "H" + this.yEnd;
|
||||||
|
}
|
||||||
|
return this.raphael.path(path).attr('stroke', color).attr('stroke-width', this.options.eventStrokeWidth);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Grid.prototype.drawYAxisLabel = function(xPos, yPos, text) {
|
Grid.prototype.drawYAxisLabel = function(xPos, yPos, text) {
|
||||||
@@ -586,6 +673,10 @@ Licensed under the BSD-2-Clause License.
|
|||||||
return this.redraw();
|
return this.redraw();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Grid.prototype.hasToShow = function(i) {
|
||||||
|
return this.options.shown === true || this.options.shown[i] === true;
|
||||||
|
};
|
||||||
|
|
||||||
return Grid;
|
return Grid;
|
||||||
|
|
||||||
})(Morris.EventEmitter);
|
})(Morris.EventEmitter);
|
||||||
@@ -662,13 +753,13 @@ Licensed under the BSD-2-Clause License.
|
|||||||
this.options.parent.append(this.el);
|
this.options.parent.append(this.el);
|
||||||
}
|
}
|
||||||
|
|
||||||
Hover.prototype.update = function(html, x, y) {
|
Hover.prototype.update = function(html, x, y, centre_y) {
|
||||||
if (!html) {
|
if (!html) {
|
||||||
return this.hide();
|
return this.hide();
|
||||||
} else {
|
} else {
|
||||||
this.html(html);
|
this.html(html);
|
||||||
this.show();
|
this.show();
|
||||||
return this.moveTo(x, y);
|
return this.moveTo(x, y, centre_y);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -676,7 +767,7 @@ Licensed under the BSD-2-Clause License.
|
|||||||
return this.el.html(content);
|
return this.el.html(content);
|
||||||
};
|
};
|
||||||
|
|
||||||
Hover.prototype.moveTo = function(x, y) {
|
Hover.prototype.moveTo = function(x, y, centre_y) {
|
||||||
var hoverHeight, hoverWidth, left, parentHeight, parentWidth, top;
|
var hoverHeight, hoverWidth, left, parentHeight, parentWidth, top;
|
||||||
parentWidth = this.options.parent.innerWidth();
|
parentWidth = this.options.parent.innerWidth();
|
||||||
parentHeight = this.options.parent.innerHeight();
|
parentHeight = this.options.parent.innerHeight();
|
||||||
@@ -684,11 +775,18 @@ Licensed under the BSD-2-Clause License.
|
|||||||
hoverHeight = this.el.outerHeight();
|
hoverHeight = this.el.outerHeight();
|
||||||
left = Math.min(Math.max(0, x - hoverWidth / 2), parentWidth - hoverWidth);
|
left = Math.min(Math.max(0, x - hoverWidth / 2), parentWidth - hoverWidth);
|
||||||
if (y != null) {
|
if (y != null) {
|
||||||
top = y - hoverHeight - 10;
|
if (centre_y === true) {
|
||||||
if (top < 0) {
|
top = y - hoverHeight / 2;
|
||||||
top = y + 10;
|
if (top < 0) {
|
||||||
if (top + hoverHeight > parentHeight) {
|
top = 0;
|
||||||
top = parentHeight / 2 - hoverHeight / 2;
|
}
|
||||||
|
} else {
|
||||||
|
top = y - hoverHeight - 10;
|
||||||
|
if (top < 0) {
|
||||||
|
top = y + 10;
|
||||||
|
if (top + hoverHeight > parentHeight) {
|
||||||
|
top = parentHeight / 2 - hoverHeight / 2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -745,10 +843,14 @@ Licensed under the BSD-2-Clause License.
|
|||||||
pointStrokeColors: ['#ffffff'],
|
pointStrokeColors: ['#ffffff'],
|
||||||
pointFillColors: [],
|
pointFillColors: [],
|
||||||
smooth: true,
|
smooth: true,
|
||||||
|
shown: true,
|
||||||
xLabels: 'auto',
|
xLabels: 'auto',
|
||||||
xLabelFormat: null,
|
xLabelFormat: null,
|
||||||
xLabelMargin: 24,
|
xLabelMargin: 24,
|
||||||
hideHover: false
|
hideHover: false,
|
||||||
|
trendLine: false,
|
||||||
|
trendLineWidth: 2,
|
||||||
|
trendLineColors: ['#689bc3', '#a2b3bf', '#64b764']
|
||||||
};
|
};
|
||||||
|
|
||||||
Line.prototype.calc = function() {
|
Line.prototype.calc = function() {
|
||||||
@@ -840,11 +942,15 @@ Licensed under the BSD-2-Clause License.
|
|||||||
Line.prototype.hoverContentForRow = function(index) {
|
Line.prototype.hoverContentForRow = function(index) {
|
||||||
var content, j, row, y, _i, _len, _ref;
|
var content, j, row, y, _i, _len, _ref;
|
||||||
row = this.data[index];
|
row = this.data[index];
|
||||||
content = "<div class='morris-hover-row-label'>" + row.label + "</div>";
|
content = $("<div class='morris-hover-row-label'>").text(row.label);
|
||||||
|
content = content.prop('outerHTML');
|
||||||
_ref = row.y;
|
_ref = row.y;
|
||||||
for (j = _i = 0, _len = _ref.length; _i < _len; j = ++_i) {
|
for (j = _i = 0, _len = _ref.length; _i < _len; j = ++_i) {
|
||||||
y = _ref[j];
|
y = _ref[j];
|
||||||
content += "<div class='morris-hover-point' style='color: " + (this.colorFor(row, j, 'label')) + "'>\n " + this.options.labels[j] + ":\n " + (this.yLabelFormat(y)) + "\n</div>";
|
if (this.options.labels[j] === false) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
content += "<div class='morris-hover-point' style='color: " + (this.colorFor(row, j, 'label')) + "'>\n " + this.options.labels[j] + ":\n " + (this.yLabelFormat(y, j)) + "\n</div>";
|
||||||
}
|
}
|
||||||
if (typeof this.options.hoverCallback === 'function') {
|
if (typeof this.options.hoverCallback === 'function') {
|
||||||
content = this.options.hoverCallback(index, this.options, content, row.src);
|
content = this.options.hoverCallback(index, this.options, content, row.src);
|
||||||
@@ -954,11 +1060,20 @@ Licensed under the BSD-2-Clause License.
|
|||||||
var i, _i, _j, _ref, _ref1, _results;
|
var i, _i, _j, _ref, _ref1, _results;
|
||||||
this.seriesPoints = [];
|
this.seriesPoints = [];
|
||||||
for (i = _i = _ref = this.options.ykeys.length - 1; _ref <= 0 ? _i <= 0 : _i >= 0; i = _ref <= 0 ? ++_i : --_i) {
|
for (i = _i = _ref = this.options.ykeys.length - 1; _ref <= 0 ? _i <= 0 : _i >= 0; i = _ref <= 0 ? ++_i : --_i) {
|
||||||
this._drawLineFor(i);
|
if (this.hasToShow(i)) {
|
||||||
|
if (this.options.trendLine !== false && this.options.trendLine === true || this.options.trendLine[i] === true) {
|
||||||
|
this._drawTrendLine(i);
|
||||||
|
}
|
||||||
|
this._drawLineFor(i);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_results = [];
|
_results = [];
|
||||||
for (i = _j = _ref1 = this.options.ykeys.length - 1; _ref1 <= 0 ? _j <= 0 : _j >= 0; i = _ref1 <= 0 ? ++_j : --_j) {
|
for (i = _j = _ref1 = this.options.ykeys.length - 1; _ref1 <= 0 ? _j <= 0 : _j >= 0; i = _ref1 <= 0 ? ++_j : --_j) {
|
||||||
_results.push(this._drawPointFor(i));
|
if (this.hasToShow(i)) {
|
||||||
|
_results.push(this._drawPointFor(i));
|
||||||
|
} else {
|
||||||
|
_results.push(void 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return _results;
|
return _results;
|
||||||
};
|
};
|
||||||
@@ -987,6 +1102,38 @@ Licensed under the BSD-2-Clause License.
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Line.prototype._drawTrendLine = function(index) {
|
||||||
|
var a, b, data, datapoints, path, sum_x, sum_xx, sum_xy, sum_y, val, x, y, _i, _len, _ref;
|
||||||
|
sum_x = 0;
|
||||||
|
sum_y = 0;
|
||||||
|
sum_xx = 0;
|
||||||
|
sum_xy = 0;
|
||||||
|
datapoints = 0;
|
||||||
|
_ref = this.data;
|
||||||
|
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||||
|
val = _ref[_i];
|
||||||
|
x = val.x;
|
||||||
|
y = val.y[index];
|
||||||
|
if (y === void 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
datapoints += 1;
|
||||||
|
sum_x += x;
|
||||||
|
sum_y += y;
|
||||||
|
sum_xx += x * x;
|
||||||
|
sum_xy += x * y;
|
||||||
|
}
|
||||||
|
a = (datapoints * sum_xy - sum_x * sum_y) / (datapoints * sum_xx - sum_x * sum_x);
|
||||||
|
b = (sum_y / datapoints) - ((a * sum_x) / datapoints);
|
||||||
|
data = [{}, {}];
|
||||||
|
data[0].x = this.transX(this.data[0].x);
|
||||||
|
data[0].y = this.transY(this.data[0].x * a + b);
|
||||||
|
data[1].x = this.transX(this.data[this.data.length - 1].x);
|
||||||
|
data[1].y = this.transY(this.data[this.data.length - 1].x * a + b);
|
||||||
|
path = Morris.Line.createPath(data, false, this.bottom);
|
||||||
|
return path = this.raphael.path(path).attr('stroke', this.colorFor(null, index, 'trendLine')).attr('stroke-width', this.options.trendLineWidth);
|
||||||
|
};
|
||||||
|
|
||||||
Line.createPath = function(coords, smooth, bottom) {
|
Line.createPath = function(coords, smooth, bottom) {
|
||||||
var coord, g, grads, i, ix, lg, path, prevCoord, x1, x2, y1, y2, _i, _len;
|
var coord, g, grads, i, ix, lg, path, prevCoord, x1, x2, y1, y2, _i, _len;
|
||||||
path = "";
|
path = "";
|
||||||
@@ -1078,8 +1225,10 @@ Licensed under the BSD-2-Clause License.
|
|||||||
return this.options.lineColors.call(this, row, sidx, type);
|
return this.options.lineColors.call(this, row, sidx, type);
|
||||||
} else if (type === 'point') {
|
} else if (type === 'point') {
|
||||||
return this.options.pointFillColors[sidx % this.options.pointFillColors.length] || this.options.lineColors[sidx % this.options.lineColors.length];
|
return this.options.pointFillColors[sidx % this.options.pointFillColors.length] || this.options.lineColors[sidx % this.options.lineColors.length];
|
||||||
} else {
|
} else if (type === 'line') {
|
||||||
return this.options.lineColors[sidx % this.options.lineColors.length];
|
return this.options.lineColors[sidx % this.options.lineColors.length];
|
||||||
|
} else {
|
||||||
|
return this.options.trendLineColors[sidx % this.options.trendLineColors.length];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1120,6 +1269,9 @@ Licensed under the BSD-2-Clause License.
|
|||||||
};
|
};
|
||||||
|
|
||||||
Line.prototype.pointGrowSeries = function(index) {
|
Line.prototype.pointGrowSeries = function(index) {
|
||||||
|
if (this.pointSizeForSeries(index) === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
return Raphael.animation({
|
return Raphael.animation({
|
||||||
r: this.pointSizeForSeries(index) + 3
|
r: this.pointSizeForSeries(index) + 3
|
||||||
}, 25, 'linear');
|
}, 25, 'linear');
|
||||||
@@ -1409,7 +1561,9 @@ Licensed under the BSD-2-Clause License.
|
|||||||
barColors: ['#0b62a4', '#7a92a3', '#4da74d', '#afd8f8', '#edc240', '#cb4b4b', '#9440ed'],
|
barColors: ['#0b62a4', '#7a92a3', '#4da74d', '#afd8f8', '#edc240', '#cb4b4b', '#9440ed'],
|
||||||
barOpacity: 1.0,
|
barOpacity: 1.0,
|
||||||
barRadius: [0, 0, 0, 0],
|
barRadius: [0, 0, 0, 0],
|
||||||
xLabelMargin: 50
|
xLabelMargin: 50,
|
||||||
|
horizontal: false,
|
||||||
|
shown: true
|
||||||
};
|
};
|
||||||
|
|
||||||
Bar.prototype.calc = function() {
|
Bar.prototype.calc = function() {
|
||||||
@@ -1426,7 +1580,7 @@ Licensed under the BSD-2-Clause License.
|
|||||||
_results = [];
|
_results = [];
|
||||||
for (idx = _i = 0, _len = _ref.length; _i < _len; idx = ++_i) {
|
for (idx = _i = 0, _len = _ref.length; _i < _len; idx = ++_i) {
|
||||||
row = _ref[idx];
|
row = _ref[idx];
|
||||||
row._x = this.left + this.width * (idx + 0.5) / this.data.length;
|
row._x = this.xStart + this.xSize * (idx + 0.5) / this.data.length;
|
||||||
_results.push(row._y = (function() {
|
_results.push(row._y = (function() {
|
||||||
var _j, _len1, _ref1, _results1;
|
var _j, _len1, _ref1, _results1;
|
||||||
_ref1 = row.y;
|
_ref1 = row.y;
|
||||||
@@ -1454,28 +1608,54 @@ Licensed under the BSD-2-Clause License.
|
|||||||
};
|
};
|
||||||
|
|
||||||
Bar.prototype.drawXAxis = function() {
|
Bar.prototype.drawXAxis = function() {
|
||||||
var i, label, labelBox, margin, offset, prevAngleMargin, prevLabelMargin, row, textBox, ypos, _i, _ref, _results;
|
var angle, basePos, i, label, labelBox, margin, maxSize, offset, prevAngleMargin, prevLabelMargin, row, size, startPos, textBox, _i, _ref, _results;
|
||||||
ypos = this.bottom + (this.options.xAxisLabelTopPadding || this.options.padding / 2);
|
if (!this.options.horizontal) {
|
||||||
|
basePos = this.getXAxisLabelY();
|
||||||
|
} else {
|
||||||
|
basePos = this.getYAxisLabelX();
|
||||||
|
}
|
||||||
prevLabelMargin = null;
|
prevLabelMargin = null;
|
||||||
prevAngleMargin = null;
|
prevAngleMargin = null;
|
||||||
_results = [];
|
_results = [];
|
||||||
for (i = _i = 0, _ref = this.data.length; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) {
|
for (i = _i = 0, _ref = this.data.length; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) {
|
||||||
row = this.data[this.data.length - 1 - i];
|
row = this.data[this.data.length - 1 - i];
|
||||||
label = this.drawXAxisLabel(row._x, ypos, row.label);
|
if (!this.options.horizontal) {
|
||||||
|
label = this.drawXAxisLabel(row._x, basePos, row.label);
|
||||||
|
} else {
|
||||||
|
label = this.drawYAxisLabel(basePos, row._x - 0.5 * this.options.gridTextSize, row.label);
|
||||||
|
}
|
||||||
|
if (!this.options.horizontal) {
|
||||||
|
angle = this.options.xLabelAngle;
|
||||||
|
} else {
|
||||||
|
angle = 0;
|
||||||
|
}
|
||||||
textBox = label.getBBox();
|
textBox = label.getBBox();
|
||||||
label.transform("r" + (-this.options.xLabelAngle));
|
label.transform("r" + (-angle));
|
||||||
labelBox = label.getBBox();
|
labelBox = label.getBBox();
|
||||||
label.transform("t0," + (labelBox.height / 2) + "...");
|
label.transform("t0," + (labelBox.height / 2) + "...");
|
||||||
if (this.options.xLabelAngle !== 0) {
|
if (angle !== 0) {
|
||||||
offset = -0.5 * textBox.width * Math.cos(this.options.xLabelAngle * Math.PI / 180.0);
|
offset = -0.5 * textBox.width * Math.cos(angle * Math.PI / 180.0);
|
||||||
label.transform("t" + offset + ",0...");
|
label.transform("t" + offset + ",0...");
|
||||||
}
|
}
|
||||||
if (((prevLabelMargin == null) || prevLabelMargin >= labelBox.x + labelBox.width || (prevAngleMargin != null) && prevAngleMargin >= labelBox.x) && labelBox.x >= 0 && (labelBox.x + labelBox.width) < this.el.width()) {
|
if (!this.options.horizontal) {
|
||||||
if (this.options.xLabelAngle !== 0) {
|
startPos = labelBox.x;
|
||||||
margin = 1.25 * this.options.gridTextSize / Math.sin(this.options.xLabelAngle * Math.PI / 180.0);
|
size = labelBox.width;
|
||||||
prevAngleMargin = labelBox.x - margin;
|
maxSize = this.el.width();
|
||||||
|
} else {
|
||||||
|
startPos = labelBox.y;
|
||||||
|
size = labelBox.height;
|
||||||
|
maxSize = this.el.height();
|
||||||
|
}
|
||||||
|
if (((prevLabelMargin == null) || prevLabelMargin >= startPos + size || (prevAngleMargin != null) && prevAngleMargin >= startPos) && startPos >= 0 && (startPos + size) < maxSize) {
|
||||||
|
if (angle !== 0) {
|
||||||
|
margin = 1.25 * this.options.gridTextSize / Math.sin(angle * Math.PI / 180.0);
|
||||||
|
prevAngleMargin = startPos - margin;
|
||||||
|
}
|
||||||
|
if (!this.options.horizontal) {
|
||||||
|
_results.push(prevLabelMargin = startPos - this.options.xLabelMargin);
|
||||||
|
} else {
|
||||||
|
_results.push(prevLabelMargin = startPos);
|
||||||
}
|
}
|
||||||
_results.push(prevLabelMargin = labelBox.x - this.options.xLabelMargin);
|
|
||||||
} else {
|
} else {
|
||||||
_results.push(label.remove());
|
_results.push(label.remove());
|
||||||
}
|
}
|
||||||
@@ -1483,10 +1663,23 @@ Licensed under the BSD-2-Clause License.
|
|||||||
return _results;
|
return _results;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Bar.prototype.getXAxisLabelY = function() {
|
||||||
|
return this.bottom + (this.options.xAxisLabelTopPadding || this.options.padding / 2);
|
||||||
|
};
|
||||||
|
|
||||||
Bar.prototype.drawSeries = function() {
|
Bar.prototype.drawSeries = function() {
|
||||||
var barWidth, bottom, groupWidth, idx, lastTop, left, leftPadding, numBars, row, sidx, size, spaceLeft, top, ypos, zeroPos;
|
var barWidth, bottom, groupWidth, i, idx, lastTop, left, leftPadding, numBars, row, sidx, size, spaceLeft, top, ypos, zeroPos, _i, _ref;
|
||||||
groupWidth = this.width / this.options.data.length;
|
groupWidth = this.xSize / this.options.data.length;
|
||||||
numBars = this.options.stacked ? 1 : this.options.ykeys.length;
|
if (this.options.stacked) {
|
||||||
|
numBars = 1;
|
||||||
|
} else {
|
||||||
|
numBars = 0;
|
||||||
|
for (i = _i = 0, _ref = this.options.ykeys.length - 1; 0 <= _ref ? _i <= _ref : _i >= _ref; i = 0 <= _ref ? ++_i : --_i) {
|
||||||
|
if (this.hasToShow(i)) {
|
||||||
|
numBars += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
barWidth = (groupWidth * this.options.barSizeRatio - this.options.barGap * (numBars - 1)) / numBars;
|
barWidth = (groupWidth * this.options.barSizeRatio - this.options.barGap * (numBars - 1)) / numBars;
|
||||||
if (this.options.barSize) {
|
if (this.options.barSize) {
|
||||||
barWidth = Math.min(barWidth, this.options.barSize);
|
barWidth = Math.min(barWidth, this.options.barSize);
|
||||||
@@ -1495,18 +1688,21 @@ Licensed under the BSD-2-Clause License.
|
|||||||
leftPadding = spaceLeft / 2;
|
leftPadding = spaceLeft / 2;
|
||||||
zeroPos = this.ymin <= 0 && this.ymax >= 0 ? this.transY(0) : null;
|
zeroPos = this.ymin <= 0 && this.ymax >= 0 ? this.transY(0) : null;
|
||||||
return this.bars = (function() {
|
return this.bars = (function() {
|
||||||
var _i, _len, _ref, _results;
|
var _j, _len, _ref1, _results;
|
||||||
_ref = this.data;
|
_ref1 = this.data;
|
||||||
_results = [];
|
_results = [];
|
||||||
for (idx = _i = 0, _len = _ref.length; _i < _len; idx = ++_i) {
|
for (idx = _j = 0, _len = _ref1.length; _j < _len; idx = ++_j) {
|
||||||
row = _ref[idx];
|
row = _ref1[idx];
|
||||||
lastTop = 0;
|
lastTop = 0;
|
||||||
_results.push((function() {
|
_results.push((function() {
|
||||||
var _j, _len1, _ref1, _results1;
|
var _k, _len1, _ref2, _results1;
|
||||||
_ref1 = row._y;
|
_ref2 = row._y;
|
||||||
_results1 = [];
|
_results1 = [];
|
||||||
for (sidx = _j = 0, _len1 = _ref1.length; _j < _len1; sidx = ++_j) {
|
for (sidx = _k = 0, _len1 = _ref2.length; _k < _len1; sidx = ++_k) {
|
||||||
ypos = _ref1[sidx];
|
ypos = _ref2[sidx];
|
||||||
|
if (!this.hasToShow(sidx)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (ypos !== null) {
|
if (ypos !== null) {
|
||||||
if (zeroPos) {
|
if (zeroPos) {
|
||||||
top = Math.min(ypos, zeroPos);
|
top = Math.min(ypos, zeroPos);
|
||||||
@@ -1515,19 +1711,28 @@ Licensed under the BSD-2-Clause License.
|
|||||||
top = ypos;
|
top = ypos;
|
||||||
bottom = this.bottom;
|
bottom = this.bottom;
|
||||||
}
|
}
|
||||||
left = this.left + idx * groupWidth + leftPadding;
|
left = this.xStart + idx * groupWidth + leftPadding;
|
||||||
if (!this.options.stacked) {
|
if (!this.options.stacked) {
|
||||||
left += sidx * (barWidth + this.options.barGap);
|
left += sidx * (barWidth + this.options.barGap);
|
||||||
}
|
}
|
||||||
size = bottom - top;
|
size = bottom - top;
|
||||||
if (this.options.verticalGridCondition && this.options.verticalGridCondition(row.x)) {
|
if (this.options.verticalGridCondition && this.options.verticalGridCondition(row.x)) {
|
||||||
this.drawBar(this.left + idx * groupWidth, this.top, groupWidth, Math.abs(this.top - this.bottom), this.options.verticalGridColor, this.options.verticalGridOpacity, this.options.barRadius);
|
if (!this.options.horizontal) {
|
||||||
|
this.drawBar(this.xStart + idx * groupWidth, this.yEnd, groupWidth, this.ySize, this.options.verticalGridColor, this.options.verticalGridOpacity, this.options.barRadius);
|
||||||
|
} else {
|
||||||
|
this.drawBar(this.yStart, this.xStart + idx * groupWidth, this.ySize, groupWidth, this.options.verticalGridColor, this.options.verticalGridOpacity, this.options.barRadius);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (this.options.stacked) {
|
if (this.options.stacked) {
|
||||||
top -= lastTop;
|
top -= lastTop;
|
||||||
}
|
}
|
||||||
this.drawBar(left, top, barWidth, size, this.colorFor(row, sidx, 'bar'), this.options.barOpacity, this.options.barRadius);
|
if (!this.options.horizontal) {
|
||||||
_results1.push(lastTop += size);
|
this.drawBar(left, top, barWidth, size, this.colorFor(row, sidx, 'bar'), this.options.barOpacity, this.options.barRadius);
|
||||||
|
_results1.push(lastTop += size);
|
||||||
|
} else {
|
||||||
|
this.drawBar(top, left, size, barWidth, this.colorFor(row, sidx, 'bar'), this.options.barOpacity, this.options.barRadius);
|
||||||
|
_results1.push(lastTop -= size);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
_results1.push(null);
|
_results1.push(null);
|
||||||
}
|
}
|
||||||
@@ -1558,23 +1763,29 @@ Licensed under the BSD-2-Clause License.
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Bar.prototype.hitTest = function(x) {
|
Bar.prototype.hitTest = function(x, y) {
|
||||||
|
var pos;
|
||||||
if (this.data.length === 0) {
|
if (this.data.length === 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
x = Math.max(Math.min(x, this.right), this.left);
|
if (!this.options.horizontal) {
|
||||||
return Math.min(this.data.length - 1, Math.floor((x - this.left) / (this.width / this.data.length)));
|
pos = x;
|
||||||
|
} else {
|
||||||
|
pos = y;
|
||||||
|
}
|
||||||
|
pos = Math.max(Math.min(pos, this.xEnd), this.xStart);
|
||||||
|
return Math.min(this.data.length - 1, Math.floor((pos - this.xStart) / (this.xSize / this.data.length)));
|
||||||
};
|
};
|
||||||
|
|
||||||
Bar.prototype.onGridClick = function(x, y) {
|
Bar.prototype.onGridClick = function(x, y) {
|
||||||
var index;
|
var index;
|
||||||
index = this.hitTest(x);
|
index = this.hitTest(x, y);
|
||||||
return this.fire('click', index, this.data[index].src, x, y);
|
return this.fire('click', index, this.data[index].src, x, y);
|
||||||
};
|
};
|
||||||
|
|
||||||
Bar.prototype.onHoverMove = function(x, y) {
|
Bar.prototype.onHoverMove = function(x, y) {
|
||||||
var index, _ref;
|
var index, _ref;
|
||||||
index = this.hitTest(x);
|
index = this.hitTest(x, y);
|
||||||
return (_ref = this.hover).update.apply(_ref, this.hoverContentForRow(index));
|
return (_ref = this.hover).update.apply(_ref, this.hoverContentForRow(index));
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1587,17 +1798,27 @@ Licensed under the BSD-2-Clause License.
|
|||||||
Bar.prototype.hoverContentForRow = function(index) {
|
Bar.prototype.hoverContentForRow = function(index) {
|
||||||
var content, j, row, x, y, _i, _len, _ref;
|
var content, j, row, x, y, _i, _len, _ref;
|
||||||
row = this.data[index];
|
row = this.data[index];
|
||||||
content = "<div class='morris-hover-row-label'>" + row.label + "</div>";
|
content = $("<div class='morris-hover-row-label'>").text(row.label);
|
||||||
|
content = content.prop('outerHTML');
|
||||||
_ref = row.y;
|
_ref = row.y;
|
||||||
for (j = _i = 0, _len = _ref.length; _i < _len; j = ++_i) {
|
for (j = _i = 0, _len = _ref.length; _i < _len; j = ++_i) {
|
||||||
y = _ref[j];
|
y = _ref[j];
|
||||||
content += "<div class='morris-hover-point' style='color: " + (this.colorFor(row, j, 'label')) + "'>\n " + this.options.labels[j] + ":\n " + (this.yLabelFormat(y)) + "\n</div>";
|
if (this.options.labels[j] === false) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
content += "<div class='morris-hover-point' style='color: " + (this.colorFor(row, j, 'label')) + "'>\n " + this.options.labels[j] + ":\n " + (this.yLabelFormat(y, j)) + "\n</div>";
|
||||||
}
|
}
|
||||||
if (typeof this.options.hoverCallback === 'function') {
|
if (typeof this.options.hoverCallback === 'function') {
|
||||||
content = this.options.hoverCallback(index, this.options, content, row.src);
|
content = this.options.hoverCallback(index, this.options, content, row.src);
|
||||||
}
|
}
|
||||||
x = this.left + (index + 0.5) * this.width / this.data.length;
|
if (!this.options.horizontal) {
|
||||||
return [content, x];
|
x = this.left + (index + 0.5) * this.width / this.data.length;
|
||||||
|
return [content, x];
|
||||||
|
} else {
|
||||||
|
x = this.left + 0.5 * this.width;
|
||||||
|
y = this.top + (index + 0.5) * this.height / this.data.length;
|
||||||
|
return [content, x, y, true];
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Bar.prototype.drawXAxisLabel = function(xPos, yPos, text) {
|
Bar.prototype.drawXAxisLabel = function(xPos, yPos, text) {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/* Preload images */
|
/* Preload images */
|
||||||
body:after {
|
body:after {
|
||||||
content: url(../images/close.png) url(../images/loading.gif) url(../images/prev.png) url(../images/next.png);
|
content: url(static('lightbox/images/close.png')) url(static('lightbox/images/loading.gif')) url(static('lightbox/images/prev.png')) url(static('lightbox/images/next.png'));
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,7 +79,7 @@ body:after {
|
|||||||
width: 32px;
|
width: 32px;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
background: url(../images/loading.gif) no-repeat;
|
background: url(static('lightbox/images/loading.gif')) no-repeat;
|
||||||
}
|
}
|
||||||
|
|
||||||
.lb-nav {
|
.lb-nav {
|
||||||
@@ -110,7 +110,7 @@ body:after {
|
|||||||
width: 34%;
|
width: 34%;
|
||||||
left: 0;
|
left: 0;
|
||||||
float: left;
|
float: left;
|
||||||
background: url(../images/prev.png) left 48% no-repeat;
|
background: url(static('lightbox/images/prev.png')) left 48% no-repeat;
|
||||||
filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0);
|
filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0);
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
-webkit-transition: opacity 0.6s;
|
-webkit-transition: opacity 0.6s;
|
||||||
@@ -128,7 +128,7 @@ body:after {
|
|||||||
width: 64%;
|
width: 64%;
|
||||||
right: 0;
|
right: 0;
|
||||||
float: right;
|
float: right;
|
||||||
background: url(../images/next.png) right 48% no-repeat;
|
background: url(static('lightbox/images/next.png')) right 48% no-repeat;
|
||||||
filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0);
|
filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0);
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
-webkit-transition: opacity 0.6s;
|
-webkit-transition: opacity 0.6s;
|
||||||
@@ -192,7 +192,7 @@ body:after {
|
|||||||
float: right;
|
float: right;
|
||||||
width: 30px;
|
width: 30px;
|
||||||
height: 30px;
|
height: 30px;
|
||||||
background: url(../images/close.png) top right no-repeat;
|
background: url(static('lightbox/images/close.png')) top right no-repeat;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
outline: none;
|
outline: none;
|
||||||
filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=70);
|
filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=70);
|
||||||
@@ -29,6 +29,11 @@ nav.navbar {
|
|||||||
display: inline;
|
display: inline;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#side-menu img.fa-img {
|
||||||
|
height: auto;
|
||||||
|
width: 1.28571em;
|
||||||
|
}
|
||||||
|
|
||||||
nav.navbar .danger, nav.navbar .danger:hover, nav.navbar .danger:active {
|
nav.navbar .danger, nav.navbar .danger:hover, nav.navbar .danger:active {
|
||||||
background: $brand-danger !important;
|
background: $brand-danger !important;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ function typocheck() {
|
|||||||
|
|
||||||
$sources.each(function () {
|
$sources.each(function () {
|
||||||
$.each($(this).val().toLowerCase().replace('-', '').split(' '), function (i, w) {
|
$.each($(this).val().toLowerCase().replace('-', '').split(' '), function (i, w) {
|
||||||
if (w) {
|
if (w && w.length > 5) {
|
||||||
words.push(w);
|
words.push(w);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
django-debug-toolbar==1.5
|
django-debug-toolbar==1.8
|
||||||
sqlparse==0.2.1 # pinned due to difficulties with django-debug-toolbar
|
sqlparse==0.2.1 # pinned due to difficulties with django-debug-toolbar
|
||||||
# Testing requirements
|
# Testing requirements
|
||||||
pep8==1.5.7 # exact requirement by flake8 2.4.0
|
pep8==1.5.7 # exact requirement by flake8 2.4.0
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
# Functional requirements
|
# Functional requirements
|
||||||
Django>=1.10.7,<1.11
|
Django>=1.11.*
|
||||||
python-dateutil
|
python-dateutil
|
||||||
pytz
|
pytz
|
||||||
django-bootstrap3==8.0.*
|
django-bootstrap3==8.2.*
|
||||||
django-formset-js-improved==0.5.0.1
|
django-formset-js-improved==0.5.0.1
|
||||||
django-compressor==2.1
|
django-compressor==2.1.1
|
||||||
django-hierarkey==1.0.*
|
django-hierarkey==1.0.*
|
||||||
reportlab==3.2.*
|
reportlab==3.2.*
|
||||||
PyPDF2==1.26.*
|
PyPDF2==1.26.*
|
||||||
easy-thumbnails==2.*
|
easy-thumbnails==2.4.*
|
||||||
django-libsass
|
django-libsass
|
||||||
libsass
|
libsass
|
||||||
django-otp==0.3.*
|
django-otp==0.3.*
|
||||||
python-u2flib-server==4.*
|
python-u2flib-server==4.*
|
||||||
django-formtools==1.0
|
django-formtools==2.0
|
||||||
celery==4.0.2
|
celery==4.0.2
|
||||||
kombu==4.0.2
|
kombu==4.0.2
|
||||||
django-statici18n==1.3.*
|
django-statici18n==1.3.*
|
||||||
@@ -28,14 +28,15 @@ django-markup
|
|||||||
markdown
|
markdown
|
||||||
bleach==2.*
|
bleach==2.*
|
||||||
raven
|
raven
|
||||||
django-i18nfield
|
django-i18nfield>=1.0.1
|
||||||
# Stripe
|
# Stripe
|
||||||
stripe==1.22.*
|
stripe==1.22.*
|
||||||
# PayPal
|
# PayPal
|
||||||
paypalrestsdk==1.12.*
|
paypalrestsdk==1.12.*
|
||||||
pycparser==2.13 # https://github.com/eliben/pycparser/issues/147
|
pycparser==2.13 # https://github.com/eliben/pycparser/issues/147
|
||||||
# Banktransfer
|
# Banktransfer
|
||||||
chardet>=2.3,<3
|
chardet<3.1.0,>=3.0.2
|
||||||
mt-940==3.2
|
mt-940==3.2
|
||||||
vobject==0.9.*
|
vobject==0.9.*
|
||||||
pycountry
|
pycountry
|
||||||
|
defusedcsv>=1.0.1
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
django-redis==4.1.*
|
django-redis==4.8.*
|
||||||
redis==2.10.5
|
redis==2.10.5
|
||||||
|
|||||||
21
src/setup.py
21
src/setup.py
@@ -62,21 +62,21 @@ setup(
|
|||||||
|
|
||||||
keywords='tickets web shop ecommerce',
|
keywords='tickets web shop ecommerce',
|
||||||
install_requires=[
|
install_requires=[
|
||||||
'Django==1.10.*',
|
'Django==1.11.*',
|
||||||
'python-dateutil==2.4.*',
|
'python-dateutil==2.4.*',
|
||||||
'pytz',
|
'pytz',
|
||||||
'django-bootstrap3==7.1.*',
|
'django-bootstrap3==8.2.*',
|
||||||
'django-formset-js-improved==0.5.0.1',
|
'django-formset-js-improved==0.5.0.1',
|
||||||
'django-compressor==2.1',
|
'django-compressor==2.1',
|
||||||
'django-hierarkey==1.0.*',
|
'django-hierarkey==1.0.*',
|
||||||
'reportlab==3.2.*',
|
'reportlab==3.2.*',
|
||||||
'easy-thumbnails==2.*',
|
'easy-thumbnails==2.4.*',
|
||||||
'PyPDF2==1.26.*',
|
'PyPDF2==1.26.*',
|
||||||
'django-libsass',
|
'django-libsass',
|
||||||
'libsass',
|
'libsass',
|
||||||
'django-otp==0.3.*',
|
'django-otp==0.3.*',
|
||||||
'python-u2flib-server==4.*',
|
'python-u2flib-server==4.*',
|
||||||
'django-formtools==1.0',
|
'django-formtools==2.0',
|
||||||
'celery==4.0.2',
|
'celery==4.0.2',
|
||||||
'kombu==4.0.2',
|
'kombu==4.0.2',
|
||||||
'django-statici18n==1.3.*',
|
'django-statici18n==1.3.*',
|
||||||
@@ -93,18 +93,19 @@ setup(
|
|||||||
'raven',
|
'raven',
|
||||||
'paypalrestsdk==1.12.*',
|
'paypalrestsdk==1.12.*',
|
||||||
'pycparser==2.13',
|
'pycparser==2.13',
|
||||||
'django-redis==4.1.*',
|
'django-redis==4.7.*',
|
||||||
'redis==2.10.5',
|
'redis==2.10.5',
|
||||||
'stripe==1.22.*',
|
'stripe==1.22.*',
|
||||||
'chardet>=2.3,<3',
|
'chardet<3.1.0,>=3.0.2',
|
||||||
'mt-940==3.2',
|
'mt-940==4.7',
|
||||||
'django-i18nfield',
|
'django-i18nfield>=1.0.1',
|
||||||
'vobject==0.9.*',
|
'vobject==0.9.*',
|
||||||
'pycountry'
|
'pycountry',
|
||||||
|
'defusedcsv'
|
||||||
],
|
],
|
||||||
extras_require={
|
extras_require={
|
||||||
'dev': [
|
'dev': [
|
||||||
'django-debug-toolbar==1.5',
|
'django-debug-toolbar==1.7',
|
||||||
'sqlparse==0.2.1',
|
'sqlparse==0.2.1',
|
||||||
'pep8==1.5.7',
|
'pep8==1.5.7',
|
||||||
'pyflakes==1.1.0',
|
'pyflakes==1.1.0',
|
||||||
|
|||||||
@@ -2,10 +2,9 @@ from django.db import DEFAULT_DB_ALIAS, connections
|
|||||||
from django.test.utils import CaptureQueriesContext
|
from django.test.utils import CaptureQueriesContext
|
||||||
|
|
||||||
|
|
||||||
# Inspired by /django/test/testcases.py
|
|
||||||
# but copied over to work without the unit test module
|
|
||||||
|
|
||||||
class _AssertNumQueriesContext(CaptureQueriesContext):
|
class _AssertNumQueriesContext(CaptureQueriesContext):
|
||||||
|
# Inspired by /django/test/testcases.py
|
||||||
|
# but copied over to work without the unit test module
|
||||||
def __init__(self, num, connection):
|
def __init__(self, num, connection):
|
||||||
self.num = num
|
self.num = num
|
||||||
super(_AssertNumQueriesContext, self).__init__(connection)
|
super(_AssertNumQueriesContext, self).__init__(connection)
|
||||||
|
|||||||
@@ -92,7 +92,6 @@ class I18nFieldTest(TestCase):
|
|||||||
"""
|
"""
|
||||||
This test case tests the I18n*Field classes
|
This test case tests the I18n*Field classes
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpTestData(cls):
|
def setUpTestData(cls):
|
||||||
o = Organizer.objects.create(name='Dummy', slug='dummy')
|
o = Organizer.objects.create(name='Dummy', slug='dummy')
|
||||||
@@ -102,8 +101,8 @@ class I18nFieldTest(TestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def test_save_load_cycle_plain_string(self):
|
def test_save_load_cycle_plain_string(self):
|
||||||
obj = ItemCategory.all.create(event=self.event, name="Hello")
|
obj = ItemCategory.objects.create(event=self.event, name="Hello")
|
||||||
obj = ItemCategory.all.get(id=obj.id)
|
obj = ItemCategory.objects.get(id=obj.id)
|
||||||
self.assertIsInstance(obj.name, LazyI18nString)
|
self.assertIsInstance(obj.name, LazyI18nString)
|
||||||
translation.activate('en')
|
translation.activate('en')
|
||||||
self.assertEqual(str(obj.name), "Hello")
|
self.assertEqual(str(obj.name), "Hello")
|
||||||
@@ -111,14 +110,14 @@ class I18nFieldTest(TestCase):
|
|||||||
self.assertEqual(str(obj.name), "Hello")
|
self.assertEqual(str(obj.name), "Hello")
|
||||||
|
|
||||||
def test_save_load_cycle_i18n_string(self):
|
def test_save_load_cycle_i18n_string(self):
|
||||||
obj = ItemCategory.all.create(event=self.event,
|
obj = ItemCategory.objects.create(event=self.event,
|
||||||
name=LazyI18nString(
|
name=LazyI18nString(
|
||||||
{
|
{
|
||||||
'de': 'Hallo',
|
'de': 'Hallo',
|
||||||
'en': 'Hello'
|
'en': 'Hello'
|
||||||
}
|
}
|
||||||
))
|
))
|
||||||
obj = ItemCategory.all.get(id=obj.id)
|
obj = ItemCategory.objects.get(id=obj.id)
|
||||||
self.assertIsInstance(obj.name, LazyI18nString)
|
self.assertIsInstance(obj.name, LazyI18nString)
|
||||||
translation.activate('en')
|
translation.activate('en')
|
||||||
self.assertEqual(str(obj.name), "Hello")
|
self.assertEqual(str(obj.name), "Hello")
|
||||||
|
|||||||
@@ -687,8 +687,8 @@ class ItemCategoryTest(TestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def test_sorting(self):
|
def test_sorting(self):
|
||||||
c1 = ItemCategory.all.create(event=self.event)
|
c1 = ItemCategory.objects.create(event=self.event)
|
||||||
c2 = ItemCategory.all.create(event=self.event)
|
c2 = ItemCategory.objects.create(event=self.event)
|
||||||
assert c1 < c2
|
assert c1 < c2
|
||||||
c1.position = 2
|
c1.position = 2
|
||||||
c2.position = 1
|
c2.position = 1
|
||||||
|
|||||||
@@ -210,12 +210,12 @@ def checkin_list_env():
|
|||||||
('code', ['A1Ticket', 'A1Mascot', 'A2Ticket', 'A3Ticket']),
|
('code', ['A1Ticket', 'A1Mascot', 'A2Ticket', 'A3Ticket']),
|
||||||
('-email', ['A3Ticket', 'A2Ticket', 'A1Ticket', 'A1Mascot']),
|
('-email', ['A3Ticket', 'A2Ticket', 'A1Ticket', 'A1Mascot']),
|
||||||
('email', ['A1Ticket', 'A1Mascot', 'A2Ticket', 'A3Ticket']),
|
('email', ['A1Ticket', 'A1Mascot', 'A2Ticket', 'A3Ticket']),
|
||||||
# ('-status', ['A3Ticket', 'A1Ticket', 'A1Mascot', 'A2Ticket']),
|
('-status', ['A3Ticket', 'A1Ticket', 'A1Mascot', 'A2Ticket']),
|
||||||
# ('status', ['A1Mascot', 'A2Ticket', 'A1Ticket', 'A3Ticket']),
|
('status', ['A1Mascot', 'A2Ticket', 'A1Ticket', 'A3Ticket']),
|
||||||
# ('-timestamp', ['A1Ticket', 'A3Ticket', 'A1Mascot', 'A2Ticket']), # A1 checkin date > A3 checkin date
|
('-timestamp', ['A1Ticket', 'A3Ticket', 'A1Mascot', 'A2Ticket']), # A1 checkin date > A3 checkin date
|
||||||
# ('timestamp', ['A1Mascot', 'A2Ticket', 'A3Ticket', 'A1Ticket']),
|
('timestamp', ['A1Mascot', 'A2Ticket', 'A3Ticket', 'A1Ticket']),
|
||||||
# ('-name', ['A3Ticket', 'A2Ticket', 'A1Ticket', 'A1Mascot']),
|
('-name', ['A3Ticket', 'A2Ticket', 'A1Ticket', 'A1Mascot']),
|
||||||
# ('name', ['A1Mascot', 'A1Ticket', 'A2Ticket', 'A3Ticket']), # mascot doesn't include attendee name
|
('name', ['A1Mascot', 'A1Ticket', 'A2Ticket', 'A3Ticket']), # mascot doesn't include attendee name
|
||||||
('-item', ['A1Ticket', 'A2Ticket', 'A3Ticket', 'A1Mascot']),
|
('-item', ['A1Ticket', 'A2Ticket', 'A3Ticket', 'A1Mascot']),
|
||||||
('item', ['A1Mascot', 'A1Ticket', 'A2Ticket', 'A3Ticket']),
|
('item', ['A1Mascot', 'A1Ticket', 'A2Ticket', 'A3Ticket']),
|
||||||
])
|
])
|
||||||
@@ -259,8 +259,7 @@ def test_checkins_item_filter(client, checkin_list_env):
|
|||||||
@pytest.mark.parametrize("query, expected", [
|
@pytest.mark.parametrize("query, expected", [
|
||||||
('status=&item=&user=&ordering=', ['A1Ticket', 'A1Mascot', 'A2Ticket', 'A3Ticket']),
|
('status=&item=&user=&ordering=', ['A1Ticket', 'A1Mascot', 'A2Ticket', 'A3Ticket']),
|
||||||
('status=1&item=&user=&ordering=timestamp', ['A3Ticket', 'A1Ticket']),
|
('status=1&item=&user=&ordering=timestamp', ['A3Ticket', 'A1Ticket']),
|
||||||
# ('status=0&item=&user=&ordering=-name', ['A2Ticket', 'A1Mascot']),
|
('status=0&item=&user=&ordering=-name', ['A2Ticket', 'A1Mascot']),
|
||||||
# ('status=&item=Ticket&user=&ordering=checkins__datetime', ['A2Ticket', 'A3Ticket', 'A1Ticket']),
|
|
||||||
])
|
])
|
||||||
def test_checkins_list_mixed(client, checkin_list_env, query, expected):
|
def test_checkins_list_mixed(client, checkin_list_env, query, expected):
|
||||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||||
@@ -289,8 +288,8 @@ def checkin_list_with_addon_env():
|
|||||||
team.limit_events.add(event)
|
team.limit_events.add(event)
|
||||||
|
|
||||||
# item
|
# item
|
||||||
cat_adm = ItemCategory.all.create(event=event, name="Admission")
|
cat_adm = ItemCategory.objects.create(event=event, name="Admission")
|
||||||
cat_workshop = ItemCategory.all.create(event=event, name="Admission", is_addon=True)
|
cat_workshop = ItemCategory.objects.create(event=event, name="Admission", is_addon=True)
|
||||||
item_ticket = Item.objects.create(event=event, name="Ticket", default_price=23, admission=True, category=cat_adm)
|
item_ticket = Item.objects.create(event=event, name="Ticket", default_price=23, admission=True, category=cat_adm)
|
||||||
item_workshop = Item.objects.create(event=event, name="Workshop", default_price=10, admission=False,
|
item_workshop = Item.objects.create(event=event, name="Workshop", default_price=10, admission=False,
|
||||||
category=cat_workshop)
|
category=cat_workshop)
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ class EventsTest(SoupTest):
|
|||||||
assert len(doc.select(".alert-success")) > 0
|
assert len(doc.select(".alert-success")) > 0
|
||||||
# date_to should not be changed even though the timezone is changed
|
# date_to should not be changed even though the timezone is changed
|
||||||
assert doc.select("[name=date_to]")[0]['value'] == "2013-12-30 17:00:00"
|
assert doc.select("[name=date_to]")[0]['value'] == "2013-12-30 17:00:00"
|
||||||
assert doc.find('option', {"value": "Asia/Tokyo"})['selected'] == "selected"
|
assert 'selected' in doc.find('option', {"value": "Asia/Tokyo"}).attrs
|
||||||
assert doc.select("[name=settings-max_items_per_order]")[0]['value'] == "12"
|
assert doc.select("[name=settings-max_items_per_order]")[0]['value'] == "12"
|
||||||
|
|
||||||
self.event1.refresh_from_db()
|
self.event1.refresh_from_db()
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ class CategoriesTest(ItemFormTest):
|
|||||||
self.assertIn("Entry tickets", doc.select("#page-wrapper table")[0].text)
|
self.assertIn("Entry tickets", doc.select("#page-wrapper table")[0].text)
|
||||||
|
|
||||||
def test_update(self):
|
def test_update(self):
|
||||||
c = ItemCategory.all.create(event=self.event1, name="Entry tickets")
|
c = ItemCategory.objects.create(event=self.event1, name="Entry tickets")
|
||||||
doc = self.get_doc('/control/event/%s/%s/categories/%s/' % (self.orga1.slug, self.event1.slug, c.id))
|
doc = self.get_doc('/control/event/%s/%s/categories/%s/' % (self.orga1.slug, self.event1.slug, c.id))
|
||||||
form_data = extract_form_fields(doc.select('.container-fluid form')[0])
|
form_data = extract_form_fields(doc.select('.container-fluid form')[0])
|
||||||
form_data['name_0'] = 'T-Shirts'
|
form_data['name_0'] = 'T-Shirts'
|
||||||
@@ -46,11 +46,11 @@ class CategoriesTest(ItemFormTest):
|
|||||||
assert doc.select(".alert-success")
|
assert doc.select(".alert-success")
|
||||||
self.assertIn("T-Shirts", doc.select("#page-wrapper table")[0].text)
|
self.assertIn("T-Shirts", doc.select("#page-wrapper table")[0].text)
|
||||||
self.assertNotIn("Entry tickets", doc.select("#page-wrapper table")[0].text)
|
self.assertNotIn("Entry tickets", doc.select("#page-wrapper table")[0].text)
|
||||||
assert str(ItemCategory.all.get(id=c.id).name) == 'T-Shirts'
|
assert str(ItemCategory.objects.get(id=c.id).name) == 'T-Shirts'
|
||||||
|
|
||||||
def test_sort(self):
|
def test_sort(self):
|
||||||
c1 = ItemCategory.all.create(event=self.event1, name="Entry tickets", position=0)
|
c1 = ItemCategory.objects.create(event=self.event1, name="Entry tickets", position=0)
|
||||||
ItemCategory.all.create(event=self.event1, name="T-Shirts", position=1)
|
ItemCategory.objects.create(event=self.event1, name="T-Shirts", position=1)
|
||||||
doc = self.get_doc('/control/event/%s/%s/categories/' % (self.orga1.slug, self.event1.slug))
|
doc = self.get_doc('/control/event/%s/%s/categories/' % (self.orga1.slug, self.event1.slug))
|
||||||
self.assertIn("Entry tickets", doc.select("table > tbody > tr")[0].text)
|
self.assertIn("Entry tickets", doc.select("table > tbody > tr")[0].text)
|
||||||
self.assertIn("T-Shirts", doc.select("table > tbody > tr")[1].text)
|
self.assertIn("T-Shirts", doc.select("table > tbody > tr")[1].text)
|
||||||
@@ -66,14 +66,14 @@ class CategoriesTest(ItemFormTest):
|
|||||||
self.assertIn("T-Shirts", doc.select("table > tbody > tr")[1].text)
|
self.assertIn("T-Shirts", doc.select("table > tbody > tr")[1].text)
|
||||||
|
|
||||||
def test_delete(self):
|
def test_delete(self):
|
||||||
c = ItemCategory.all.create(event=self.event1, name="Entry tickets")
|
c = ItemCategory.objects.create(event=self.event1, name="Entry tickets")
|
||||||
doc = self.get_doc('/control/event/%s/%s/categories/%s/delete' % (self.orga1.slug, self.event1.slug, c.id))
|
doc = self.get_doc('/control/event/%s/%s/categories/%s/delete' % (self.orga1.slug, self.event1.slug, c.id))
|
||||||
form_data = extract_form_fields(doc.select('.container-fluid form')[0])
|
form_data = extract_form_fields(doc.select('.container-fluid form')[0])
|
||||||
doc = self.post_doc('/control/event/%s/%s/categories/%s/delete' % (self.orga1.slug, self.event1.slug, c.id),
|
doc = self.post_doc('/control/event/%s/%s/categories/%s/delete' % (self.orga1.slug, self.event1.slug, c.id),
|
||||||
form_data)
|
form_data)
|
||||||
assert doc.select(".alert-success")
|
assert doc.select(".alert-success")
|
||||||
self.assertNotIn("Entry tickets", doc.select("#page-wrapper")[0].text)
|
self.assertNotIn("Entry tickets", doc.select("#page-wrapper")[0].text)
|
||||||
assert not ItemCategory.all.filter(id=c.id).exists()
|
assert not ItemCategory.objects.filter(id=c.id).exists()
|
||||||
|
|
||||||
|
|
||||||
class QuestionsTest(ItemFormTest):
|
class QuestionsTest(ItemFormTest):
|
||||||
@@ -264,7 +264,7 @@ class ItemsTest(ItemFormTest):
|
|||||||
require_voucher=True, allow_cancel=False)
|
require_voucher=True, allow_cancel=False)
|
||||||
self.var1 = ItemVariation.objects.create(item=self.item2, value="Silver")
|
self.var1 = ItemVariation.objects.create(item=self.item2, value="Silver")
|
||||||
self.var2 = ItemVariation.objects.create(item=self.item2, value="Gold")
|
self.var2 = ItemVariation.objects.create(item=self.item2, value="Gold")
|
||||||
self.addoncat = ItemCategory.all.create(event=self.event1, name="Item category")
|
self.addoncat = ItemCategory.objects.create(event=self.event1, name="Item category")
|
||||||
|
|
||||||
def test_move(self):
|
def test_move(self):
|
||||||
self.client.post('/control/event/%s/%s/items/%s/down' % (self.orga1.slug, self.event1.slug, self.item1.id),)
|
self.client.post('/control/event/%s/%s/items/%s/down' % (self.orga1.slug, self.event1.slug, self.item1.id),)
|
||||||
|
|||||||
@@ -141,7 +141,7 @@ class MailSettingPreviewTest(SoupTest):
|
|||||||
assert res['msgs']['en'] == self.locale_event.name['en']
|
assert res['msgs']['en'] == self.locale_event.name['en']
|
||||||
|
|
||||||
def test_mail_text_order_placed(self):
|
def test_mail_text_order_placed(self):
|
||||||
text = '{event}{total}{currency}{date}{paymentinfo}{url}{invoice_name}{invoice_company}'
|
text = '{event}{total}{currency}{date}{payment_info}{url}{invoice_name}{invoice_company}'
|
||||||
response = self.client.post(self.target.format(
|
response = self.client.post(self.target.format(
|
||||||
self.orga1.slug, self.event1.slug), {
|
self.orga1.slug, self.event1.slug), {
|
||||||
'item': 'mail_text_order_placed',
|
'item': 'mail_text_order_placed',
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ def item(event):
|
|||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def item_category(event):
|
def item_category(event):
|
||||||
return ItemCategory.all.create(event=event)
|
return ItemCategory.objects.create(event=event)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ class CsvImportTest(TestCase):
|
|||||||
with open(os.path.join(DATA_DIR, filename), 'rb') as f:
|
with open(os.path.join(DATA_DIR, filename), 'rb') as f:
|
||||||
data = csvimport.get_rows_from_file(f)
|
data = csvimport.get_rows_from_file(f)
|
||||||
self.assertEqual(data, expected)
|
self.assertEqual(data, expected)
|
||||||
parsed = csvimport.parse(data, hint)
|
parsed, good = csvimport.parse(data, hint)
|
||||||
self.assertEqual(parsed, expected_parsed)
|
self.assertEqual(parsed, expected_parsed)
|
||||||
|
|
||||||
def test_sample_file_bbbank(self):
|
def test_sample_file_bbbank(self):
|
||||||
@@ -125,7 +125,7 @@ class CsvImportTest(TestCase):
|
|||||||
def test_sample_file_postbank(self):
|
def test_sample_file_postbank(self):
|
||||||
expected = [
|
expected = [
|
||||||
['Buchungstag', 'Wertstellung', 'Umsatzart', 'Buchungsdetails', 'Auftraggeber', 'Empfänger',
|
['Buchungstag', 'Wertstellung', 'Umsatzart', 'Buchungsdetails', 'Auftraggeber', 'Empfänger',
|
||||||
'Betrag (\x80)', 'Saldo (\x80)'],
|
'Betrag (€)', 'Saldo (€)'],
|
||||||
['07.08.2016', '01.08.2016', 'Gutschrift', 'Verwendungszweck 2015ABCDE', 'Karla Kundin',
|
['07.08.2016', '01.08.2016', 'Gutschrift', 'Verwendungszweck 2015ABCDE', 'Karla Kundin',
|
||||||
'Fiktive Veranstaltungsgesellschaft mbH', '\xA4 42,00', '\xA4 1.337,42'],
|
'Fiktive Veranstaltungsgesellschaft mbH', '\xA4 42,00', '\xA4 1.337,42'],
|
||||||
['29.07.2016', '29.07.2016', 'Gutschrift', 'Referenz NOTPROVIDED', 'Lars Lieferant',
|
['29.07.2016', '29.07.2016', 'Gutschrift', 'Referenz NOTPROVIDED', 'Lars Lieferant',
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ def env(client):
|
|||||||
plugins='pretix.plugins.paypal',
|
plugins='pretix.plugins.paypal',
|
||||||
live=True
|
live=True
|
||||||
)
|
)
|
||||||
category = ItemCategory.all.create(event=event, name="Everything", position=0)
|
category = ItemCategory.objects.create(event=event, name="Everything", position=0)
|
||||||
quota_tickets = Quota.objects.create(event=event, name='Tickets', size=5)
|
quota_tickets = Quota.objects.create(event=event, name='Tickets', size=5)
|
||||||
ticket = Item.objects.create(event=event, name='Early-bird ticket',
|
ticket = Item.objects.create(event=event, name='Early-bird ticket',
|
||||||
category=category, default_price=23, admission=True)
|
category=category, default_price=23, admission=True)
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ def env(client):
|
|||||||
plugins='pretix.plugins.stripe',
|
plugins='pretix.plugins.stripe',
|
||||||
live=True
|
live=True
|
||||||
)
|
)
|
||||||
category = ItemCategory.all.create(event=event, name="Everything", position=0)
|
category = ItemCategory.objects.create(event=event, name="Everything", position=0)
|
||||||
quota_tickets = Quota.objects.create(event=event, name='Tickets', size=5)
|
quota_tickets = Quota.objects.create(event=event, name='Tickets', size=5)
|
||||||
ticket = Item.objects.create(event=event, name='Early-bird ticket',
|
ticket = Item.objects.create(event=event, name='Early-bird ticket',
|
||||||
category=category, default_price=23, admission=True)
|
category=category, default_price=23, admission=True)
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ def item(event):
|
|||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def item_category(event):
|
def item_category(event):
|
||||||
"""Returns an item category instance"""
|
"""Returns an item category instance"""
|
||||||
return ItemCategory.all.create(event=event)
|
return ItemCategory.objects.create(event=event)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ class CartTestMixin:
|
|||||||
date_from=datetime.datetime(2013, 12, 26, tzinfo=datetime.timezone.utc),
|
date_from=datetime.datetime(2013, 12, 26, tzinfo=datetime.timezone.utc),
|
||||||
live=True
|
live=True
|
||||||
)
|
)
|
||||||
self.category = ItemCategory.all.create(event=self.event, name="Everything", position=0)
|
self.category = ItemCategory.objects.create(event=self.event, name="Everything", position=0)
|
||||||
self.quota_shirts = Quota.objects.create(event=self.event, name='Shirts', size=2)
|
self.quota_shirts = Quota.objects.create(event=self.event, name='Shirts', size=2)
|
||||||
self.shirt = Item.objects.create(event=self.event, name='T-Shirt', category=self.category, default_price=12)
|
self.shirt = Item.objects.create(event=self.event, name='T-Shirt', category=self.category, default_price=12)
|
||||||
self.quota_shirts.items.add(self.shirt)
|
self.quota_shirts.items.add(self.shirt)
|
||||||
@@ -1013,7 +1013,7 @@ class CartTest(CartTestMixin, TestCase):
|
|||||||
class CartAddonTest(CartTestMixin, TestCase):
|
class CartAddonTest(CartTestMixin, TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super().setUp()
|
super().setUp()
|
||||||
self.workshopcat = ItemCategory.all.create(name="Workshops", is_addon=True, event=self.event)
|
self.workshopcat = ItemCategory.objects.create(name="Workshops", is_addon=True, event=self.event)
|
||||||
self.workshopquota = Quota.objects.create(event=self.event, name='Workshop 1', size=5)
|
self.workshopquota = Quota.objects.create(event=self.event, name='Workshop 1', size=5)
|
||||||
self.workshop1 = Item.objects.create(event=self.event, name='Workshop 1',
|
self.workshop1 = Item.objects.create(event=self.event, name='Workshop 1',
|
||||||
category=self.workshopcat, default_price=12)
|
category=self.workshopcat, default_price=12)
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ class CheckoutTestCase(TestCase):
|
|||||||
plugins='pretix.plugins.stripe,pretix.plugins.banktransfer',
|
plugins='pretix.plugins.stripe,pretix.plugins.banktransfer',
|
||||||
live=True
|
live=True
|
||||||
)
|
)
|
||||||
self.category = ItemCategory.all.create(event=self.event, name="Everything", position=0)
|
self.category = ItemCategory.objects.create(event=self.event, name="Everything", position=0)
|
||||||
self.quota_tickets = Quota.objects.create(event=self.event, name='Tickets', size=5)
|
self.quota_tickets = Quota.objects.create(event=self.event, name='Tickets', size=5)
|
||||||
self.ticket = Item.objects.create(event=self.event, name='Early-bird ticket',
|
self.ticket = Item.objects.create(event=self.event, name='Early-bird ticket',
|
||||||
category=self.category, default_price=23, admission=True)
|
category=self.category, default_price=23, admission=True)
|
||||||
@@ -36,7 +36,7 @@ class CheckoutTestCase(TestCase):
|
|||||||
self.session_key = self.client.cookies.get(settings.SESSION_COOKIE_NAME).value
|
self.session_key = self.client.cookies.get(settings.SESSION_COOKIE_NAME).value
|
||||||
self._set_session('email', 'admin@localhost')
|
self._set_session('email', 'admin@localhost')
|
||||||
|
|
||||||
self.workshopcat = ItemCategory.all.create(name="Workshops", is_addon=True, event=self.event)
|
self.workshopcat = ItemCategory.objects.create(name="Workshops", is_addon=True, event=self.event)
|
||||||
self.workshopquota = Quota.objects.create(event=self.event, name='Workshop 1', size=5)
|
self.workshopquota = Quota.objects.create(event=self.event, name='Workshop 1', size=5)
|
||||||
self.workshop1 = Item.objects.create(event=self.event, name='Workshop 1',
|
self.workshop1 = Item.objects.create(event=self.event, name='Workshop 1',
|
||||||
category=self.workshopcat, default_price=12)
|
category=self.workshopcat, default_price=12)
|
||||||
|
|||||||
@@ -66,6 +66,18 @@ class EventMiddlewareTest(EventTestMixin, SoupTest):
|
|||||||
|
|
||||||
|
|
||||||
class ItemDisplayTest(EventTestMixin, SoupTest):
|
class ItemDisplayTest(EventTestMixin, SoupTest):
|
||||||
|
def test_link_rewrite(self):
|
||||||
|
q = Quota.objects.create(event=self.event, name='Quota', size=2)
|
||||||
|
item = Item.objects.create(event=self.event, name='Early-bird ticket', default_price=0, active=True,
|
||||||
|
description="http://example.org [Sample](http://example.net)")
|
||||||
|
q.items.add(item)
|
||||||
|
html = self.client.get('/%s/%s/' % (self.orga.slug, self.event.slug)).rendered_content
|
||||||
|
|
||||||
|
self.assertNotIn('href="http://example.org', html)
|
||||||
|
self.assertNotIn('href="http://example.net', html)
|
||||||
|
self.assertIn('href="/redirect/?url=http%3A//example.org%3A', html)
|
||||||
|
self.assertIn('href="/redirect/?url=http%3A//example.net%3A', html)
|
||||||
|
|
||||||
def test_not_active(self):
|
def test_not_active(self):
|
||||||
q = Quota.objects.create(event=self.event, name='Quota', size=2)
|
q = Quota.objects.create(event=self.event, name='Quota', size=2)
|
||||||
item = Item.objects.create(event=self.event, name='Early-bird ticket', default_price=0, active=False)
|
item = Item.objects.create(event=self.event, name='Early-bird ticket', default_price=0, active=False)
|
||||||
@@ -116,7 +128,7 @@ class ItemDisplayTest(EventTestMixin, SoupTest):
|
|||||||
self.assertNotIn("Early-bird", html)
|
self.assertNotIn("Early-bird", html)
|
||||||
|
|
||||||
def test_simple_with_category(self):
|
def test_simple_with_category(self):
|
||||||
c = ItemCategory.all.create(event=self.event, name="Entry tickets", position=0)
|
c = ItemCategory.objects.create(event=self.event, name="Entry tickets", position=0)
|
||||||
q = Quota.objects.create(event=self.event, name='Quota', size=2)
|
q = Quota.objects.create(event=self.event, name='Quota', size=2)
|
||||||
item = Item.objects.create(event=self.event, name='Early-bird ticket', category=c, default_price=0)
|
item = Item.objects.create(event=self.event, name='Early-bird ticket', category=c, default_price=0)
|
||||||
q.items.add(item)
|
q.items.add(item)
|
||||||
@@ -125,13 +137,13 @@ class ItemDisplayTest(EventTestMixin, SoupTest):
|
|||||||
self.assertIn("Early-bird", doc.select("section:nth-of-type(1) div:nth-of-type(1)")[0].text)
|
self.assertIn("Early-bird", doc.select("section:nth-of-type(1) div:nth-of-type(1)")[0].text)
|
||||||
|
|
||||||
def test_simple_without_quota(self):
|
def test_simple_without_quota(self):
|
||||||
c = ItemCategory.all.create(event=self.event, name="Entry tickets", position=0)
|
c = ItemCategory.objects.create(event=self.event, name="Entry tickets", position=0)
|
||||||
Item.objects.create(event=self.event, name='Early-bird ticket', category=c, default_price=0)
|
Item.objects.create(event=self.event, name='Early-bird ticket', category=c, default_price=0)
|
||||||
resp = self.client.get('/%s/%s/' % (self.orga.slug, self.event.slug))
|
resp = self.client.get('/%s/%s/' % (self.orga.slug, self.event.slug))
|
||||||
self.assertNotIn("Early-bird", resp.rendered_content)
|
self.assertNotIn("Early-bird", resp.rendered_content)
|
||||||
|
|
||||||
def test_no_variations_in_quota(self):
|
def test_no_variations_in_quota(self):
|
||||||
c = ItemCategory.all.create(event=self.event, name="Entry tickets", position=0)
|
c = ItemCategory.objects.create(event=self.event, name="Entry tickets", position=0)
|
||||||
q = Quota.objects.create(event=self.event, name='Quota', size=2)
|
q = Quota.objects.create(event=self.event, name='Quota', size=2)
|
||||||
item = Item.objects.create(event=self.event, name='Early-bird ticket', category=c, default_price=0)
|
item = Item.objects.create(event=self.event, name='Early-bird ticket', category=c, default_price=0)
|
||||||
ItemVariation.objects.create(item=item, value='Blue')
|
ItemVariation.objects.create(item=item, value='Blue')
|
||||||
@@ -140,7 +152,7 @@ class ItemDisplayTest(EventTestMixin, SoupTest):
|
|||||||
self.assertNotIn("Early-bird", resp.rendered_content)
|
self.assertNotIn("Early-bird", resp.rendered_content)
|
||||||
|
|
||||||
def test_one_variation_in_quota(self):
|
def test_one_variation_in_quota(self):
|
||||||
c = ItemCategory.all.create(event=self.event, name="Entry tickets", position=0)
|
c = ItemCategory.objects.create(event=self.event, name="Entry tickets", position=0)
|
||||||
q = Quota.objects.create(event=self.event, name='Quota', size=2)
|
q = Quota.objects.create(event=self.event, name='Quota', size=2)
|
||||||
item = Item.objects.create(event=self.event, name='Early-bird ticket', category=c, default_price=0)
|
item = Item.objects.create(event=self.event, name='Early-bird ticket', category=c, default_price=0)
|
||||||
var1 = ItemVariation.objects.create(item=item, value='Red')
|
var1 = ItemVariation.objects.create(item=item, value='Red')
|
||||||
@@ -150,7 +162,7 @@ class ItemDisplayTest(EventTestMixin, SoupTest):
|
|||||||
self._assert_variation_found()
|
self._assert_variation_found()
|
||||||
|
|
||||||
def test_one_variation_in_unlimited_quota(self):
|
def test_one_variation_in_unlimited_quota(self):
|
||||||
c = ItemCategory.all.create(event=self.event, name="Entry tickets", position=0)
|
c = ItemCategory.objects.create(event=self.event, name="Entry tickets", position=0)
|
||||||
q = Quota.objects.create(event=self.event, name='Quota', size=None)
|
q = Quota.objects.create(event=self.event, name='Quota', size=None)
|
||||||
item = Item.objects.create(event=self.event, name='Early-bird ticket', category=c, default_price=0)
|
item = Item.objects.create(event=self.event, name='Early-bird ticket', category=c, default_price=0)
|
||||||
var1 = ItemVariation.objects.create(item=item, value='Red')
|
var1 = ItemVariation.objects.create(item=item, value='Red')
|
||||||
@@ -166,7 +178,7 @@ class ItemDisplayTest(EventTestMixin, SoupTest):
|
|||||||
self.assertNotIn("Black", doc.select("section:nth-of-type(1)")[0].text)
|
self.assertNotIn("Black", doc.select("section:nth-of-type(1)")[0].text)
|
||||||
|
|
||||||
def test_variation_prices_in_quota(self):
|
def test_variation_prices_in_quota(self):
|
||||||
c = ItemCategory.all.create(event=self.event, name="Entry tickets", position=0)
|
c = ItemCategory.objects.create(event=self.event, name="Entry tickets", position=0)
|
||||||
q = Quota.objects.create(event=self.event, name='Quota', size=2)
|
q = Quota.objects.create(event=self.event, name='Quota', size=2)
|
||||||
item = Item.objects.create(event=self.event, name='Early-bird ticket', category=c, default_price=12)
|
item = Item.objects.create(event=self.event, name='Early-bird ticket', category=c, default_price=12)
|
||||||
var1 = ItemVariation.objects.create(item=item, value='Red', default_price=14, position=1)
|
var1 = ItemVariation.objects.create(item=item, value='Red', default_price=14, position=1)
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ class OrdersTest(TestCase):
|
|||||||
self.event.settings.set('payment_banktransfer__enabled', True)
|
self.event.settings.set('payment_banktransfer__enabled', True)
|
||||||
self.event.settings.set('ticketoutput_testdummy__enabled', True)
|
self.event.settings.set('ticketoutput_testdummy__enabled', True)
|
||||||
|
|
||||||
self.category = ItemCategory.all.create(event=self.event, name="Everything", position=0)
|
self.category = ItemCategory.objects.create(event=self.event, name="Everything", position=0)
|
||||||
self.quota_shirts = Quota.objects.create(event=self.event, name='Shirts', size=2)
|
self.quota_shirts = Quota.objects.create(event=self.event, name='Shirts', size=2)
|
||||||
self.shirt = Item.objects.create(event=self.event, name='T-Shirt', category=self.category, default_price=12)
|
self.shirt = Item.objects.create(event=self.event, name='T-Shirt', category=self.category, default_price=12)
|
||||||
self.quota_shirts.items.add(self.shirt)
|
self.quota_shirts.items.add(self.shirt)
|
||||||
|
|||||||
Reference in New Issue
Block a user