diff --git a/src/tixlbase/models.py b/src/tixlbase/models.py index 1d5099a96..e2ac48d7a 100644 --- a/src/tixlbase/models.py +++ b/src/tixlbase/models.py @@ -3,6 +3,7 @@ from django.conf import settings from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, PermissionsMixin from django.utils.translation import ugettext_lazy as _ from django.template.defaultfilters import date as _date +from django.core.validators import RegexValidator class UserManager(BaseUserManager): @@ -215,8 +216,19 @@ class Event(models.Model): on_delete=models.PROTECT) name = models.CharField(max_length=200, verbose_name=_("Name")) - slug = models.CharField(max_length=50, db_index=True, - verbose_name=_("Slug")) + slug = models.CharField( + 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='EventPermission', related_name="events",) locale = models.CharField(max_length=10, @@ -232,27 +244,33 @@ class Event(models.Model): verbose_name=_("Event end time")) show_date_to = models.BooleanField( default=True, - verbose_name=_("Show event end date") + verbose_name=_("Show event end date"), + help_text=("If disabled, only event's start date will be displayed to the public."), ) show_times = models.BooleanField( default=True, - verbose_name=_("Show dates with time") + verbose_name=_("Show dates with time"), + help_text=("If disabled, the event's start and end date will be displayed without the time of day."), ) presale_end = models.DateTimeField( null=True, blank=True, - verbose_name=_("End of presale") + verbose_name=_("End of presale"), + help_text=_("No items will be sold after this date."), ) presale_start = models.DateTimeField( null=True, blank=True, - verbose_name=_("Start of presale") + verbose_name=_("Start of presale"), + help_text=_("No items will be sold before this date."), ) payment_term_days = models.IntegerField( default=14, - verbose_name=_("Payment term in days") + verbose_name=_("Payment term in days"), + help_text=_("The number of days after placing an order the user has to pay to preserve his reservation."), ) payment_term_last = models.DateTimeField( null=True, blank=True, - verbose_name=_("Last date of payments") + verbose_name=_("Last date of payments"), + help_text=_("The last date any payments are accepted. This has precedence over the number of days configured above.") ) def get_date_from_display(self): diff --git a/src/tixlcontrol/permissions.py b/src/tixlcontrol/permissions.py new file mode 100644 index 000000000..d584b0e0f --- /dev/null +++ b/src/tixlcontrol/permissions.py @@ -0,0 +1,32 @@ +from django.http import HttpResponseForbidden +from django.utils.translation import ugettext as _ + +from tixlbase.models import EventPermission + + +def event_permission_required(function, permission): + def wrapper(request, *args, **kw): + if not request.user.is_authenticated(): + return HttpResponseForbidden() + perm = EventPermission.objects.get( + event=request.event, + user=request.user + ) + allowed = False + try: + allowed = getattr(perm, permission) + except AttributeError: + pass + if allowed: + return function(request, *args, **kw) + return HttpResponseForbidden(_('You do not have permission to view this content.')) + return wrapper + + +class EventPermissionRequiredMixin: + permission = '' + + @classmethod + def as_view(cls, **initkwargs): + view = super(EventPermissionRequiredMixin, cls).as_view(**initkwargs) + return event_permission_required(view, cls.permission) diff --git a/src/tixlcontrol/templates/tixlcontrol/event/base.html b/src/tixlcontrol/templates/tixlcontrol/event/base.html index afccc93bf..9c359455d 100644 --- a/src/tixlcontrol/templates/tixlcontrol/event/base.html +++ b/src/tixlcontrol/templates/tixlcontrol/event/base.html @@ -15,6 +15,6 @@ {% endfor %} -
  • {% trans "Dashboard" %}
  • -
  • {% trans "Settings" %}
  • +
  • {% trans "Dashboard" %}
  • +
  • {% trans "Settings" %}
  • {% endblock %} diff --git a/src/tixlcontrol/templates/tixlcontrol/event/settings.html b/src/tixlcontrol/templates/tixlcontrol/event/settings.html new file mode 100644 index 000000000..1463e44ca --- /dev/null +++ b/src/tixlcontrol/templates/tixlcontrol/event/settings.html @@ -0,0 +1,42 @@ +{% extends "tixlcontrol/event/base.html" %} +{% load i18n %} +{% load bootstrap3 %} +{% block title %}{{ request.event.name }}{% endblock %} +{% block content %} +

    {% trans "Settings" %}

    +
    + {% 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 "Display settings" %} + {% bootstrap_field form.locale layout="horizontal" %} + {% bootstrap_field form.timezone layout="horizontal" %} + {% bootstrap_field form.show_date_to layout="horizontal" %} + {% bootstrap_field form.show_times layout="horizontal" %} +
    +
    + {% trans "Presale settings" %} + {% bootstrap_field form.presale_start layout="horizontal" %} + {% bootstrap_field form.presale_end layout="horizontal" %} +
    +
    + {% trans "Payment settings" %} + {% bootstrap_field form.payment_term_days layout="horizontal" %} + {% bootstrap_field form.payment_term_last layout="horizontal" %} +
    +
    +
    + +
    +
    +
    +{% endblock %} diff --git a/src/tixlcontrol/urls.py b/src/tixlcontrol/urls.py index df6920998..0c6fd8ad8 100644 --- a/src/tixlcontrol/urls.py +++ b/src/tixlcontrol/urls.py @@ -1,9 +1,10 @@ from django.conf.urls import patterns, url -from tixlcontrol.views import main +from tixlcontrol.views import main, event urlpatterns = patterns('', url(r'^$', 'tixlcontrol.views.main.index', name='index'), url(r'^event/(?P[^/]+)/(?P[^/]+)/$', 'tixlcontrol.views.event.index', name='event.index'), + url(r'^event/(?P[^/]+)/(?P[^/]+)/settings$', event.EventUpdate.as_view(), name='event.settings'), url(r'^events/$', main.EventList.as_view(), name='events'), url(r'^logout$', 'tixlcontrol.views.auth.logout', name='auth.logout'), url(r'^login$', 'tixlcontrol.views.auth.login', name='auth.login'), diff --git a/src/tixlcontrol/views/event.py b/src/tixlcontrol/views/event.py index 9464d18e1..17bc63f99 100644 --- a/src/tixlcontrol/views/event.py +++ b/src/tixlcontrol/views/event.py @@ -1,4 +1,63 @@ from django.shortcuts import render +from django.views.generic.edit import UpdateView +from django import forms +from django.utils.translation import ugettext_lazy as _ +from django.core.urlresolvers import reverse + +from pytz import common_timezones + +from tixlbase.models import Event +from tixlcontrol.permissions import EventPermissionRequiredMixin + + +class EventUpdateForm(forms.ModelForm): + + timezone = forms.ChoiceField( + choices=((a, a) for a in common_timezones), + label=_("Default timezone"), + ) + + def clean_slug(self): + return self.instance.slug + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.fields['slug'].widget.attrs['readonly'] = True + + class Meta: + model = Event + localized_fields = '__all__' + fields = [ + 'name', + 'slug', + 'locale', + 'timezone', + 'currency', + 'date_from', + 'date_to', + 'show_date_to', + 'show_times', + 'presale_start', + 'presale_end', + 'payment_term_days', + 'payment_term_last', + ] + + +class EventUpdate(EventPermissionRequiredMixin, UpdateView): + model = Event + form_class = EventUpdateForm + template_name = 'tixlcontrol/event/settings.html' + permission = 'can_change_settings' + + def get_object(self, queryset=None): + return self.request.event + + def get_success_url(self): + return reverse('control:event.settings', kwargs={ + 'organizer': self.get_object().organizer.slug, + 'event': self.get_object().slug, + }) def index(request, organizer, event):