diff --git a/src/pretix/base/models.py b/src/pretix/base/models.py index 32cef5eecf..48089bfe74 100644 --- a/src/pretix/base/models.py +++ b/src/pretix/base/models.py @@ -297,9 +297,19 @@ class Organizer(Versionable): name = models.CharField(max_length=200, verbose_name=_("Name")) - slug = models.SlugField(max_length=50, - db_index=True, - verbose_name=_("Slug")) + slug = models.SlugField( + max_length=50, db_index=True, + help_text=_( + "Should be short, only contain lowercase letters and numbers, and must be unique among your events. " + + "This is being used in addresses and bank transfer references."), + validators=[ + RegexValidator( + regex="^[a-zA-Z0-9.-]+$", + message=_("The slug may only contain letters, numbers, dots and dashes."), + ) + ], + verbose_name=_("Slug"), + ) permitted = models.ManyToManyField(User, through='OrganizerPermission', related_name="organizers") diff --git a/src/pretix/control/templates/pretixcontrol/event/settings.html b/src/pretix/control/templates/pretixcontrol/event/settings.html index 36630fad75..04e1724e31 100644 --- a/src/pretix/control/templates/pretixcontrol/event/settings.html +++ b/src/pretix/control/templates/pretixcontrol/event/settings.html @@ -3,11 +3,6 @@ {% load bootstrap3 %} {% block inside %}
- {% if "success" in request.GET %} -
- {% trans "Your changes have been saved." %} -
- {% endif %} {% csrf_token %}
{% trans "General information" %} diff --git a/src/pretix/control/templates/pretixcontrol/events/create.html b/src/pretix/control/templates/pretixcontrol/events/create.html new file mode 100644 index 0000000000..5210f3dcc0 --- /dev/null +++ b/src/pretix/control/templates/pretixcontrol/events/create.html @@ -0,0 +1,33 @@ +{% extends "pretixcontrol/base.html" %} +{% load i18n %} +{% load bootstrap3 %} +{% block title %}{% trans "Create a new event" %}{% endblock %} +{% block content %} +

{% trans "Create a new event" %}

+ + {% csrf_token %} +
+ {% trans "General information" %} + {% bootstrap_field form.name layout="horizontal" %} + {% bootstrap_field form.slug layout="horizontal" %} + {% bootstrap_field form.date_from layout="horizontal" %} + {% bootstrap_field form.date_to layout="horizontal" %} + {% bootstrap_field form.currency layout="horizontal" %} +
+
+ {% trans "Timeline" %} + {% bootstrap_field form.presale_start layout="horizontal" %} + {% bootstrap_field form.presale_end layout="horizontal" %} +
+

+ {% blocktrans trimmed %} + You will be able to adjust further settings in the next step. + {% endblocktrans %} +

+
+ +
+ +{% endblock %} diff --git a/src/pretix/control/templates/pretixcontrol/events/index.html b/src/pretix/control/templates/pretixcontrol/events/index.html index 84b0d54f74..48e6a688c4 100644 --- a/src/pretix/control/templates/pretixcontrol/events/index.html +++ b/src/pretix/control/templates/pretixcontrol/events/index.html @@ -4,6 +4,10 @@ {% block content %}

{% trans "Events" %}

{% trans "The list below shows all events you have administrative access to. Click on the event name to access event details." %}

+ + + {% trans "Create a new event" %} + diff --git a/src/pretix/control/templates/pretixcontrol/events/start.html b/src/pretix/control/templates/pretixcontrol/events/start.html new file mode 100644 index 0000000000..d29a95766c --- /dev/null +++ b/src/pretix/control/templates/pretixcontrol/events/start.html @@ -0,0 +1,30 @@ +{% extends "pretixcontrol/base.html" %} +{% load i18n %} +{% block title %}{% trans "Create a new event" %}{% endblock %} +{% block content %} +

{% trans "Create a new event" %}

+ {% if organizers|length == 0 %} +
+ {% trans "You are not permitted to create new events in the name of any organizer." %} +
+ {% else %} +

{% trans "Please choose the organizer of this event from the list below." %}

+
+ + + + + + + {% for o in organizers %} + + + + {% endfor %} + +
{% trans "Organizer name" %}
+ {{ o.name }} +
+ {% include "pretixcontrol/pagination.html" %} + {% endif %} +{% endblock %} diff --git a/src/pretix/control/templates/pretixcontrol/organizers/create.html b/src/pretix/control/templates/pretixcontrol/organizers/create.html index b27c3bdf91..d92d629962 100644 --- a/src/pretix/control/templates/pretixcontrol/organizers/create.html +++ b/src/pretix/control/templates/pretixcontrol/organizers/create.html @@ -1,9 +1,9 @@ {% extends "pretixcontrol/base.html" %} {% load i18n %} {% load bootstrap3 %} -{% block title %}{% trans "Create Organizer" %}{% endblock %} +{% block title %}{% trans "Create a new organizer" %}{% endblock %} {% block content %} -

{% trans "Create Organizer" %}

+

{% trans "Create a new organizer" %}

