Compare commits

...

6 Commits

Author SHA1 Message Date
Raphael Michel
0581d82306 Prepare release 2017-03-28 10:37:37 +02:00
Raphael Michel
44ba1904f6 Backport minor fix 2017-03-28 10:37:02 +02:00
jlwt90
a5c7c9dd1e Fix #428 -- Timezone handling on event creation/update (#432)
* add event timezone during event creation

* add timezone handling in EventUpdate

* added event creation test cases & form cleaning bug fix
2017-03-28 10:27:11 +02:00
Raphael Michel
6d7ada4836 Sendmail plugin: Fix usage of old argument 2017-03-28 10:26:31 +02:00
Raphael Michel
96fa4d68bc Prepare release 2017-03-08 18:59:49 +01:00
Raphael Michel
a0d3cafc94 Pin bleach version 2017-03-08 18:58:39 +01:00
7 changed files with 228 additions and 8 deletions

View File

@@ -1 +1 @@
__version__ = "1.1.0" __version__ = "1.1.2"

View File

@@ -5,7 +5,7 @@ from django.core.validators import RegexValidator
from django.utils.timezone import get_current_timezone_name from django.utils.timezone import get_current_timezone_name
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from i18nfield.forms import I18nFormField, I18nTextarea from i18nfield.forms import I18nFormField, I18nTextarea
from pytz import common_timezones from pytz import common_timezones, timezone
from pretix.base.forms import I18nModelForm, SettingsForm from pretix.base.forms import I18nModelForm, SettingsForm
from pretix.base.models import Event, Organizer from pretix.base.models import Event, Organizer
@@ -76,12 +76,27 @@ class EventWizardBasicsForm(I18nModelForm):
def clean(self): def clean(self):
data = super().clean() data = super().clean()
if data['locale'] not in self.locales: if data.get('locale') not in self.locales:
raise ValidationError({ raise ValidationError({
'locale': _('Your default locale must also be enabled for your event (see box above).') 'locale': _('Your default locale must also be enabled for your event (see box above).')
}) })
if data.get('timezone') not in common_timezones:
raise ValidationError({
'timezone': _('Your default locale must be specified.')
})
# change timezone
zone = timezone(data.get('timezone'))
data['date_from'] = self.reset_timezone(zone, data.get('date_from'))
data['date_to'] = self.reset_timezone(zone, data.get('date_to'))
data['presale_start'] = self.reset_timezone(zone, data.get('presale_start'))
data['presale_end'] = self.reset_timezone(zone, data.get('presale_end'))
return data return data
@staticmethod
def reset_timezone(tz, dt):
return tz.localize(dt.replace(tzinfo=None)) if dt is not None else None
def clean_slug(self): def clean_slug(self):
slug = self.cleaned_data['slug'] slug = self.cleaned_data['slug']
if Event.objects.filter(slug=slug, organizer=self.organizer).exists(): if Event.objects.filter(slug=slug, organizer=self.organizer).exists():

View File

