forked from CGM_Public/pretix_original
Merge branch 'master' of https://github.com/pretix/pretix
This commit is contained in:
10
.gitmodules
vendored
10
.gitmodules
vendored
@@ -1,6 +1,6 @@
|
|||||||
[submodule "src/pretixbase/static/bootstrap"]
|
[submodule "src/pretix/base/static/fontawesome"]
|
||||||
path = src/pretixbase/static/bootstrap
|
path = src/pretix/base/static/fontawesome
|
||||||
url = https://github.com/twbs/bootstrap.git
|
|
||||||
[submodule "src/pretixbase/static/fontawesome"]
|
|
||||||
path = src/pretixbase/static/fontawesome
|
|
||||||
url = https://github.com/FortAwesome/Font-Awesome.git
|
url = https://github.com/FortAwesome/Font-Awesome.git
|
||||||
|
[submodule "src/pretix/base/static/bootstrap"]
|
||||||
|
path = src/pretix/base/static/bootstrap
|
||||||
|
url = https://github.com/twbs/bootstrap.git
|
||||||
|
|||||||
@@ -23,23 +23,7 @@ on the next pages.
|
|||||||
Creating a plugin
|
Creating a plugin
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
To create a new plugin, create a new python package as a subpackage to ``pretixplugins``.
|
To create a new plugin, create a new python package.
|
||||||
In order to do so, you can place your module into pretix's :file:`pretixplugins` folder *or
|
|
||||||
anywhere else in your python import path* inside a folder called ``pretixplugins``.
|
|
||||||
|
|
||||||
.. IMPORTANT::
|
|
||||||
This makes use of a design pattern called `namespace packages`_ which is only
|
|
||||||
implicitly available as of Python 3.4. As we aim to support Python 3.2 for a bit
|
|
||||||
longer, you **MUST** put **EXACLTY** the following content into ``pretixplugins/__init__.py``
|
|
||||||
if you create a new ``pretixplugins`` folder somewhere in your path::
|
|
||||||
|
|
||||||
from pkgutil import extend_path
|
|
||||||
__path__ = extend_path(__path__, __name__)
|
|
||||||
|
|
||||||
Otherwise it **will break** on Python 3.2 systems *depending on the python path's order*,
|
|
||||||
which is not tolerable behaviour. Also, even on Python 3.4 the test runner seems to have
|
|
||||||
problems without this workaround.
|
|
||||||
|
|
||||||
|
|
||||||
Inside your newly created folder, you'll probably need the three python modules ``__init__.py``,
|
Inside your newly created folder, you'll probably need the three python modules ``__init__.py``,
|
||||||
``models.py`` and ``signals.py``, although this is up to you. You can take the following
|
``models.py`` and ``signals.py``, although this is up to you. You can take the following
|
||||||
@@ -48,14 +32,14 @@ example, taken from the time restriction module (see next chapter) as a template
|
|||||||
|
|
||||||
from django.apps import AppConfig
|
from django.apps import AppConfig
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from pretixbase.plugins import PluginType
|
from pretix.base.plugins import PluginType
|
||||||
|
|
||||||
|
|
||||||
class TimeRestrictionApp(AppConfig):
|
class TimeRestrictionApp(AppConfig):
|
||||||
name = 'pretixplugins.timerestriction'
|
name = 'pretix.plugins.timerestriction'
|
||||||
verbose_name = _("Time restriction")
|
verbose_name = _("Time restriction")
|
||||||
|
|
||||||
class TixlPluginMeta:
|
class PretixPluginMeta:
|
||||||
type = PluginType.RESTRICTION
|
type = PluginType.RESTRICTION
|
||||||
name = _("Restriciton by time")
|
name = _("Restriciton by time")
|
||||||
author = _("the pretix team")
|
author = _("the pretix team")
|
||||||
@@ -67,10 +51,10 @@ example, taken from the time restriction module (see next chapter) as a template
|
|||||||
def ready(self):
|
def ready(self):
|
||||||
from . import signals # NOQA
|
from . import signals # NOQA
|
||||||
|
|
||||||
default_app_config = 'pretixplugins.timerestriction.TimeRestrictionApp'
|
default_app_config = 'pretix.plugins.timerestriction.TimeRestrictionApp'
|
||||||
|
|
||||||
.. IMPORTANT::
|
.. IMPORTANT::
|
||||||
You have to implement a ``TixlPluginMeta`` class like in the example to make your
|
You have to implement a ``PretixPluginMeta`` class like in the example to make your
|
||||||
plugin available to the users.
|
plugin available to the users.
|
||||||
|
|
||||||
.. _signal dispatcher: https://docs.djangoproject.com/en/1.7/topics/signals/
|
.. _signal dispatcher: https://docs.djangoproject.com/en/1.7/topics/signals/
|
||||||
|
|||||||
@@ -15,14 +15,14 @@ The restriction model
|
|||||||
|
|
||||||
It is very likely that your new restriction plugin needs to store data. In order to do
|
It is very likely that your new restriction plugin needs to store data. In order to do
|
||||||
so, it should define its own model with a name related to what your restriction does,
|
so, it should define its own model with a name related to what your restriction does,
|
||||||
e.g. ``TimeRestriction``. This model should be a child class of ``pretixbase.models.BaseRestriction``.
|
e.g. ``TimeRestriction``. This model should be a child class of ``pretix.base.models.BaseRestriction``.
|
||||||
You do not need to define custom fields, but you should create at least an empty model.
|
You do not need to define custom fields, but you should create at least an empty model.
|
||||||
In our example, we put the following into :file:`pretixplugins/timerestriction/models.py`::
|
In our example, we put the following into :file:`pretix/plugins/timerestriction/models.py`::
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from pretixbase.models import BaseRestriction
|
from pretix.base.models import BaseRestriction
|
||||||
|
|
||||||
|
|
||||||
class TimeRestriction(BaseRestriction):
|
class TimeRestriction(BaseRestriction):
|
||||||
@@ -52,14 +52,14 @@ Availability determination
|
|||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
This is the one signal *every* restriction plugin has to listen for, as your plugin does not
|
This is the one signal *every* restriction plugin has to listen for, as your plugin does not
|
||||||
restrict anything without doing so. It is available as ``pretixbase.signals.determine_availability``
|
restrict anything without doing so. It is available as ``pretix.base.signals.determine_availability``
|
||||||
and is sent out every time some component of pretix wants to know whether a specific item or
|
and is sent out every time some component of pretix wants to know whether a specific item or
|
||||||
variation is available for sell.
|
variation is available for sell.
|
||||||
|
|
||||||
It is sent out with several keyword arguments:
|
It is sent out with several keyword arguments:
|
||||||
|
|
||||||
``item``
|
``item``
|
||||||
The instance of ``pretixbase.models.Item`` in question.
|
The instance of ``pretix.base.models.Item`` in question.
|
||||||
``variations``
|
``variations``
|
||||||
A list of dictionaries in the same format as ``Item.get_all_variations``:
|
A list of dictionaries in the same format as ``Item.get_all_variations``:
|
||||||
The list contains one dictionary per variation, where the ``Property`` IDs are
|
The list contains one dictionary per variation, where the ``Property`` IDs are
|
||||||
@@ -68,7 +68,7 @@ It is sent out with several keyword arguments:
|
|||||||
the item does not have any properties, the list will contain exactly one empty
|
the item does not have any properties, the list will contain exactly one empty
|
||||||
dictionary. Please note: this is *not* the list of all possible variations, this is
|
dictionary. Please note: this is *not* the list of all possible variations, this is
|
||||||
only the list of all variations the frontend likes to determine the status for.
|
only the list of all variations the frontend likes to determine the status for.
|
||||||
Technically, you won't get ``dict`` objects but ``pretixbase.types.VariationDict``
|
Technically, you won't get ``dict`` objects but ``pretix.base.types.VariationDict``
|
||||||
objects, which behave exactly the same but add some extra methods.
|
objects, which behave exactly the same but add some extra methods.
|
||||||
``context``
|
``context``
|
||||||
A yet-to-be-defined context object containing information about the user and the order
|
A yet-to-be-defined context object containing information about the user and the order
|
||||||
@@ -103,7 +103,7 @@ In our example, the implementation could look like this::
|
|||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
from django.utils.timezone import now
|
from django.utils.timezone import now
|
||||||
|
|
||||||
from pretixbase.signals import determine_availability
|
from pretix.base.signals import determine_availability
|
||||||
|
|
||||||
from .models import TimeRestriction
|
from .models import TimeRestriction
|
||||||
|
|
||||||
@@ -217,12 +217,12 @@ Control interface formsets
|
|||||||
To make it possible for the event organizer to configure your restriction, there is a
|
To make it possible for the event organizer to configure your restriction, there is a
|
||||||
'Restrictions' page in the item configuration. This page is able to show a formset for
|
'Restrictions' page in the item configuration. This page is able to show a formset for
|
||||||
each restriction plugin, but *you* are required to create this formset. This is why you
|
each restriction plugin, but *you* are required to create this formset. This is why you
|
||||||
should listen to the the ``pretixcontrol.signals.restriction_formset`` signal.
|
should listen to the the ``pretix.control.signals.restriction_formset`` signal.
|
||||||
|
|
||||||
Currently, the signal comes with only one keyword argument:
|
Currently, the signal comes with only one keyword argument:
|
||||||
|
|
||||||
``item``
|
``item``
|
||||||
The instance of ``pretixbase.models.Item`` we want a formset for.
|
The instance of ``pretix.base.models.Item`` we want a formset for.
|
||||||
|
|
||||||
You are expected to return a dict containing the following items:
|
You are expected to return a dict containing the following items:
|
||||||
|
|
||||||
@@ -245,9 +245,9 @@ Our time restriction example looks like this::
|
|||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
from django.forms.models import inlineformset_factory
|
from django.forms.models import inlineformset_factory
|
||||||
|
|
||||||
from pretixcontrol.signals import restriction_formset
|
from pretix.control.signals import restriction_formset
|
||||||
from pretixbase.models import Item
|
from pretix.base.models import Item
|
||||||
from pretixcontrol.views.forms import (
|
from pretix.control.views.forms import (
|
||||||
VariationsField, RestrictionInlineFormset, RestrictionForm
|
VariationsField, RestrictionInlineFormset, RestrictionForm
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -9,26 +9,26 @@ The components
|
|||||||
|
|
||||||
The project pretix is split into several components. The main three of them are:
|
The project pretix is split into several components. The main three of them are:
|
||||||
|
|
||||||
**pretixbase**
|
**pretix.base**
|
||||||
Tixlbase is the foundation below all other components. It is primarily
|
Pretixbase is the foundation below all other components. It is primarily
|
||||||
responsible for the data structures and database communication. It also hosts
|
responsible for the data structures and database communication. It also hosts
|
||||||
several utilities which are used by multiple other components.
|
several utilities which are used by multiple other components.
|
||||||
|
|
||||||
**pretixcontrol**
|
**pretix.control**
|
||||||
Tixlcontrol is the web-based backend software which allows organizers to
|
Pretixcontrol is the web-based backend software which allows organizers to
|
||||||
create and manage their events, items, orders and tickets.
|
create and manage their events, items, orders and tickets.
|
||||||
|
|
||||||
**pretixpresale**
|
**pretix.presale**
|
||||||
Tixlpresale is the ticket-shop itself, containing all the parts visible to the
|
Pretixpresale is the ticket-shop itself, containing all the parts visible to the
|
||||||
end user.
|
end user.
|
||||||
|
|
||||||
Users and events
|
Users and events
|
||||||
^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
Tixl is all about **events**, which are defined as something happening somewhere.
|
Pretix is all about **events**, which are defined as something happening somewhere.
|
||||||
Every Event is managed by the **organizer**, an abstract entity running the event.
|
Every Event is managed by the **organizer**, an abstract entity running the event.
|
||||||
|
|
||||||
Tixl is used by **users**. We want to enable global users who can just login into
|
Pretix is used by **users**. We want to enable global users who can just login into
|
||||||
pretix and buy tickets for as many events as they like but at the same time it
|
pretix and buy tickets for as many events as they like but at the same time it
|
||||||
should be possible to create some kind of local user to have a temporary account
|
should be possible to create some kind of local user to have a temporary account
|
||||||
just to buy tickets for one single event.
|
just to buy tickets for one single event.
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
Development goals
|
Development goals
|
||||||
=================
|
=================
|
||||||
|
|
||||||
Tixl is a web software handling presale of event tickets.
|
Pretix is a web software handling presale of event tickets.
|
||||||
|
|
||||||
Technical goals
|
Technical goals
|
||||||
---------------
|
---------------
|
||||||
@@ -19,7 +19,7 @@ Feature goals
|
|||||||
|
|
||||||
* One pretix software installation has to cope with multiple events by multiple organizers
|
* One pretix software installation has to cope with multiple events by multiple organizers
|
||||||
* There is no code access necessary to create a new event
|
* There is no code access necessary to create a new event
|
||||||
* Tixl is abstract in many ways to adopt to as much events as possible.
|
*Tixe is abstract in many ways to adopt to as much events as possible.
|
||||||
|
|
||||||
* Tickets are only an instance of an abstract model called items, such that the system can also sell e.g. merchandise
|
* Tickets are only an instance of an abstract model called items, such that the system can also sell e.g. merchandise
|
||||||
* An abstract concept of restriction is used to restrict the selling of tickets, for example by date, by number or by user permissions.
|
* An abstract concept of restriction is used to restrict the selling of tickets, for example by date, by number or by user permissions.
|
||||||
|
|||||||
@@ -7,22 +7,22 @@ Python source code
|
|||||||
All the source code lives in ``src/``, which has several subdirectories.
|
All the source code lives in ``src/``, which has several subdirectories.
|
||||||
|
|
||||||
pretix/
|
pretix/
|
||||||
This directory contains the basic Django settings and URL routing.
|
This directory contains nearly all source code.
|
||||||
|
|
||||||
pretixbase/
|
base/
|
||||||
This is the django app containing all the models and methods which are
|
This is the django app containing all the models and methods which are
|
||||||
essential to all of pretix's features.
|
essential to all of pretix's features.
|
||||||
|
|
||||||
pretixcontrol/
|
control/
|
||||||
This is the django app containing the frontend for organizers.
|
This is the django app containing the frontend for organizers.
|
||||||
|
|
||||||
pretixpresale/
|
presale/
|
||||||
This is the django app containing the frontend for users buying tickets.
|
This is the django app containing the frontend for users buying tickets.
|
||||||
|
|
||||||
helpers/
|
helpers/
|
||||||
Helpers contain a very few modules providing workarounds for low-level flaws in
|
Helpers contain a very few modules providing workarounds for low-level flaws in
|
||||||
Django or installed 3rd-party packages, like a filter to combine the ``lessc``
|
Django or installed 3rd-party packages, like a filter to combine the ``lessc``
|
||||||
preprocessor with ``django-compressor``'s URL rewriting.
|
preprocessor with ``django-compressor``'s URL rewriting.
|
||||||
|
|
||||||
Language files
|
Language files
|
||||||
--------------
|
--------------
|
||||||
@@ -37,21 +37,25 @@ LESS source code
|
|||||||
We use less as a preprocessor for CSS. Our own less code is built in the same
|
We use less as a preprocessor for CSS. Our own less code is built in the same
|
||||||
step as Bootstrap and FontAwesome, so their mixins etc. are fully available.
|
step as Bootstrap and FontAwesome, so their mixins etc. are fully available.
|
||||||
|
|
||||||
pretixcontrol
|
pretix.control
|
||||||
pretixcontrol has two main LESS files, ``pretixcontrol/static/pretixcontrol/less/main.less`` and
|
pretixcontrol has two main LESS files, ``pretix/control/static/pretixcontrol/less/main.less`` and
|
||||||
``pretixcontrol/static/pretixcontrol/less/auth.less``, importing everything else.
|
``pretix/control/static/pretixcontrol/less/auth.less``, importing everything else.
|
||||||
|
|
||||||
|
pretix.presale
|
||||||
|
pretixpresale has one main LESS files, ``pretix/control/static/pretix/presale/less/main.less``,
|
||||||
|
importing everything else.
|
||||||
|
|
||||||
3rd-party assets
|
3rd-party assets
|
||||||
^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
Bootstrap
|
Bootstrap
|
||||||
Bootstrap lives as a git submodule at ``pretixbase/static/bootstrap/``
|
Bootstrap lives as a git submodule at ``pretix/base/static/bootstrap/``
|
||||||
|
|
||||||
Font Awesome
|
Font Awesome
|
||||||
Font Awesome lives as a git submodule at ``pretixbase/static/fontawesome/``
|
Font Awesome lives as a git submodule at ``pretix/base/static/fontawesome/``
|
||||||
|
|
||||||
jQuery
|
jQuery
|
||||||
jQuery lives as a single JavaScript file in ``pretixbase/static/jquery/js/``
|
jQuery lives as a single JavaScript file in ``pretix/base/static/jquery/js/``
|
||||||
|
|
||||||
jQuery plugin: Django formsets
|
jQuery plugin: Django formsets
|
||||||
Our own modified version of `django-formset-js`_ is available as an independent
|
Our own modified version of `django-formset-js`_ is available as an independent
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
[run]
|
[run]
|
||||||
source = pretixbase,pretixcontrol,pretixpresale,pretixplugins
|
source = pretix
|
||||||
omit = */migrations/*,*/urls.py,*/tests/*,*/testdummy/*,*/admin.py
|
omit = */migrations/*,*/urls.py,*/tests/*,*/testdummy/*,*/admin.py
|
||||||
|
|
||||||
[report]
|
[report]
|
||||||
|
|||||||
8
src/pretix/base/__init__.py
Normal file
8
src/pretix/base/__init__.py
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class PretixBaseConfig(AppConfig):
|
||||||
|
name = 'pretix.base'
|
||||||
|
label = 'pretixbase'
|
||||||
|
|
||||||
|
default_app_config = 'pretix.base.PretixBaseConfig'
|
||||||
@@ -3,13 +3,13 @@ from django.contrib.auth.admin import UserAdmin
|
|||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
from django import forms
|
from django import forms
|
||||||
|
|
||||||
from pretixbase.models import (
|
from pretix.base.models import (
|
||||||
User, Organizer, OrganizerPermission, Event, EventPermission,
|
User, Organizer, OrganizerPermission, Event, EventPermission,
|
||||||
Property, PropertyValue, Item, ItemVariation, ItemCategory
|
Property, PropertyValue, Item, ItemVariation, ItemCategory
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class TixlUserCreationForm(forms.ModelForm):
|
class PretixUserCreationForm(forms.ModelForm):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
A form that creates a user, with no privileges, from the given username and
|
A form that creates a user, with no privileges, from the given username and
|
||||||
@@ -39,14 +39,14 @@ class TixlUserCreationForm(forms.ModelForm):
|
|||||||
return password2
|
return password2
|
||||||
|
|
||||||
def save(self, commit=True):
|
def save(self, commit=True):
|
||||||
user = super(TixlUserCreationForm, self).save(commit=False)
|
user = super(PretixUserCreationForm, self).save(commit=False)
|
||||||
user.set_password(self.cleaned_data["password1"])
|
user.set_password(self.cleaned_data["password1"])
|
||||||
if commit:
|
if commit:
|
||||||
user.save()
|
user.save()
|
||||||
return user
|
return user
|
||||||
|
|
||||||
|
|
||||||
class TixlUserAdmin(UserAdmin):
|
class PretixUserAdmin(UserAdmin):
|
||||||
|
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
(None, {'fields': ('identifier', 'event', 'username', 'password')}),
|
(None, {'fields': ('identifier', 'event', 'username', 'password')}),
|
||||||
@@ -59,7 +59,7 @@ class TixlUserAdmin(UserAdmin):
|
|||||||
search_fields = ('identifier', 'username', 'givenname', 'familyname', 'email')
|
search_fields = ('identifier', 'username', 'givenname', 'familyname', 'email')
|
||||||
ordering = ('identifier',)
|
ordering = ('identifier',)
|
||||||
list_filter = ('is_staff', 'is_active', 'groups')
|
list_filter = ('is_staff', 'is_active', 'groups')
|
||||||
add_form = TixlUserCreationForm
|
add_form = PretixUserCreationForm
|
||||||
|
|
||||||
|
|
||||||
class OrganizerPermissionInline(admin.TabularInline):
|
class OrganizerPermissionInline(admin.TabularInline):
|
||||||
@@ -126,7 +126,7 @@ class ItemAdmin(admin.ModelAdmin):
|
|||||||
search_fields = ('name', 'event', 'category', 'short_description')
|
search_fields = ('name', 'event', 'category', 'short_description')
|
||||||
|
|
||||||
|
|
||||||
admin.site.register(User, TixlUserAdmin)
|
admin.site.register(User, PretixUserAdmin)
|
||||||
admin.site.register(Organizer, OrganizerAdmin)
|
admin.site.register(Organizer, OrganizerAdmin)
|
||||||
admin.site.register(Event, EventAdmin)
|
admin.site.register(Event, EventAdmin)
|
||||||
admin.site.register(Property, PropertyAdmin)
|
admin.site.register(Property, PropertyAdmin)
|
||||||
@@ -3,7 +3,7 @@ import hashlib
|
|||||||
|
|
||||||
from django.core.cache import caches
|
from django.core.cache import caches
|
||||||
|
|
||||||
from pretixbase.models import Event
|
from pretix.base.models import Event
|
||||||
|
|
||||||
|
|
||||||
class EventRelatedCache:
|
class EventRelatedCache:
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
import pytz
|
import pytz
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.urlresolvers import resolve
|
|
||||||
from django.middleware.locale import LocaleMiddleware as BaseLocaleMiddleware
|
from django.middleware.locale import LocaleMiddleware as BaseLocaleMiddleware
|
||||||
from django.utils.translation.trans_real import (
|
from django.utils.translation.trans_real import (
|
||||||
get_supported_language_variant,
|
get_supported_language_variant,
|
||||||
@@ -14,7 +13,6 @@ from django.utils import translation, timezone
|
|||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from django.utils.cache import patch_vary_headers
|
from django.utils.cache import patch_vary_headers
|
||||||
|
|
||||||
from pretixbase.models import Event
|
|
||||||
|
|
||||||
_supported = None
|
_supported = None
|
||||||
|
|
||||||
@@ -7,7 +7,7 @@ import django.core.validators
|
|||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
import django.db.models.deletion
|
import django.db.models.deletion
|
||||||
import django.utils.timezone
|
import django.utils.timezone
|
||||||
import pretixbase.models
|
import pretix.base.models
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
@@ -322,7 +322,8 @@ class Migration(migrations.Migration):
|
|||||||
('items', versions.models.VersionedManyToManyField(blank=True, related_name='quotas', to='pretixbase.Item', verbose_name='Item')),
|
('items', versions.models.VersionedManyToManyField(blank=True, related_name='quotas', to='pretixbase.Item', verbose_name='Item')),
|
||||||
('lock_cache', models.ManyToManyField(blank=True, to='pretixbase.CartPosition')),
|
('lock_cache', models.ManyToManyField(blank=True, to='pretixbase.CartPosition')),
|
||||||
('order_cache', models.ManyToManyField(blank=True, to='pretixbase.OrderPosition')),
|
('order_cache', models.ManyToManyField(blank=True, to='pretixbase.OrderPosition')),
|
||||||
('variations', pretixbase.models.VariationsField(blank=True, related_name='quotas', to='pretixbase.ItemVariation', verbose_name='Variations')),
|
('variations', pretix.base.models.VariationsField(blank=True, related_name='quotas',
|
||||||
|
to='pretixbase.ItemVariation', verbose_name='Variations')),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'verbose_name_plural': 'Quotas',
|
'verbose_name_plural': 'Quotas',
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.db import models, migrations
|
from django.db import models, migrations
|
||||||
import pretixbase.models
|
import pretix.base.models
|
||||||
import versions.models
|
import versions.models
|
||||||
|
|
||||||
|
|
||||||
@@ -3,7 +3,7 @@ from __future__ import unicode_literals
|
|||||||
|
|
||||||
from django.db import models, migrations
|
from django.db import models, migrations
|
||||||
import versions.models
|
import versions.models
|
||||||
import pretixbase.models
|
import pretix.base.models
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
@@ -5,7 +5,7 @@ from django.db import models, migrations
|
|||||||
from django.utils.timezone import utc
|
from django.utils.timezone import utc
|
||||||
import versions.models
|
import versions.models
|
||||||
import datetime
|
import datetime
|
||||||
import pretixbase.models
|
import pretix.base.models
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.db import models, migrations
|
from django.db import models, migrations
|
||||||
import pretixbase.models
|
import pretix.base.models
|
||||||
import versions.models
|
import versions.models
|
||||||
|
|
||||||
|
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.db import models, migrations
|
from django.db import models, migrations
|
||||||
import pretixbase.models
|
import pretix.base.models
|
||||||
import versions.models
|
import versions.models
|
||||||
|
|
||||||
|
|
||||||
@@ -3,7 +3,7 @@ from __future__ import unicode_literals
|
|||||||
|
|
||||||
from django.db import models, migrations
|
from django.db import models, migrations
|
||||||
import versions.models
|
import versions.models
|
||||||
import pretixbase.models
|
import pretix.base.models
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import models, migrations
|
||||||
|
import versions.models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('pretixbase', '0008_quota_locked'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='EventSetting',
|
||||||
|
fields=[
|
||||||
|
('id', models.CharField(primary_key=True, serialize=False, max_length=36)),
|
||||||
|
('identity', models.CharField(max_length=36)),
|
||||||
|
('version_start_date', models.DateTimeField()),
|
||||||
|
('version_end_date', models.DateTimeField(blank=True, default=None, null=True)),
|
||||||
|
('version_birth_date', models.DateTimeField()),
|
||||||
|
('key', models.CharField(max_length=255)),
|
||||||
|
('value', models.TextField()),
|
||||||
|
('event', versions.models.VersionedForeignKey(related_name='setting_objects', to='pretixbase.Event')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
bases=(models.Model,),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='OrganizerSetting',
|
||||||
|
fields=[
|
||||||
|
('id', models.CharField(primary_key=True, serialize=False, max_length=36)),
|
||||||
|
('identity', models.CharField(max_length=36)),
|
||||||
|
('version_start_date', models.DateTimeField()),
|
||||||
|
('version_end_date', models.DateTimeField(blank=True, default=None, null=True)),
|
||||||
|
('version_birth_date', models.DateTimeField()),
|
||||||
|
('key', models.CharField(max_length=255)),
|
||||||
|
('value', models.TextField()),
|
||||||
|
('organizer', versions.models.VersionedForeignKey(related_name='setting_objects', to='pretixbase.Organizer')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
bases=(models.Model,),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -7,6 +7,7 @@ from django.db import models
|
|||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, PermissionsMixin
|
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, PermissionsMixin
|
||||||
from django.db.models import Q, Count
|
from django.db.models import Q, Count
|
||||||
|
from django.utils.functional import cached_property
|
||||||
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 django.template.defaultfilters import date as _date
|
from django.template.defaultfilters import date as _date
|
||||||
@@ -227,6 +228,58 @@ class Organizer(Versionable):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
class OrganizerSettingsProxy:
|
||||||
|
"""
|
||||||
|
This objects allows convenient access to settings stored in the
|
||||||
|
OrganizerSettings database model. It exposes all settings as properties
|
||||||
|
and it will do all the nasty defaults stuff for
|
||||||
|
you. It will return None for non-existing properties.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, organizer):
|
||||||
|
self._organizer = organizer
|
||||||
|
self._cached_obj = None
|
||||||
|
|
||||||
|
def _cache(self):
|
||||||
|
if self._cached_obj is None:
|
||||||
|
self._cached_obj = {}
|
||||||
|
for setting in self._organizer.setting_objects.current.all():
|
||||||
|
self._cached_obj[setting.key] = setting
|
||||||
|
return self._cached_obj
|
||||||
|
|
||||||
|
def __getattr__(self, key):
|
||||||
|
if key in self._cache():
|
||||||
|
return self._cache()[key].value
|
||||||
|
if key in OrganizerSetting.DEFAULTS:
|
||||||
|
return OrganizerSetting.DEFAULTS[key]
|
||||||
|
return None
|
||||||
|
|
||||||
|
def __delattr__(self, key):
|
||||||
|
if key.startswith('_'):
|
||||||
|
return super().__delattr__(key)
|
||||||
|
if key in self._cache():
|
||||||
|
self._cache()[key].delete()
|
||||||
|
del self._cache()[key]
|
||||||
|
|
||||||
|
def __setattr__(self, key, value):
|
||||||
|
if key.startswith('_'):
|
||||||
|
return super().__setattr__(key, value)
|
||||||
|
if key in self._cache():
|
||||||
|
s = self._cache()[key]
|
||||||
|
s = s.clone()
|
||||||
|
else:
|
||||||
|
s = OrganizerSetting(organizer=self._organizer, key=key)
|
||||||
|
s.value = value
|
||||||
|
s.save()
|
||||||
|
self._cache()[key] = s
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def settings(self):
|
||||||
|
"""
|
||||||
|
Returns an object representing this organizer's settings
|
||||||
|
"""
|
||||||
|
return Organizer.OrganizerSettingsProxy(self)
|
||||||
|
|
||||||
|
|
||||||
class OrganizerPermission(Versionable):
|
class OrganizerPermission(Versionable):
|
||||||
"""
|
"""
|
||||||
@@ -382,10 +435,60 @@ class Event(Versionable):
|
|||||||
"DATETIME_FORMAT" if self.show_times else "DATE_FORMAT"
|
"DATETIME_FORMAT" if self.show_times else "DATE_FORMAT"
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_cache(self) -> "pretixbase.cache.EventRelatedCache":
|
def get_cache(self) -> "pretix.base.cache.EventRelatedCache":
|
||||||
from pretixbase.cache import EventRelatedCache
|
from pretix.base.cache import EventRelatedCache
|
||||||
return EventRelatedCache(self)
|
return EventRelatedCache(self)
|
||||||
|
|
||||||
|
class EventSettingsProxy:
|
||||||
|
"""
|
||||||
|
This objects allows convenient access to settings stored in the
|
||||||
|
EventSettings database model. It exposes all settings as properties
|
||||||
|
and it will do all the nasty inheritance and defaults stuff for
|
||||||
|
you. It will return None for non-existing properties.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, event):
|
||||||
|
self._event = event
|
||||||
|
self._cached_obj = None
|
||||||
|
|
||||||
|
def _cache(self):
|
||||||
|
if self._cached_obj is None:
|
||||||
|
self._cached_obj = {}
|
||||||
|
for setting in self._event.setting_objects.current.all():
|
||||||
|
self._cached_obj[setting.key] = setting
|
||||||
|
return self._cached_obj
|
||||||
|
|
||||||
|
def __getattr__(self, key):
|
||||||
|
if key in self._cache():
|
||||||
|
return self._cache()[key].value
|
||||||
|
return getattr(self._event.organizer.settings, key)
|
||||||
|
|
||||||
|
def __setattr__(self, key, value):
|
||||||
|
if key.startswith('_'):
|
||||||
|
return super().__setattr__(key, value)
|
||||||
|
if key in self._cache():
|
||||||
|
s = self._cache()[key]
|
||||||
|
s = s.clone()
|
||||||
|
else:
|
||||||
|
s = EventSetting(event=self._event, key=key)
|
||||||
|
s.value = value
|
||||||
|
s.save()
|
||||||
|
self._cache()[key] = s
|
||||||
|
|
||||||
|
def __delattr__(self, key):
|
||||||
|
if key.startswith('_'):
|
||||||
|
return super().__delattr__(key)
|
||||||
|
if key in self._cache():
|
||||||
|
self._cache()[key].delete()
|
||||||
|
del self._cache()[key]
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def settings(self):
|
||||||
|
"""
|
||||||
|
Returns an object representing this event's settings
|
||||||
|
"""
|
||||||
|
return Event.EventSettingsProxy(self)
|
||||||
|
|
||||||
|
|
||||||
class EventPermission(Versionable):
|
class EventPermission(Versionable):
|
||||||
"""
|
"""
|
||||||
@@ -745,14 +848,27 @@ class Item(Versionable):
|
|||||||
return self._get_all_available_variations_cache
|
return self._get_all_available_variations_cache
|
||||||
|
|
||||||
from .signals import determine_availability
|
from .signals import determine_availability
|
||||||
if self.properties.count() == 0:
|
|
||||||
|
propids = set([p.identity for p in self.properties.all()])
|
||||||
|
if len(propids) == 0:
|
||||||
variations = [VariationDict()]
|
variations = [VariationDict()]
|
||||||
else:
|
else:
|
||||||
all_variations = list(self.variations.annotate(qc=Count('quotas')).filter(qc__gt=0))
|
all_variations = list(
|
||||||
|
self.variations.annotate(
|
||||||
|
qc=Count('quotas')
|
||||||
|
).filter(qc__gt=0).prefetch_related(
|
||||||
|
"values", "values__prop"
|
||||||
|
)
|
||||||
|
)
|
||||||
variations = []
|
variations = []
|
||||||
for var in all_variations:
|
for var in all_variations:
|
||||||
|
values = list(var.values.all())
|
||||||
|
# Make sure we don't expose stale ItemVariation objects which are
|
||||||
|
# still around altough they have an old set of properties
|
||||||
|
if set([v.prop.identity for v in values]) != propids:
|
||||||
|
continue
|
||||||
vardict = VariationDict()
|
vardict = VariationDict()
|
||||||
for v in var.values.all():
|
for v in values:
|
||||||
vardict[v.prop.identity] = v
|
vardict[v.prop.identity] = v
|
||||||
vardict['variation'] = var
|
vardict['variation'] = var
|
||||||
variations.append(vardict)
|
variations.append(vardict)
|
||||||
@@ -782,7 +898,7 @@ class Item(Versionable):
|
|||||||
self._get_all_available_variations_cache = variations
|
self._get_all_available_variations_cache = variations
|
||||||
return variations
|
return variations
|
||||||
|
|
||||||
def availability(self):
|
def check_quotas(self):
|
||||||
"""
|
"""
|
||||||
This method is used to determine whether this Item is currently available
|
This method is used to determine whether this Item is currently available
|
||||||
for sale. It may return any of the return codes of Quota.availability()
|
for sale. It may return any of the return codes of Quota.availability()
|
||||||
@@ -792,7 +908,7 @@ class Item(Versionable):
|
|||||||
'but call this on their ItemVariation objects')
|
'but call this on their ItemVariation objects')
|
||||||
return min([q.availability() for q in self.quotas.all()])
|
return min([q.availability() for q in self.quotas.all()])
|
||||||
|
|
||||||
def execute_restrictions(self):
|
def check_restrictions(self):
|
||||||
"""
|
"""
|
||||||
This method is used to determine whether this ItemVariation is restricted
|
This method is used to determine whether this ItemVariation is restricted
|
||||||
in sale by any restriction plugins.
|
in sale by any restriction plugins.
|
||||||
@@ -859,6 +975,9 @@ class ItemVariation(Versionable):
|
|||||||
verbose_name = _("Item variation")
|
verbose_name = _("Item variation")
|
||||||
verbose_name_plural = _("Item variations")
|
verbose_name_plural = _("Item variations")
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return str(self.to_variation_dict())
|
||||||
|
|
||||||
def delete(self, *args, **kwargs):
|
def delete(self, *args, **kwargs):
|
||||||
super().delete(*args, **kwargs)
|
super().delete(*args, **kwargs)
|
||||||
if self.item:
|
if self.item:
|
||||||
@@ -869,7 +988,7 @@ class ItemVariation(Versionable):
|
|||||||
if self.item:
|
if self.item:
|
||||||
self.item.event.get_cache().clear()
|
self.item.event.get_cache().clear()
|
||||||
|
|
||||||
def availability(self):
|
def check_quotas(self):
|
||||||
"""
|
"""
|
||||||
This method is used to determine whether this ItemVariation is currently
|
This method is used to determine whether this ItemVariation is currently
|
||||||
available for sale in terms of quotas. It may return any of the return codes
|
available for sale in terms of quotas. It may return any of the return codes
|
||||||
@@ -884,7 +1003,7 @@ class ItemVariation(Versionable):
|
|||||||
vd['variation'] = self
|
vd['variation'] = self
|
||||||
return vd
|
return vd
|
||||||
|
|
||||||
def execute_restrictions(self):
|
def check_restrictions(self):
|
||||||
"""
|
"""
|
||||||
This method is used to determine whether this ItemVariation is restricted
|
This method is used to determine whether this ItemVariation is restricted
|
||||||
in sale by any restriction plugins.
|
in sale by any restriction plugins.
|
||||||
@@ -913,7 +1032,7 @@ class VariationsField(VersionedManyToManyField):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def formfield(self, **kwargs):
|
def formfield(self, **kwargs):
|
||||||
from pretixcontrol.views.forms import VariationsField as FVariationsField
|
from pretix.control.views.forms import VariationsField as FVariationsField
|
||||||
from django.db.models.fields.related import RelatedField
|
from django.db.models.fields.related import RelatedField
|
||||||
defaults = {
|
defaults = {
|
||||||
'form_class': FVariationsField,
|
'form_class': FVariationsField,
|
||||||
@@ -1086,14 +1205,14 @@ class Quota(Versionable):
|
|||||||
Q(variation__quotas__in=[self])
|
Q(variation__quotas__in=[self])
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
paid_orders = OrderPosition.objects.filter(
|
paid_orders = OrderPosition.objects.current.filter(
|
||||||
Q(order__status=Order.STATUS_PAID)
|
Q(order__status=Order.STATUS_PAID)
|
||||||
& quotalookup
|
& quotalookup
|
||||||
).count()
|
).count()
|
||||||
if paid_orders >= self.size:
|
if paid_orders >= self.size:
|
||||||
return Quota.AVAILABILITY_GONE, 0
|
return Quota.AVAILABILITY_GONE, 0
|
||||||
|
|
||||||
pending_valid_orders = OrderPosition.objects.filter(
|
pending_valid_orders = OrderPosition.objects.current.filter(
|
||||||
Q(order__status=Order.STATUS_PENDING)
|
Q(order__status=Order.STATUS_PENDING)
|
||||||
& Q(order__expires__gte=now())
|
& Q(order__expires__gte=now())
|
||||||
& quotalookup
|
& quotalookup
|
||||||
@@ -1101,7 +1220,7 @@ class Quota(Versionable):
|
|||||||
if (paid_orders + pending_valid_orders) >= self.size:
|
if (paid_orders + pending_valid_orders) >= self.size:
|
||||||
return Quota.AVAILABILITY_ORDERED, 0
|
return Quota.AVAILABILITY_ORDERED, 0
|
||||||
|
|
||||||
valid_cart_positions = CartPosition.objects.filter(
|
valid_cart_positions = CartPosition.objects.current.filter(
|
||||||
Q(expires__gte=now())
|
Q(expires__gte=now())
|
||||||
& quotalookup
|
& quotalookup
|
||||||
).count()
|
).count()
|
||||||
@@ -1307,3 +1426,26 @@ class CartPosition(Versionable):
|
|||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _("Cart position")
|
verbose_name = _("Cart position")
|
||||||
verbose_name_plural = _("Cart positions")
|
verbose_name_plural = _("Cart positions")
|
||||||
|
|
||||||
|
|
||||||
|
class EventSetting(Versionable):
|
||||||
|
"""
|
||||||
|
An event settings is a key-value setting which can be set for a
|
||||||
|
specific event
|
||||||
|
"""
|
||||||
|
event = VersionedForeignKey(Event, related_name='setting_objects')
|
||||||
|
key = models.CharField(max_length=255)
|
||||||
|
value = models.TextField()
|
||||||
|
|
||||||
|
|
||||||
|
class OrganizerSetting(Versionable):
|
||||||
|
"""
|
||||||
|
An event option is a key-value setting which can be set for an
|
||||||
|
organizer. It will be inherited by the events of this organizer
|
||||||
|
"""
|
||||||
|
DEFAULTS = {
|
||||||
|
|
||||||
|
}
|
||||||
|
organizer = VersionedForeignKey(Organizer, related_name='setting_objects')
|
||||||
|
key = models.CharField(max_length=255)
|
||||||
|
value = models.TextField()
|
||||||
@@ -12,12 +12,12 @@ class PluginType(Enum):
|
|||||||
|
|
||||||
def get_all_plugins() -> "List[class]":
|
def get_all_plugins() -> "List[class]":
|
||||||
"""
|
"""
|
||||||
Returns the TixlPluginMeta classes of all plugins found in the installed Django apps.
|
Returns the PretixPluginMeta classes of all plugins found in the installed Django apps.
|
||||||
"""
|
"""
|
||||||
plugins = []
|
plugins = []
|
||||||
for app in apps.get_app_configs():
|
for app in apps.get_app_configs():
|
||||||
if hasattr(app, 'TixlPluginMeta'):
|
if hasattr(app, 'PretixPluginMeta'):
|
||||||
meta = app.TixlPluginMeta
|
meta = app.PretixPluginMeta
|
||||||
meta.module = app.name
|
meta.module = app.name
|
||||||
plugins.append(meta)
|
plugins.append(meta)
|
||||||
return plugins
|
return plugins
|
||||||
@@ -17,7 +17,7 @@ class EventPluginSignal(django.dispatch.Signal):
|
|||||||
Send signal from sender to all connected receivers that belong to
|
Send signal from sender to all connected receivers that belong to
|
||||||
plugins enabled for the given Event.
|
plugins enabled for the given Event.
|
||||||
|
|
||||||
sender is required to be an instance of ``pretixbase.models.Event``.
|
sender is required to be an instance of ``pretix.base.models.Event``.
|
||||||
"""
|
"""
|
||||||
assert isinstance(sender, Event)
|
assert isinstance(sender, Event)
|
||||||
|
|
||||||
1
src/pretix/base/static/bootstrap
Submodule
1
src/pretix/base/static/bootstrap
Submodule
Submodule src/pretix/base/static/bootstrap added at 0d7b85ece7
1
src/pretix/base/static/fontawesome
Submodule
1
src/pretix/base/static/fontawesome
Submodule
Submodule src/pretix/base/static/fontawesome added at c79474df99
@@ -2,7 +2,6 @@ import os
|
|||||||
import sys
|
import sys
|
||||||
from django.contrib.staticfiles.testing import StaticLiveServerTestCase
|
from django.contrib.staticfiles.testing import StaticLiveServerTestCase
|
||||||
|
|
||||||
from django.test import LiveServerTestCase
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from selenium import webdriver
|
from selenium import webdriver
|
||||||
|
|
||||||
@@ -4,7 +4,7 @@ from django.test import TestCase
|
|||||||
from django.core.cache import cache as django_cache
|
from django.core.cache import cache as django_cache
|
||||||
from django.utils.timezone import now
|
from django.utils.timezone import now
|
||||||
|
|
||||||
from pretixbase.models import Event, Organizer
|
from pretix.base.models import Event, Organizer
|
||||||
|
|
||||||
|
|
||||||
class CacheTest(TestCase):
|
class CacheTest(TestCase):
|
||||||
@@ -2,7 +2,7 @@ from django.test import TestCase, Client
|
|||||||
from django.utils.timezone import now
|
from django.utils.timezone import now
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
from pretixbase.models import Event, Organizer, User
|
from pretix.base.models import Event, Organizer, User
|
||||||
|
|
||||||
|
|
||||||
class LocaleDeterminationTest(TestCase):
|
class LocaleDeterminationTest(TestCase):
|
||||||
@@ -2,12 +2,12 @@ from datetime import timedelta
|
|||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.utils.timezone import now
|
from django.utils.timezone import now
|
||||||
|
|
||||||
from pretixbase.models import (
|
from pretix.base.models import (
|
||||||
Event, Organizer, Item, ItemVariation,
|
Event, Organizer, Item, ItemVariation,
|
||||||
Property, PropertyValue, User, Quota,
|
Property, PropertyValue, User, Quota,
|
||||||
Order, OrderPosition, CartPosition
|
Order, OrderPosition, CartPosition,
|
||||||
)
|
OrganizerSetting)
|
||||||
from pretixbase.types import VariationDict
|
from pretix.base.types import VariationDict
|
||||||
|
|
||||||
|
|
||||||
class ItemVariationsTest(TestCase):
|
class ItemVariationsTest(TestCase):
|
||||||
@@ -202,15 +202,15 @@ class QuotaTestCase(TestCase):
|
|||||||
|
|
||||||
def test_available(self):
|
def test_available(self):
|
||||||
self.quota.items.add(self.item1)
|
self.quota.items.add(self.item1)
|
||||||
self.assertEqual(self.item1.availability(), (Quota.AVAILABILITY_OK, 2))
|
self.assertEqual(self.item1.check_quotas(), (Quota.AVAILABILITY_OK, 2))
|
||||||
self.quota.items.add(self.item2)
|
self.quota.items.add(self.item2)
|
||||||
self.quota.variations.add(self.var1)
|
self.quota.variations.add(self.var1)
|
||||||
try:
|
try:
|
||||||
self.item2.availability()
|
self.item2.check_quotas()
|
||||||
self.assertTrue(False)
|
self.assertTrue(False)
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
self.assertEqual(self.var1.availability(), (Quota.AVAILABILITY_OK, 2))
|
self.assertEqual(self.var1.check_quotas(), (Quota.AVAILABILITY_OK, 2))
|
||||||
|
|
||||||
def test_sold_out(self):
|
def test_sold_out(self):
|
||||||
self.quota.items.add(self.item1)
|
self.quota.items.add(self.item1)
|
||||||
@@ -219,19 +219,19 @@ class QuotaTestCase(TestCase):
|
|||||||
total=4)
|
total=4)
|
||||||
OrderPosition.objects.create(order=order, item=self.item1, price=2)
|
OrderPosition.objects.create(order=order, item=self.item1, price=2)
|
||||||
OrderPosition.objects.create(order=order, item=self.item1, price=2)
|
OrderPosition.objects.create(order=order, item=self.item1, price=2)
|
||||||
self.assertEqual(self.item1.availability(), (Quota.AVAILABILITY_GONE, 0))
|
self.assertEqual(self.item1.check_quotas(), (Quota.AVAILABILITY_GONE, 0))
|
||||||
|
|
||||||
self.quota.items.add(self.item2)
|
self.quota.items.add(self.item2)
|
||||||
self.quota.variations.add(self.var1)
|
self.quota.variations.add(self.var1)
|
||||||
self.quota.size = 3
|
self.quota.size = 3
|
||||||
self.quota.save()
|
self.quota.save()
|
||||||
self.assertEqual(self.var1.availability(), (Quota.AVAILABILITY_OK, 1))
|
self.assertEqual(self.var1.check_quotas(), (Quota.AVAILABILITY_OK, 1))
|
||||||
|
|
||||||
order = Order.objects.create(event=self.event, status=Order.STATUS_PAID,
|
order = Order.objects.create(event=self.event, status=Order.STATUS_PAID,
|
||||||
expires=now() + timedelta(days=3),
|
expires=now() + timedelta(days=3),
|
||||||
total=4)
|
total=4)
|
||||||
OrderPosition.objects.create(order=order, item=self.item2, variation=self.var1, price=2)
|
OrderPosition.objects.create(order=order, item=self.item2, variation=self.var1, price=2)
|
||||||
self.assertEqual(self.var1.availability(), (Quota.AVAILABILITY_GONE, 0))
|
self.assertEqual(self.var1.check_quotas(), (Quota.AVAILABILITY_GONE, 0))
|
||||||
|
|
||||||
def test_ordered(self):
|
def test_ordered(self):
|
||||||
self.quota.items.add(self.item1)
|
self.quota.items.add(self.item1)
|
||||||
@@ -239,17 +239,17 @@ class QuotaTestCase(TestCase):
|
|||||||
expires=now() + timedelta(days=3),
|
expires=now() + timedelta(days=3),
|
||||||
total=4)
|
total=4)
|
||||||
OrderPosition.objects.create(order=order, item=self.item1, price=2)
|
OrderPosition.objects.create(order=order, item=self.item1, price=2)
|
||||||
self.assertEqual(self.item1.availability(), (Quota.AVAILABILITY_OK, 1))
|
self.assertEqual(self.item1.check_quotas(), (Quota.AVAILABILITY_OK, 1))
|
||||||
|
|
||||||
order = Order.objects.create(event=self.event, status=Order.STATUS_PENDING,
|
order = Order.objects.create(event=self.event, status=Order.STATUS_PENDING,
|
||||||
expires=now() + timedelta(days=3),
|
expires=now() + timedelta(days=3),
|
||||||
total=4)
|
total=4)
|
||||||
OrderPosition.objects.create(order=order, item=self.item1, price=2)
|
OrderPosition.objects.create(order=order, item=self.item1, price=2)
|
||||||
self.assertEqual(self.item1.availability(), (Quota.AVAILABILITY_ORDERED, 0))
|
self.assertEqual(self.item1.check_quotas(), (Quota.AVAILABILITY_ORDERED, 0))
|
||||||
|
|
||||||
order.expires = now() - timedelta(days=3)
|
order.expires = now() - timedelta(days=3)
|
||||||
order.save()
|
order.save()
|
||||||
self.assertEqual(self.item1.availability(), (Quota.AVAILABILITY_OK, 1))
|
self.assertEqual(self.item1.check_quotas(), (Quota.AVAILABILITY_OK, 1))
|
||||||
|
|
||||||
def test_reserved(self):
|
def test_reserved(self):
|
||||||
self.quota.items.add(self.item1)
|
self.quota.items.add(self.item1)
|
||||||
@@ -259,36 +259,100 @@ class QuotaTestCase(TestCase):
|
|||||||
expires=now() + timedelta(days=3),
|
expires=now() + timedelta(days=3),
|
||||||
total=4)
|
total=4)
|
||||||
OrderPosition.objects.create(order=order, item=self.item1, price=2)
|
OrderPosition.objects.create(order=order, item=self.item1, price=2)
|
||||||
self.assertEqual(self.item1.availability(), (Quota.AVAILABILITY_OK, 2))
|
self.assertEqual(self.item1.check_quotas(), (Quota.AVAILABILITY_OK, 2))
|
||||||
|
|
||||||
order = Order.objects.create(event=self.event, status=Order.STATUS_PENDING,
|
order = Order.objects.create(event=self.event, status=Order.STATUS_PENDING,
|
||||||
expires=now() + timedelta(days=3),
|
expires=now() + timedelta(days=3),
|
||||||
total=4)
|
total=4)
|
||||||
OrderPosition.objects.create(order=order, item=self.item1, price=2)
|
OrderPosition.objects.create(order=order, item=self.item1, price=2)
|
||||||
self.assertEqual(self.item1.availability(), (Quota.AVAILABILITY_OK, 1))
|
self.assertEqual(self.item1.check_quotas(), (Quota.AVAILABILITY_OK, 1))
|
||||||
|
|
||||||
cp = CartPosition.objects.create(event=self.event, item=self.item1, price=2,
|
cp = CartPosition.objects.create(event=self.event, item=self.item1, price=2,
|
||||||
expires=now() + timedelta(days=3))
|
expires=now() + timedelta(days=3))
|
||||||
self.assertEqual(self.item1.availability(), (Quota.AVAILABILITY_RESERVED, 0))
|
self.assertEqual(self.item1.check_quotas(), (Quota.AVAILABILITY_RESERVED, 0))
|
||||||
|
|
||||||
cp.expires = now() - timedelta(days=3)
|
cp.expires = now() - timedelta(days=3)
|
||||||
cp.save()
|
cp.save()
|
||||||
self.assertEqual(self.item1.availability(), (Quota.AVAILABILITY_OK, 1))
|
self.assertEqual(self.item1.check_quotas(), (Quota.AVAILABILITY_OK, 1))
|
||||||
|
|
||||||
self.quota.items.add(self.item2)
|
self.quota.items.add(self.item2)
|
||||||
self.quota.variations.add(self.var1)
|
self.quota.variations.add(self.var1)
|
||||||
cp = CartPosition.objects.create(event=self.event, item=self.item2, variation=self.var1,
|
cp = CartPosition.objects.create(event=self.event, item=self.item2, variation=self.var1,
|
||||||
price=2, expires=now() + timedelta(days=3))
|
price=2, expires=now() + timedelta(days=3))
|
||||||
self.assertEqual(self.item1.availability(), (Quota.AVAILABILITY_RESERVED, 0))
|
self.assertEqual(self.item1.check_quotas(), (Quota.AVAILABILITY_RESERVED, 0))
|
||||||
|
|
||||||
def test_multiple(self):
|
def test_multiple(self):
|
||||||
self.quota.items.add(self.item1)
|
self.quota.items.add(self.item1)
|
||||||
self.assertEqual(self.item1.availability(), (Quota.AVAILABILITY_OK, 2))
|
self.assertEqual(self.item1.check_quotas(), (Quota.AVAILABILITY_OK, 2))
|
||||||
|
|
||||||
quota2 = Quota.objects.create(event=self.event, name="Test 2", size=1)
|
quota2 = Quota.objects.create(event=self.event, name="Test 2", size=1)
|
||||||
quota2.items.add(self.item1)
|
quota2.items.add(self.item1)
|
||||||
self.assertEqual(self.item1.availability(), (Quota.AVAILABILITY_OK, 1))
|
self.assertEqual(self.item1.check_quotas(), (Quota.AVAILABILITY_OK, 1))
|
||||||
|
|
||||||
quota2.size = 0
|
quota2.size = 0
|
||||||
quota2.save()
|
quota2.save()
|
||||||
self.assertEqual(self.item1.availability(), (Quota.AVAILABILITY_GONE, 0))
|
self.assertEqual(self.item1.check_quotas(), (Quota.AVAILABILITY_GONE, 0))
|
||||||
|
|
||||||
|
|
||||||
|
class SettingsTestCase(TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
OrganizerSetting.DEFAULTS['test_default'] = 'def'
|
||||||
|
self.organizer = Organizer.objects.create(name='Dummy', slug='dummy')
|
||||||
|
self.event = Event.objects.create(
|
||||||
|
organizer=self.organizer, name='Dummy', slug='dummy',
|
||||||
|
date_from=now(),
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_event_set_explicit(self):
|
||||||
|
self.event.settings.test = 'foo'
|
||||||
|
self.assertEqual(self.event.settings.test, 'foo')
|
||||||
|
|
||||||
|
# Reload object
|
||||||
|
self.event = Event.objects.get(identity=self.event.identity)
|
||||||
|
self.assertEqual(self.event.settings.test, 'foo')
|
||||||
|
|
||||||
|
def test_event_set_on_organizer(self):
|
||||||
|
self.organizer.settings.test = 'foo'
|
||||||
|
self.assertEqual(self.organizer.settings.test, 'foo')
|
||||||
|
self.assertEqual(self.event.settings.test, 'foo')
|
||||||
|
|
||||||
|
# Reload object
|
||||||
|
self.organizer = Organizer.objects.get(identity=self.organizer.identity)
|
||||||
|
self.event = Event.objects.get(identity=self.event.identity)
|
||||||
|
self.assertEqual(self.organizer.settings.test, 'foo')
|
||||||
|
self.assertEqual(self.event.settings.test, 'foo')
|
||||||
|
|
||||||
|
def test_override_organizer(self):
|
||||||
|
self.organizer.settings.test = 'foo'
|
||||||
|
self.event.settings.test = 'bar'
|
||||||
|
self.assertEqual(self.organizer.settings.test, 'foo')
|
||||||
|
self.assertEqual(self.event.settings.test, 'bar')
|
||||||
|
|
||||||
|
# Reload object
|
||||||
|
self.organizer = Organizer.objects.get(identity=self.organizer.identity)
|
||||||
|
self.event = Event.objects.get(identity=self.event.identity)
|
||||||
|
self.assertEqual(self.organizer.settings.test, 'foo')
|
||||||
|
self.assertEqual(self.event.settings.test, 'bar')
|
||||||
|
|
||||||
|
def test_default(self):
|
||||||
|
self.assertEqual(self.organizer.settings.test_default, 'def')
|
||||||
|
self.assertEqual(self.event.settings.test_default, 'def')
|
||||||
|
|
||||||
|
def test_delete(self):
|
||||||
|
self.organizer.settings.test = 'foo'
|
||||||
|
self.event.settings.test = 'bar'
|
||||||
|
self.assertEqual(self.organizer.settings.test, 'foo')
|
||||||
|
self.assertEqual(self.event.settings.test, 'bar')
|
||||||
|
|
||||||
|
del self.event.settings.test
|
||||||
|
self.assertEqual(self.event.settings.test, 'foo')
|
||||||
|
|
||||||
|
self.event = Event.objects.get(identity=self.event.identity)
|
||||||
|
self.assertEqual(self.event.settings.test, 'foo')
|
||||||
|
|
||||||
|
del self.organizer.settings.test
|
||||||
|
self.assertIsNone(self.organizer.settings.test)
|
||||||
|
|
||||||
|
self.organizer = Organizer.objects.get(identity=self.organizer.identity)
|
||||||
|
self.assertIsNone(self.organizer.settings.test)
|
||||||
@@ -2,9 +2,9 @@ from django.test import TestCase
|
|||||||
from django.utils.timezone import now
|
from django.utils.timezone import now
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
from pretixbase.models import Event, Organizer
|
from pretix.base.models import Event, Organizer
|
||||||
from pretixbase.plugins import get_all_plugins
|
from pretix.base.plugins import get_all_plugins
|
||||||
from pretixbase.signals import determine_availability
|
from pretix.base.signals import determine_availability
|
||||||
|
|
||||||
|
|
||||||
class PluginRegistryTest(TestCase):
|
class PluginRegistryTest(TestCase):
|
||||||
@@ -49,9 +49,9 @@ class PluginSignalTest(TestCase):
|
|||||||
self.assertEqual(len(responses), 0)
|
self.assertEqual(len(responses), 0)
|
||||||
|
|
||||||
def test_one_plugin_active(self):
|
def test_one_plugin_active(self):
|
||||||
self.event.plugins = 'pretixplugins.testdummy'
|
self.event.plugins = 'pretix.plugins.testdummy'
|
||||||
self.event.save()
|
self.event.save()
|
||||||
payload = {'foo': 'bar'}
|
payload = {'foo': 'bar'}
|
||||||
responses = determine_availability.send(self.event, **payload)
|
responses = determine_availability.send(self.event, **payload)
|
||||||
self.assertEqual(len(responses), 1)
|
self.assertEqual(len(responses), 1)
|
||||||
self.assertIn('pretixplugins.testdummy.signals', [r[0].__module__ for r in responses])
|
self.assertIn('pretix.plugins.testdummy.signals', [r[0].__module__ for r in responses])
|
||||||
8
src/pretix/control/__init__.py
Normal file
8
src/pretix/control/__init__.py
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class PretixControlConfig(AppConfig):
|
||||||
|
name = 'pretix.control'
|
||||||
|
label = 'pretixcontrol'
|
||||||
|
|
||||||
|
default_app_config = 'pretix.control.PretixControlConfig'
|
||||||
@@ -7,7 +7,7 @@ from django.contrib.auth import REDIRECT_FIELD_NAME
|
|||||||
from django.http import HttpResponseNotFound
|
from django.http import HttpResponseNotFound
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
from pretixbase.models import Event
|
from pretix.base.models import Event
|
||||||
|
|
||||||
|
|
||||||
class PermissionMiddleware:
|
class PermissionMiddleware:
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
from django.http import HttpResponseForbidden
|
from django.http import HttpResponseForbidden
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
from pretixbase.models import EventPermission
|
from pretix.base.models import EventPermission
|
||||||
|
|
||||||
|
|
||||||
def event_permission_required(permission):
|
def event_permission_required(permission):
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
from pretixbase.signals import EventPluginSignal
|
from pretix.base.signals import EventPluginSignal
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
@import "../../../../pretixbase/static/bootstrap/less/bootstrap.less";
|
@import "../../../../base/static/bootstrap/less/bootstrap.less";
|
||||||
|
|
||||||
body {
|
body {
|
||||||
background: #eee;
|
background: #eee;
|
||||||
4
src/pretix/control/static/pretixcontrol/less/main.less
Normal file
4
src/pretix/control/static/pretixcontrol/less/main.less
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
@import "../../../../base/static/bootstrap/less/bootstrap.less";
|
||||||
|
@import "../../../../base/static/fontawesome/less/font-awesome.less";
|
||||||
|
@fa-font-path: "../../fontawesome/fonts";
|
||||||
|
@import "forms.less";
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
from django.test import TestCase, Client
|
from django.test import TestCase, Client
|
||||||
|
|
||||||
from pretixbase.models import User
|
from pretix.base.models import User
|
||||||
from pretixbase.tests import BrowserTest, on_platforms
|
from pretix.base.tests import BrowserTest, on_platforms
|
||||||
|
|
||||||
|
|
||||||
@on_platforms()
|
@on_platforms()
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import datetime
|
import datetime
|
||||||
from pretixbase.models import User, Organizer, Event, OrganizerPermission, EventPermission
|
from pretix.base.models import User, Organizer, Event, OrganizerPermission, EventPermission
|
||||||
from pretixbase.tests import BrowserTest, on_platforms
|
from pretix.base.tests import BrowserTest, on_platforms
|
||||||
|
|
||||||
|
|
||||||
@on_platforms()
|
@on_platforms()
|
||||||
@@ -54,8 +54,8 @@ class EventsTest(BrowserTest):
|
|||||||
self.driver.get('%s/control/event/%s/%s/settings/plugins' % (self.live_server_url, self.orga1.slug,
|
self.driver.get('%s/control/event/%s/%s/settings/plugins' % (self.live_server_url, self.orga1.slug,
|
||||||
self.event1.slug))
|
self.event1.slug))
|
||||||
self.assertIn("Restriction by time", self.driver.find_element_by_class_name("form-plugins").text)
|
self.assertIn("Restriction by time", self.driver.find_element_by_class_name("form-plugins").text)
|
||||||
self.assertIn("Enable", self.driver.find_element_by_name("plugin:pretixplugins.timerestriction").text)
|
self.assertIn("Enable", self.driver.find_element_by_name("plugin:pretix.plugins.timerestriction").text)
|
||||||
self.driver.find_element_by_name("plugin:pretixplugins.timerestriction").click()
|
self.driver.find_element_by_name("plugin:pretix.plugins.timerestriction").click()
|
||||||
self.assertIn("Disable", self.driver.find_element_by_name("plugin:pretixplugins.timerestriction").text)
|
self.assertIn("Disable", self.driver.find_element_by_name("plugin:pretix.plugins.timerestriction").text)
|
||||||
self.driver.find_element_by_name("plugin:pretixplugins.timerestriction").click()
|
self.driver.find_element_by_name("plugin:pretix.plugins.timerestriction").click()
|
||||||
self.assertIn("Enable", self.driver.find_element_by_name("plugin:pretixplugins.timerestriction").text)
|
self.assertIn("Enable", self.driver.find_element_by_name("plugin:pretix.plugins.timerestriction").text)
|
||||||
@@ -3,9 +3,9 @@ import time
|
|||||||
import datetime
|
import datetime
|
||||||
from django.utils import unittest
|
from django.utils import unittest
|
||||||
from selenium.webdriver.support.select import Select
|
from selenium.webdriver.support.select import Select
|
||||||
from pretixbase.models import User, Organizer, Event, OrganizerPermission, EventPermission, ItemCategory, Property, \
|
from pretix.base.models import User, Organizer, Event, OrganizerPermission, EventPermission, ItemCategory, Property, \
|
||||||
PropertyValue, Question, Quota, Item
|
PropertyValue, Question, Quota, Item
|
||||||
from pretixbase.tests import BrowserTest, on_platforms
|
from pretix.base.tests import BrowserTest, on_platforms
|
||||||
|
|
||||||
|
|
||||||
class ItemFormTest(BrowserTest):
|
class ItemFormTest(BrowserTest):
|
||||||
@@ -18,7 +18,7 @@ class ItemFormTest(BrowserTest):
|
|||||||
self.event1 = Event.objects.create(
|
self.event1 = Event.objects.create(
|
||||||
organizer=self.orga1, name='30C3', slug='30c3',
|
organizer=self.orga1, name='30C3', slug='30c3',
|
||||||
date_from=datetime.datetime(2013, 12, 26, tzinfo=datetime.timezone.utc),
|
date_from=datetime.datetime(2013, 12, 26, tzinfo=datetime.timezone.utc),
|
||||||
)
|
)
|
||||||
OrganizerPermission.objects.create(organizer=self.orga1, user=self.user)
|
OrganizerPermission.objects.create(organizer=self.orga1, user=self.user)
|
||||||
EventPermission.objects.create(event=self.event1, user=self.user, can_change_items=True,
|
EventPermission.objects.create(event=self.event1, user=self.user, can_change_items=True,
|
||||||
can_change_settings=True)
|
can_change_settings=True)
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
from django.test import TestCase, Client
|
from django.test import TestCase, Client
|
||||||
from django.utils.timezone import now
|
from django.utils.timezone import now
|
||||||
|
|
||||||
from pretixbase.models import Event, Organizer, User, EventPermission
|
from pretix.base.models import Event, Organizer, User, EventPermission
|
||||||
|
|
||||||
|
|
||||||
class PermissionMiddlewareTest(TestCase):
|
class PermissionMiddlewareTest(TestCase):
|
||||||
@@ -1,22 +1,22 @@
|
|||||||
from django.conf.urls import patterns, url, include
|
from django.conf.urls import patterns, url, include
|
||||||
from pretixcontrol.views import main, event, item
|
from pretix.control.views import main, event, item
|
||||||
|
|
||||||
urlpatterns = patterns('',)
|
urlpatterns = patterns('',)
|
||||||
urlpatterns += patterns(
|
urlpatterns += patterns(
|
||||||
'pretixcontrol.views.auth',
|
'pretix.control.views.auth',
|
||||||
url(r'^logout$', 'logout', name='auth.logout'),
|
url(r'^logout$', 'logout', name='auth.logout'),
|
||||||
url(r'^login$', 'login', name='auth.login'),
|
url(r'^login$', 'login', name='auth.login'),
|
||||||
)
|
)
|
||||||
urlpatterns += patterns(
|
urlpatterns += patterns(
|
||||||
'pretixcontrol.views.main',
|
'pretix.control.views.main',
|
||||||
url(r'^$', 'index', name='index'),
|
url(r'^$', 'index', name='index'),
|
||||||
url(r'^events/$', main.EventList.as_view(), name='events'),
|
url(r'^events/$', main.EventList.as_view(), name='events'),
|
||||||
)
|
)
|
||||||
urlpatterns += patterns(
|
urlpatterns += patterns(
|
||||||
'pretixcontrol.views.event',
|
'pretix.control.views.event',
|
||||||
url(r'^event/(?P<organizer>[^/]+)/(?P<event>[^/]+)/', include(
|
url(r'^event/(?P<organizer>[^/]+)/(?P<event>[^/]+)/', include(
|
||||||
patterns(
|
patterns(
|
||||||
'pretixcontrol.views',
|
'pretix.control.views',
|
||||||
url(r'^$', 'event.index', name='event.index'),
|
url(r'^$', 'event.index', name='event.index'),
|
||||||
url(r'^settings/$', event.EventUpdate.as_view(), name='event.settings'),
|
url(r'^settings/$', event.EventUpdate.as_view(), name='event.settings'),
|
||||||
url(r'^settings/plugins$', event.EventPlugins.as_view(), name='event.settings.plugins'),
|
url(r'^settings/plugins$', event.EventPlugins.as_view(), name='event.settings.plugins'),
|
||||||
@@ -54,5 +54,5 @@ urlpatterns += patterns(
|
|||||||
name='event.items.quotas.delete'),
|
name='event.items.quotas.delete'),
|
||||||
url(r'^quotas/add$', item.QuotaCreate.as_view(), name='event.items.quotas.add'),
|
url(r'^quotas/add$', item.QuotaCreate.as_view(), name='event.items.quotas.add'),
|
||||||
)
|
)
|
||||||
))
|
))
|
||||||
)
|
)
|
||||||
@@ -7,10 +7,10 @@ from django.utils.translation import ugettext_lazy as _
|
|||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
|
|
||||||
from pytz import common_timezones
|
from pytz import common_timezones
|
||||||
from pretixbase.forms import VersionedModelForm
|
from pretix.base.forms import VersionedModelForm
|
||||||
|
|
||||||
from pretixbase.models import Event
|
from pretix.base.models import Event
|
||||||
from pretixcontrol.permissions import EventPermissionRequiredMixin
|
from pretix.control.permissions import EventPermissionRequiredMixin
|
||||||
|
|
||||||
|
|
||||||
class EventUpdateForm(VersionedModelForm):
|
class EventUpdateForm(VersionedModelForm):
|
||||||
@@ -75,7 +75,7 @@ class EventPlugins(EventPermissionRequiredMixin, TemplateView, SingleObjectMixin
|
|||||||
return self.request.event
|
return self.request.event
|
||||||
|
|
||||||
def get_context_data(self, *args, **kwargs) -> dict:
|
def get_context_data(self, *args, **kwargs) -> dict:
|
||||||
from pretixbase.plugins import get_all_plugins
|
from pretix.base.plugins import get_all_plugins
|
||||||
context = super().get_context_data(*args, **kwargs)
|
context = super().get_context_data(*args, **kwargs)
|
||||||
context['plugins'] = [p for p in get_all_plugins() if not p.name.startswith('.')]
|
context['plugins'] = [p for p in get_all_plugins() if not p.name.startswith('.')]
|
||||||
context['plugins_active'] = self.object.get_plugins()
|
context['plugins_active'] = self.object.get_plugins()
|
||||||
@@ -1,15 +1,15 @@
|
|||||||
from itertools import product
|
from itertools import product
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.db import transaction, IntegrityError
|
from django.db import transaction
|
||||||
from django.forms.widgets import flatatt
|
from django.forms.widgets import flatatt
|
||||||
from django.utils.encoding import force_text
|
from django.utils.encoding import force_text
|
||||||
from django.utils.html import format_html
|
from django.utils.html import format_html
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from pretixbase.forms import VersionedModelForm
|
from pretix.base.forms import VersionedModelForm
|
||||||
|
|
||||||
from pretixbase.models import ItemVariation, PropertyValue, Item
|
from pretix.base.models import ItemVariation, PropertyValue, Item
|
||||||
|
|
||||||
|
|
||||||
class TolerantFormsetModelForm(VersionedModelForm):
|
class TolerantFormsetModelForm(VersionedModelForm):
|
||||||
@@ -12,14 +12,14 @@ from django.http import HttpResponseRedirect, HttpResponseForbidden
|
|||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect
|
||||||
from django.forms.models import inlineformset_factory
|
from django.forms.models import inlineformset_factory
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from pretixbase.forms import VersionedModelForm
|
from pretix.base.forms import VersionedModelForm
|
||||||
|
|
||||||
from pretixbase.models import (
|
from pretix.base.models import (
|
||||||
Item, ItemCategory, Property, ItemVariation, PropertyValue, Question, Quota,
|
Item, ItemCategory, Property, ItemVariation, PropertyValue, Question, Quota,
|
||||||
Versionable)
|
Versionable)
|
||||||
from pretixcontrol.permissions import EventPermissionRequiredMixin, event_permission_required
|
from pretix.control.permissions import EventPermissionRequiredMixin, event_permission_required
|
||||||
from pretixcontrol.views.forms import TolerantFormsetModelForm, VariationsField
|
from pretix.control.views.forms import TolerantFormsetModelForm, VariationsField
|
||||||
from pretixcontrol.signals import restriction_formset
|
from pretix.control.signals import restriction_formset
|
||||||
|
|
||||||
|
|
||||||
class ItemList(ListView):
|
class ItemList(ListView):
|
||||||
@@ -649,7 +649,7 @@ class ItemCreate(EventPermissionRequiredMixin, CreateView):
|
|||||||
'organizer': self.request.event.organizer.slug,
|
'organizer': self.request.event.organizer.slug,
|
||||||
'event': self.request.event.slug,
|
'event': self.request.event.slug,
|
||||||
'item': self.object.identity,
|
'item': self.object.identity,
|
||||||
}) + '?success=true'
|
}) + '?success=true'
|
||||||
|
|
||||||
def get_form_kwargs(self):
|
def get_form_kwargs(self):
|
||||||
"""
|
"""
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
from django.views.generic import ListView
|
from django.views.generic import ListView
|
||||||
|
|
||||||
from pretixbase.models import Event
|
from pretix.base.models import Event
|
||||||
|
|
||||||
|
|
||||||
class EventList(ListView):
|
class EventList(ListView):
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
from django.apps import AppConfig
|
from django.apps import AppConfig
|
||||||
from pretixbase.plugins import PluginType
|
from pretix.base.plugins import PluginType
|
||||||
|
|
||||||
|
|
||||||
class TestDummyApp(AppConfig):
|
class TestDummyApp(AppConfig):
|
||||||
name = 'pretixplugins.testdummy'
|
name = 'pretix.plugins.testdummy'
|
||||||
verbose_name = '.testdummy'
|
verbose_name = '.testdummy'
|
||||||
|
|
||||||
class TixlPluginMeta:
|
class PretixPluginMeta:
|
||||||
type = PluginType.RESTRICTION
|
type = PluginType.RESTRICTION
|
||||||
name = '.testdummy'
|
name = '.testdummy'
|
||||||
version = '1.0.0'
|
version = '1.0.0'
|
||||||
@@ -14,4 +14,4 @@ class TestDummyApp(AppConfig):
|
|||||||
def ready(self):
|
def ready(self):
|
||||||
from . import signals # NOQA
|
from . import signals # NOQA
|
||||||
|
|
||||||
default_app_config = 'pretixplugins.testdummy.TestDummyApp'
|
default_app_config = 'pretix.plugins.testdummy.TestDummyApp'
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
|
|
||||||
from pretixbase.signals import determine_availability
|
from pretix.base.signals import determine_availability
|
||||||
|
|
||||||
|
|
||||||
@receiver(determine_availability)
|
@receiver(determine_availability)
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
from django.apps import AppConfig
|
from django.apps import AppConfig
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from pretixbase.plugins import PluginType
|
from pretix.base.plugins import PluginType
|
||||||
|
|
||||||
|
|
||||||
class TimeRestrictionApp(AppConfig):
|
class TimeRestrictionApp(AppConfig):
|
||||||
name = 'pretixplugins.timerestriction'
|
name = 'pretix.plugins.timerestriction'
|
||||||
verbose_name = _("Time restriction")
|
verbose_name = _("Time restriction")
|
||||||
|
|
||||||
class TixlPluginMeta:
|
class PretixPluginMeta:
|
||||||
type = PluginType.RESTRICTION
|
type = PluginType.RESTRICTION
|
||||||
name = _("Restriction by time")
|
name = _("Restriction by time")
|
||||||
author = _("the pretix team")
|
author = _("the pretix team")
|
||||||
@@ -19,4 +19,4 @@ class TimeRestrictionApp(AppConfig):
|
|||||||
def ready(self):
|
def ready(self):
|
||||||
from . import signals # NOQA
|
from . import signals # NOQA
|
||||||
|
|
||||||
default_app_config = 'pretixplugins.timerestriction.TimeRestrictionApp'
|
default_app_config = 'pretix.plugins.timerestriction.TimeRestrictionApp'
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.db import models, migrations
|
from django.db import models, migrations
|
||||||
import pretixbase.models
|
import pretix.base.models
|
||||||
import versions.models
|
import versions.models
|
||||||
|
|
||||||
|
|
||||||
@@ -26,7 +26,8 @@ class Migration(migrations.Migration):
|
|||||||
('price', models.DecimalField(null=True, blank=True, verbose_name='Price in time frame', max_digits=7, decimal_places=2)),
|
('price', models.DecimalField(null=True, blank=True, verbose_name='Price in time frame', max_digits=7, decimal_places=2)),
|
||||||
('event', versions.models.VersionedForeignKey(to='pretixbase.Event', related_name='restrictions_timerestriction_timerestriction', verbose_name='Event')),
|
('event', versions.models.VersionedForeignKey(to='pretixbase.Event', related_name='restrictions_timerestriction_timerestriction', verbose_name='Event')),
|
||||||
('item', versions.models.VersionedForeignKey(to='pretixbase.Item', blank=True, null=True, related_name='restrictions_timerestriction_timerestriction', verbose_name='Item')),
|
('item', versions.models.VersionedForeignKey(to='pretixbase.Item', blank=True, null=True, related_name='restrictions_timerestriction_timerestriction', verbose_name='Item')),
|
||||||
('variations', pretixbase.models.VariationsField(to='pretixbase.ItemVariation', blank=True, verbose_name='Variations', related_name='restrictions_timerestriction_timerestriction')),
|
('variations', pretix.base.models.VariationsField(to='pretixbase.ItemVariation', blank=True,
|
||||||
|
verbose_name='Variations', related_name='restrictions_timerestriction_timerestriction')),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'verbose_name': 'Restriction',
|
'verbose_name': 'Restriction',
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from pretixbase.models import BaseRestriction
|
from pretix.base.models import BaseRestriction
|
||||||
|
|
||||||
|
|
||||||
class TimeRestriction(BaseRestriction):
|
class TimeRestriction(BaseRestriction):
|
||||||
@@ -3,10 +3,10 @@ from django.utils.timezone import now
|
|||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.forms.models import inlineformset_factory
|
from django.forms.models import inlineformset_factory
|
||||||
|
|
||||||
from pretixbase.signals import determine_availability
|
from pretix.base.signals import determine_availability
|
||||||
from pretixbase.models import Item
|
from pretix.base.models import Item
|
||||||
from pretixcontrol.views.forms import VariationsField, RestrictionInlineFormset, RestrictionForm
|
from pretix.control.views.forms import RestrictionInlineFormset, RestrictionForm
|
||||||
from pretixcontrol.signals import restriction_formset
|
from pretix.control.signals import restriction_formset
|
||||||
|
|
||||||
from .models import TimeRestriction
|
from .models import TimeRestriction
|
||||||
|
|
||||||
@@ -3,13 +3,13 @@ from datetime import timedelta
|
|||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.utils.timezone import now
|
from django.utils.timezone import now
|
||||||
|
|
||||||
from pretixbase.models import (
|
from pretix.base.models import (
|
||||||
Event, Organizer, Item, Property, PropertyValue, ItemVariation
|
Event, Organizer, Item, Property, PropertyValue, ItemVariation
|
||||||
)
|
)
|
||||||
|
|
||||||
# Do NOT use relative imports here
|
# Do NOT use relative imports here
|
||||||
from pretixplugins.timerestriction import signals
|
from pretix.plugins.timerestriction import signals
|
||||||
from pretixplugins.timerestriction.models import TimeRestriction
|
from pretix.plugins.timerestriction.models import TimeRestriction
|
||||||
|
|
||||||
|
|
||||||
class TimeRestrictionTest(TestCase):
|
class TimeRestrictionTest(TestCase):
|
||||||
8
src/pretix/presale/__init__.py
Normal file
8
src/pretix/presale/__init__.py
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class PretixPresaleConfig(AppConfig):
|
||||||
|
name = 'pretix.presale'
|
||||||
|
label = 'pretixpresale'
|
||||||
|
|
||||||
|
default_app_config = 'pretix.presale.PretixPresaleConfig'
|
||||||
@@ -1,13 +1,7 @@
|
|||||||
from django.conf import settings
|
|
||||||
from django.core.urlresolvers import resolve
|
from django.core.urlresolvers import resolve
|
||||||
from django.utils.encoding import force_str
|
|
||||||
from django.utils.six.moves.urllib.parse import urlparse
|
|
||||||
from django.shortcuts import resolve_url
|
|
||||||
from django.contrib.auth import REDIRECT_FIELD_NAME
|
|
||||||
from django.http import HttpResponseNotFound
|
from django.http import HttpResponseNotFound
|
||||||
from django.utils.translation import ugettext as _
|
|
||||||
|
|
||||||
from pretixbase.models import Event
|
from pretix.base.models import Event
|
||||||
|
|
||||||
|
|
||||||
class EventMiddleware:
|
class EventMiddleware:
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user