{% csrf_token %}
diff --git a/src/pretix/control/urls.py b/src/pretix/control/urls.py index 5110540c9b..8662439d3f 100644 --- a/src/pretix/control/urls.py +++ b/src/pretix/control/urls.py @@ -12,6 +12,8 @@ urlpatterns = [ url(r'^organizers/add$', organizer.OrganizerCreate.as_view(), name='organizers.add'), url(r'^organizer/(?P[^/]+)/edit$', organizer.OrganizerUpdate.as_view(), name='organizer.edit'), url(r'^events/$', main.EventList.as_view(), name='events'), + url(r'^events/add$', main.EventCreateStart.as_view(), name='events.add'), + url(r'^event/(?P[^/]+)/add', main.EventCreate.as_view(), name='events.create'), url(r'^event/(?P[^/]+)/(?P[^/]+)/', include([ url(r'^$', event.index, name='event.index'), url(r'^settings/$', event.EventUpdate.as_view(), name='event.settings'), diff --git a/src/pretix/control/views/event.py b/src/pretix/control/views/event.py index 9d3494f45a..4bf01c9b29 100644 --- a/src/pretix/control/views/event.py +++ b/src/pretix/control/views/event.py @@ -1,5 +1,6 @@ from collections import OrderedDict from django.conf import settings +from django.contrib import messages from django.shortcuts import render, redirect from django.utils.functional import cached_property from django.views.generic import FormView @@ -156,13 +157,14 @@ class EventUpdate(EventPermissionRequiredMixin, UpdateView): def form_valid(self, form): self.sform.save() + messages.success(self.request, _('Your changes have been saved.')) return super().form_valid(form) def get_success_url(self) -> str: return reverse('control:event.settings', kwargs={ 'organizer': self.object.organizer.slug, 'event': self.object.slug, - }) + '?success=true' + }) def post(self, request, *args, **kwargs): form = self.get_form() diff --git a/src/pretix/control/views/main.py b/src/pretix/control/views/main.py index 622c0541a7..d8f6c05edc 100644 --- a/src/pretix/control/views/main.py +++ b/src/pretix/control/views/main.py @@ -1,7 +1,13 @@ +from django import forms +from django.contrib import messages +from django.core.urlresolvers import reverse from django.shortcuts import render -from django.views.generic import ListView +from django.views.generic import ListView, CreateView, TemplateView +from django.utils.translation import ugettext_lazy as _ +from pretix.base.forms import VersionedModelForm -from pretix.base.models import Event +from pretix.base.models import Event, EventPermission, Organizer, OrganizerPermission +from pretix.control.permissions import OrganizerPermissionRequiredMixin class EventList(ListView): @@ -20,3 +26,79 @@ class EventList(ListView): def index(request): return render(request, 'pretixcontrol/base.html', {}) + + +class EventForm(VersionedModelForm): + error_messages = { + 'duplicate_slug': _("You already used this slug for a different event. Please choose a new one."), + } + + class Meta: + model = Event + fields = [ + 'name', + 'slug', + 'currency', + 'date_from', + 'date_to', + 'presale_start', + 'presale_end' + ] + + def __init__(self, *args, **kwargs): + self.organizer = kwargs.pop('organizer') + super().__init__(*args, **kwargs) + + def clean_slug(self): + slug = self.cleaned_data['slug'] + if Event.objects.filter(slug=slug, organizer=self.organizer).exists(): + raise forms.ValidationError( + self.error_messages['duplicate_slug'], + code='duplicate_slug', + ) + return slug + + +class EventCreateStart(TemplateView): + template_name = 'pretixcontrol/events/start.html' + + def get_context_data(self, **kwargs): + ctx = super().get_context_data(**kwargs) + ctx['organizers'] = [ + p.organizer for p in OrganizerPermission.objects.current.filter( + user=self.request.user, can_create_events=True + ).select_related("organizer") + ] + return ctx + + +class EventCreate(OrganizerPermissionRequiredMixin, CreateView): + model = Event + form_class = EventForm + template_name = 'pretixcontrol/events/create.html' + context_object_name = 'event' + permission = 'can_create_events' + + def dispatch(self, request, *args, **kwargs): + return super().dispatch(request, *args, **kwargs) + + def get_form_kwargs(self): + kwargs = super().get_form_kwargs() + kwargs['organizer'] = self.request.organizer + return kwargs + + def form_valid(self, form): + messages.success(self.request, _('The new event has been created.')) + form.instance.organizer = self.request.organizer + ret = super().form_valid(form) + EventPermission.objects.create( + event=form.instance, user=self.request.user, + ) + self.object = form.instance + return ret + + def get_success_url(self) -> str: + return reverse('control:event.settings', kwargs={ + 'organizer': self.request.organizer.slug, + 'event': self.object.slug, + }) diff --git a/src/pretix/control/views/organizer.py b/src/pretix/control/views/organizer.py index 597b6e4d44..2c4327f182 100644 --- a/src/pretix/control/views/organizer.py +++ b/src/pretix/control/views/organizer.py @@ -1,3 +1,4 @@ +from django import forms from django.contrib import messages from django.core.urlresolvers import reverse from django.http import HttpResponseForbidden @@ -25,10 +26,23 @@ class OrganizerList(ListView): class OrganizerForm(VersionedModelForm): + error_messages = { + 'duplicate_slug': _("This slug is already in use. Please choose a different one."), + } + class Meta: model = Organizer fields = ['name', 'slug'] + def clean_slug(self): + slug = self.cleaned_data['slug'] + if Organizer.objects.filter(slug=slug).exists(): + raise forms.ValidationError( + self.error_messages['duplicate_slug'], + code='duplicate_slug', + ) + return slug + class OrganizerUpdateForm(OrganizerForm): @@ -71,9 +85,6 @@ class OrganizerCreate(CreateView): return HttpResponseForbidden() # TODO return super().dispatch(request, *args, **kwargs) - def get_object(self, queryset=None) -> Organizer: - return self.request.organizer - def form_valid(self, form): messages.success(self.request, _('The new organizer has been created.')) ret = super().form_valid(form)