@@ -14,6 +14,7 @@ from django.utils.translation import ugettext_lazy as _
from django.views.generic import FormView, ListView from django.views.generic import FormView, ListView
from django.views.generic.base import TemplateView, View from django.views.generic.base import TemplateView, View
from django.views.generic.detail import SingleObjectMixin from django.views.generic.detail import SingleObjectMixin
from pytz import timezone
from pretix.base.forms import I18nModelForm from pretix.base.forms import I18nModelForm
from pretix.base.models import ( from pretix.base.models import (
@@ -88,10 +89,21 @@ class EventUpdate(EventPermissionRequiredMixin, UpdateView):
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
form = self.get_form() form = self.get_form()
if form.is_valid() and self.sform.is_valid(): if form.is_valid() and self.sform.is_valid():
# reset timezone
zone = timezone(self.sform.cleaned_data['timezone'])
event = form.instance
event.date_from = self.reset_timezone(zone, event.date_from)
event.date_to = self.reset_timezone(zone, event.date_to)
event.presale_start = self.reset_timezone(zone, event.presale_start)
event.presale_end = self.reset_timezone(zone, event.presale_end)
return self.form_valid(form) return self.form_valid(form)
else: else:
return self.form_invalid(form) return self.form_invalid(form)
@staticmethod
def reset_timezone(tz, dt):
return tz.localize(dt.replace(tzinfo=None)) if dt is not None else None
class EventPlugins(EventPermissionRequiredMixin, TemplateView, SingleObjectMixin): class EventPlugins(EventPermissionRequiredMixin, TemplateView, SingleObjectMixin):
model = Event model = Event

View File

@@ -15,11 +15,11 @@ class MailForm(forms.Form):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.fields['subject'] = I18nFormField( self.fields['subject'] = I18nFormField(
widget=I18nTextInput, required=True, widget=I18nTextInput, required=True,
langcodes=event.settings.get('locales') locales=event.settings.get('locales')
) )
self.fields['message'] = I18nFormField( self.fields['message'] = I18nFormField(
widget=I18nTextarea, required=True, widget=I18nTextarea, required=True,
langcodes=event.settings.get('locales'), locales=event.settings.get('locales'),
help_text=_("Available placeholders: {due_date}, {event}, {order}, {order_date}, {order_url}, " help_text=_("Available placeholders: {due_date}, {event}, {order}, {order_date}, {order_url}, "
"{invoice_name}, {invoice_company}") "{invoice_name}, {invoice_company}")
) )

View File

@@ -26,7 +26,7 @@ dj-static
csscompressor csscompressor
django-markup django-markup
markdown markdown
bleach bleach==1.5
raven raven
django-i18nfield django-i18nfield
# Stripe # Stripe

View File

@@ -89,7 +89,7 @@ setup(
'csscompressor', 'csscompressor',
'django-markup', 'django-markup',
'markdown', 'markdown',
'bleach', 'bleach==1.5',
'raven', 'raven',
'paypalrestsdk==1.12.*', 'paypalrestsdk==1.12.*',
'pycparser==2.13', 'pycparser==2.13',

View File

@@ -1,6 +1,9 @@
import datetime import datetime
from decimal import Decimal from decimal import Decimal
import pytz
from i18nfield.strings import LazyI18nString
from pytz import timezone
from tests.base import SoupTest, extract_form_fields from tests.base import SoupTest, extract_form_fields
from pretix.base.models import ( from pretix.base.models import (
@@ -44,7 +47,6 @@ class EventsTest(SoupTest):
doc = self.get_doc('/control/event/%s/%s/settings/' % (self.orga1.slug, self.event1.slug)) doc = self.get_doc('/control/event/%s/%s/settings/' % (self.orga1.slug, self.event1.slug))
doc.select("[name=date_to]")[0]['value'] = "2013-12-30 17:00:00" doc.select("[name=date_to]")[0]['value'] = "2013-12-30 17:00:00"
doc.select("[name=settings-max_items_per_order]")[0]['value'] = "12" doc.select("[name=settings-max_items_per_order]")[0]['value'] = "12"
print(extract_form_fields(doc.select('.container-fluid form')[0]))
doc = self.post_doc('/control/event/%s/%s/settings/' % (self.orga1.slug, self.event1.slug), doc = self.post_doc('/control/event/%s/%s/settings/' % (self.orga1.slug, self.event1.slug),
extract_form_fields(doc.select('.container-fluid form')[0])) extract_form_fields(doc.select('.container-fluid form')[0]))
@@ -52,6 +54,27 @@ class EventsTest(SoupTest):
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.select("[name=settings-max_items_per_order]")[0]['value'] == "12" assert doc.select("[name=settings-max_items_per_order]")[0]['value'] == "12"
def test_settings_timezone(self):
doc = self.get_doc('/control/event/%s/%s/settings/' % (self.orga1.slug, self.event1.slug))
doc.select("[name=date_to]")[0]['value'] = "2013-12-30 17:00:00"
doc.select("[name=settings-max_items_per_order]")[0]['value'] = "12"
doc.select("[name=settings-timezone]")[0]['value'] = "Asia/Tokyo"
doc.find('option', {"value": "Asia/Tokyo"})['selected'] = 'selected'
doc.find('option', {"value": "UTC"}).attrs.pop('selected')
doc = self.post_doc('/control/event/%s/%s/settings/' % (self.orga1.slug, self.event1.slug),
extract_form_fields(doc.select('.container-fluid form')[0]))
assert len(doc.select(".alert-success")) > 0
# 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.find('option', {"value": "Asia/Tokyo"})['selected'] == "selected"
assert doc.select("[name=settings-max_items_per_order]")[0]['value'] == "12"
self.event1.refresh_from_db()
# Asia/Tokyo -> GMT+9
assert self.event1.date_to.strftime('%Y-%m-%d %H:%M:%S') == "2013-12-30 08:00:00"
assert self.event1.settings.timezone == 'Asia/Tokyo'
def test_plugins(self): def test_plugins(self):
doc = self.get_doc('/control/event/%s/%s/settings/plugins' % (self.orga1.slug, self.event1.slug)) doc = self.get_doc('/control/event/%s/%s/settings/plugins' % (self.orga1.slug, self.event1.slug))
self.assertIn("PayPal", doc.select(".form-plugins")[0].text) self.assertIn("PayPal", doc.select(".form-plugins")[0].text)
@@ -193,3 +216,173 @@ class EventsTest(SoupTest):
data, follow=True) data, follow=True)
self.event1.settings._flush() self.event1.settings._flush()
assert self.event1.settings.get('ticket_download', as_type=bool) assert self.event1.settings.get('ticket_download', as_type=bool)
def test_create_event_unauthorized(self):
doc = self.post_doc('/control/events/add', {
'event_wizard-current_step': 'foundation',
'foundation-organizer': self.orga2.pk,
'foundation-locales': ('en', 'de')
})
assert doc.select(".alert-danger")
def test_create_invalid_default_language(self):
doc = self.post_doc('/control/events/add', {
'event_wizard-current_step': 'foundation',
'foundation-organizer': self.orga1.pk,
'foundation-locales': ('de',)
})
doc = self.post_doc('/control/events/add', {
'event_wizard-current_step': 'basics',
'basics-name_0': '33C3',
'basics-name_1': '33C3',
'basics-slug': '33c3',
'basics-date_from': '2016-12-27 10:00:00',
'basics-date_to': '2016-12-30 19:00:00',
'basics-location_0': 'Hamburg',
'basics-location_1': 'Hamburg',
'basics-currency': 'EUR',
'basics-locale': 'en',
'basics-timezone': 'Europe/Berlin',
'basics-presale_start': '2016-11-01 10:00:00',
'basics-presale_end': '2016-11-30 18:00:00',
})
assert doc.select(".alert-danger")
def test_create_duplicate_slug(self):
doc = self.post_doc('/control/events/add', {
'event_wizard-current_step': 'foundation',
'foundation-organizer': self.orga1.pk,
'foundation-locales': ('de', 'en')
})
doc = self.post_doc('/control/events/add', {
'event_wizard-current_step': 'basics',
'basics-name_0': '33C3',
'basics-name_1': '33C3',
'basics-slug': '31c3',
'basics-date_from': '2016-12-27 10:00:00',
'basics-date_to': '2016-12-30 19:00:00',
'basics-location_0': 'Hamburg',
'basics-location_1': 'Hamburg',
'basics-currency': 'EUR',
'basics-locale': 'en',
'basics-timezone': 'Europe/Berlin',
'basics-presale_start': '2016-11-01 10:00:00',
'basics-presale_end': '2016-11-30 18:00:00',
})
assert doc.select(".alert-danger")
def test_create_event_success(self):
doc = self.get_doc('/control/events/add')
tabletext = doc.select("form")[0].text
self.assertIn("CCC", tabletext)
self.assertNotIn("MRM", tabletext)
doc = self.post_doc('/control/events/add', {
'event_wizard-current_step': 'foundation',
'foundation-organizer': self.orga1.pk,
'foundation-locales': ('en', 'de')
})
assert doc.select("#id_basics-name_0")
assert doc.select("#id_basics-name_1")
doc = self.post_doc('/control/events/add', {
'event_wizard-current_step': 'basics',
'basics-name_0': '33C3',
'basics-name_1': '33C3',
'basics-slug': '33c3',
'basics-date_from': '2016-12-27 10:00:00',
'basics-date_to': '2016-12-30 19:00:00',
'basics-location_0': 'Hamburg',
'basics-location_1': 'Hamburg',
'basics-currency': 'EUR',
'basics-locale': 'en',
'basics-timezone': 'Europe/Berlin',
'basics-presale_start': '2016-11-01 10:00:00',
'basics-presale_end': '2016-11-30 18:00:00',
})
assert doc.select("#id_copy-copy_from_event_1")
self.post_doc('/control/events/add', {
'event_wizard-current_step': 'copy',
'copy-copy_from_event': ''
})
ev = Event.objects.get(slug='33c3')
assert ev.name == LazyI18nString({'de': '33C3', 'en': '33C3'})
assert ev.settings.locales == ['en', 'de']
assert ev.settings.locale == 'en'
assert ev.currency == 'EUR'
assert ev.settings.timezone == 'Europe/Berlin'
assert ev.organizer == self.orga1
assert ev.location == LazyI18nString({'de': 'Hamburg', 'en': 'Hamburg'})
assert EventPermission.objects.filter(event=ev, user=self.user).exists()
berlin_tz = timezone('Europe/Berlin')
assert ev.date_from == berlin_tz.localize(datetime.datetime(2016, 12, 27, 10, 0, 0)).astimezone(pytz.utc)
assert ev.date_to == berlin_tz.localize(datetime.datetime(2016, 12, 30, 19, 0, 0)).astimezone(pytz.utc)
assert ev.presale_start == berlin_tz.localize(datetime.datetime(2016, 11, 1, 10, 0, 0)).astimezone(pytz.utc)
assert ev.presale_end == berlin_tz.localize(datetime.datetime(2016, 11, 30, 18, 0, 0)).astimezone(pytz.utc)
def test_create_event_only_date_from(self):
# date_to, presale_start & presale_end are optional fields
self.post_doc('/control/events/add', {
'event_wizard-current_step': 'foundation',
'foundation-organizer': self.orga1.pk,
'foundation-locales': 'en'
})
self.post_doc('/control/events/add', {
'event_wizard-current_step': 'basics',
'basics-name_0': '33C3',
'basics-slug': '33c3',
'basics-date_from': '2016-12-27 10:00:00',
'basics-date_to': '',
'basics-location_0': 'Hamburg',
'basics-currency': 'EUR',
'basics-locale': 'en',
'basics-timezone': 'UTC',
'basics-presale_start': '',
'basics-presale_end': '',
})
self.post_doc('/control/events/add', {
'event_wizard-current_step': 'copy',
'copy-copy_from_event': ''
})
ev = Event.objects.get(slug='33c3')
assert ev.name == LazyI18nString({'en': '33C3'})
assert ev.settings.locales == ['en']
assert ev.settings.locale == 'en'
assert ev.currency == 'EUR'
assert ev.settings.timezone == 'UTC'
assert ev.organizer == self.orga1
assert ev.location == LazyI18nString({'en': 'Hamburg'})
assert EventPermission.objects.filter(event=ev, user=self.user).exists()
assert ev.date_from == datetime.datetime(2016, 12, 27, 10, 0, 0, tzinfo=pytz.utc)
assert ev.date_to is None
assert ev.presale_start is None
assert ev.presale_end is None
def test_create_event_missing_date_from(self):
# date_from is mandatory
self.post_doc('/control/events/add', {
'event_wizard-current_step': 'foundation',
'foundation-organizer': self.orga1.pk,
'foundation-locales': 'en'
})
doc = self.post_doc('/control/events/add', {
'event_wizard-current_step': 'basics',
'basics-name_0': '33C3',
'basics-slug': '33c3',
'basics-date_from': '',
'basics-date_to': '2016-12-30 19:00:00',
'basics-location_0': 'Hamburg',
'basics-currency': 'EUR',
'basics-locale': 'en',
'basics-timezone': 'Europe/Berlin',
'basics-presale_start': '2016-11-20 11:00:00',
'basics-presale_end': '2016-11-24 18:00:00',
})
assert doc.select(".alert-danger")