mirror of
https://github.com/pretix/pretix.git
synced 2026-04-24 23:32:33 +00:00
Feature/optional team provisioning (#1487)
* Event creation: Add option to select existing team instead of provisioning. * Event creation: disallow event team provisioning on organizer level. * Event team provisioning: update default setting location and display strings. * Update src/pretix/control/views/main.py Co-Authored-By: Raphael Michel <mail@raphaelmichel.de>
This commit is contained in:
committed by
Raphael Michel
parent
f88a09a78c
commit
dddf91d3bf
@@ -683,6 +683,10 @@ Your {event} team"""))
|
||||
'default': '',
|
||||
'type': LazyI18nString
|
||||
},
|
||||
'event_team_provisioning': {
|
||||
'default': 'True',
|
||||
'type': bool
|
||||
},
|
||||
'update_check_ack': {
|
||||
'default': 'False',
|
||||
'type': bool
|
||||
|
||||
@@ -22,7 +22,7 @@ from pytz import common_timezones, timezone
|
||||
from pretix.base.channels import get_all_sales_channels
|
||||
from pretix.base.email import get_available_placeholders
|
||||
from pretix.base.forms import I18nModelForm, PlaceholderValidator, SettingsForm
|
||||
from pretix.base.models import Event, Organizer, TaxRule
|
||||
from pretix.base.models import Event, Organizer, TaxRule, Team
|
||||
from pretix.base.models.event import EventMetaValue, SubEvent
|
||||
from pretix.base.reldate import RelativeDateField, RelativeDateTimeField
|
||||
from pretix.base.settings import PERSON_NAME_SCHEMES, PERSON_NAME_TITLE_GROUPS
|
||||
@@ -101,6 +101,16 @@ class EventWizardBasicsForm(I18nModelForm):
|
||||
required=False
|
||||
)
|
||||
|
||||
team = forms.ModelChoiceField(
|
||||
label=_("Grant access to team"),
|
||||
help_text=_("You are allowed to create events under this organizer, however you do not have permission "
|
||||
"to edit all events under this organizer. Please select one of your existing teams that will"
|
||||
" be granted access to this event."),
|
||||
queryset=Team.objects.none(),
|
||||
required=False,
|
||||
empty_label=_('Create a new team for this event with me as the only member')
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = Event
|
||||
fields = [
|
||||
@@ -133,7 +143,7 @@ class EventWizardBasicsForm(I18nModelForm):
|
||||
self.organizer = kwargs.pop('organizer')
|
||||
self.locales = kwargs.get('locales')
|
||||
self.has_subevents = kwargs.pop('has_subevents')
|
||||
kwargs.pop('user')
|
||||
self.user = kwargs.pop('user')
|
||||
kwargs.pop('session')
|
||||
super().__init__(*args, **kwargs)
|
||||
self.initial['timezone'] = get_current_timezone_name()
|
||||
@@ -147,6 +157,15 @@ class EventWizardBasicsForm(I18nModelForm):
|
||||
del self.fields['presale_start']
|
||||
del self.fields['presale_end']
|
||||
|
||||
if self.has_control_rights(self.user, self.organizer):
|
||||
del self.fields['team']
|
||||
else:
|
||||
self.fields['team'].queryset = self.user.teams.filter(organizer=self.organizer)
|
||||
if not self.organizer.settings.get("event_team_provisioning", True, as_type=bool):
|
||||
self.fields['team'].required = True
|
||||
self.fields['team'].empty_label = None
|
||||
self.fields['team'].initial = 0
|
||||
|
||||
def clean(self):
|
||||
data = super().clean()
|
||||
if data.get('locale') not in self.locales:
|
||||
@@ -179,6 +198,13 @@ class EventWizardBasicsForm(I18nModelForm):
|
||||
)
|
||||
return slug
|
||||
|
||||
@staticmethod
|
||||
def has_control_rights(user, organizer):
|
||||
return user.teams.filter(
|
||||
organizer=organizer, all_events=True, can_change_event_settings=True, can_change_items=True,
|
||||
can_change_orders=True, can_change_vouchers=True
|
||||
).exists()
|
||||
|
||||
|
||||
class EventChoiceMixin:
|
||||
def label_from_instance(self, obj):
|
||||
|
||||
@@ -211,6 +211,15 @@ class OrganizerSettingsForm(SettingsForm):
|
||||
widget=I18nTextarea,
|
||||
help_text=_('Not displayed anywhere by default, but if you want to, you can use this e.g. in ticket templates.')
|
||||
)
|
||||
|
||||
event_team_provisioning = forms.BooleanField(
|
||||
label=_('Allow creating a new team during event creation'),
|
||||
help_text=_('Users that do not have access to all events under this organizer, must select one of their teams '
|
||||
'to have access to the created event. This setting allows users to create an event-specified team'
|
||||
' on-the-fly, even when they do not have \"Can change teams and permissions\" permission.'),
|
||||
required=False,
|
||||
)
|
||||
|
||||
primary_color = forms.CharField(
|
||||
label=_("Primary color"),
|
||||
required=False,
|
||||
|
||||
@@ -76,4 +76,11 @@
|
||||
{% bootstrap_field form.presale_end layout="control" %}
|
||||
</fieldset>
|
||||
{% endif %}
|
||||
|
||||
{% if form.team %}
|
||||
<fieldset>
|
||||
<legend>{% trans "Team" %}</legend>
|
||||
{% bootstrap_field form.team layout="control" %}
|
||||
</fieldset>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
{% bootstrap_field form.domain layout="control" %}
|
||||
{% endif %}
|
||||
{% bootstrap_field sform.organizer_info_text layout="control" %}
|
||||
{% bootstrap_field sform.event_team_provisioning layout="control" %}
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>{% trans "Organizer page" %}</legend>
|
||||
|
||||
@@ -228,19 +228,19 @@ class EventWizard(SafeSessionWizardView):
|
||||
event.testmode = True
|
||||
form_dict['basics'].save()
|
||||
|
||||
has_control_rights = self.request.user.teams.filter(
|
||||
organizer=event.organizer, all_events=True, can_change_event_settings=True, can_change_items=True,
|
||||
can_change_orders=True, can_change_vouchers=True
|
||||
).exists()
|
||||
if not has_control_rights:
|
||||
t = Team.objects.create(
|
||||
organizer=event.organizer, name=_('Team {event}').format(event=event.name),
|
||||
can_change_event_settings=True, can_change_items=True,
|
||||
can_view_orders=True, can_change_orders=True, can_view_vouchers=True,
|
||||
can_change_vouchers=True
|
||||
)
|
||||
t.members.add(self.request.user)
|
||||
t.limit_events.add(event)
|
||||
if not EventWizardBasicsForm.has_control_rights(self.request.user, event.organizer):
|
||||
if basics_data["team"] is not None:
|
||||
t = basics_data["team"]
|
||||
t.limit_events.add(event)
|
||||
elif event.organizer.settings.event_team_provisioning:
|
||||
t = Team.objects.create(
|
||||
organizer=event.organizer, name=_('Team {event}').format(event=event.name),
|
||||
can_change_event_settings=True, can_change_items=True,
|
||||
can_view_orders=True, can_change_orders=True, can_view_vouchers=True,
|
||||
can_change_vouchers=True
|
||||
)
|
||||
t.members.add(self.request.user)
|
||||
t.limit_events.add(event)
|
||||
|
||||
if event.has_subevents:
|
||||
se = event.subevents.create(
|
||||
|
||||
@@ -36,10 +36,14 @@ class EventsTest(SoupTest):
|
||||
date_from=datetime.datetime(2014, 9, 5, tzinfo=datetime.timezone.utc),
|
||||
)
|
||||
|
||||
t = Team.objects.create(organizer=self.orga1, can_create_events=True, can_change_event_settings=True,
|
||||
can_change_items=True)
|
||||
t.members.add(self.user)
|
||||
t.limit_events.add(self.event1)
|
||||
self.team1 = Team.objects.create(organizer=self.orga1, can_create_events=True, can_change_event_settings=True,
|
||||
can_change_items=True)
|
||||
self.team1.members.add(self.user)
|
||||
self.team1.limit_events.add(self.event1)
|
||||
|
||||
self.team2 = Team.objects.create(organizer=self.orga1, can_change_event_settings=True, can_change_items=True,
|
||||
can_change_orders=True, can_change_vouchers=True)
|
||||
self.team2.members.add(self.user)
|
||||
|
||||
self.client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
|
||||
@@ -579,6 +583,7 @@ class EventsTest(SoupTest):
|
||||
'basics-presale_start_1': '10:00:00',
|
||||
'basics-presale_end_0': '2016-11-30',
|
||||
'basics-presale_end_1': '18:00:00',
|
||||
'basics-team': '',
|
||||
})
|
||||
|
||||
self.post_doc('/control/events/add', {
|
||||
@@ -639,6 +644,7 @@ class EventsTest(SoupTest):
|
||||
'basics-presale_start_1': '10:00:00',
|
||||
'basics-presale_end_0': '2016-11-30',
|
||||
'basics-presale_end_1': '18:00:00',
|
||||
'basics-team': '',
|
||||
})
|
||||
self.post_doc('/control/events/add', {
|
||||
'event_wizard-current_step': 'copy',
|
||||
@@ -772,6 +778,7 @@ class EventsTest(SoupTest):
|
||||
'basics-presale_start_1': '10:00:00',
|
||||
'basics-presale_end_0': '2016-11-30',
|
||||
'basics-presale_end_1': '18:00:00',
|
||||
'basics-team': '',
|
||||
})
|
||||
|
||||
assert not doc.select("#id_copy-copy_from_event_1")
|
||||
@@ -822,6 +829,7 @@ class EventsTest(SoupTest):
|
||||
'basics-presale_start_1': '',
|
||||
'basics-presale_end_0': '',
|
||||
'basics-presale_end_1': '',
|
||||
'basics-team': '',
|
||||
})
|
||||
self.post_doc('/control/events/add', {
|
||||
'event_wizard-current_step': 'copy',
|
||||
@@ -844,6 +852,55 @@ class EventsTest(SoupTest):
|
||||
assert ev.presale_start is None
|
||||
assert ev.presale_end is None
|
||||
|
||||
def test_create_event_existing_team(self):
|
||||
self.post_doc('/control/events/add', {
|
||||
'event_wizard-current_step': 'foundation',
|
||||
'event_wizard-prefix': 'event_wizard',
|
||||
'foundation-organizer': self.orga1.pk,
|
||||
'foundation-locales': 'en'
|
||||
})
|
||||
self.post_doc('/control/events/add', {
|
||||
'event_wizard-current_step': 'basics',
|
||||
'event_wizard-prefix': 'event_wizard',
|
||||
'basics-name_0': '33C3',
|
||||
'basics-slug': '33c3',
|
||||
'basics-date_from_0': '2016-12-27',
|
||||
'basics-date_from_1': '10:00:00',
|
||||
'basics-date_to_0': '',
|
||||
'basics-date_to_1': '',
|
||||
'basics-location_0': 'Hamburg',
|
||||
'basics-currency': 'EUR',
|
||||
'basics-tax_rate': '',
|
||||
'basics-locale': 'en',
|
||||
'basics-timezone': 'UTC',
|
||||
'basics-presale_start_0': '',
|
||||
'basics-presale_start_1': '',
|
||||
'basics-presale_end_0': '',
|
||||
'basics-presale_end_1': '',
|
||||
'basics-team': str(self.team2.pk),
|
||||
})
|
||||
self.post_doc('/control/events/add', {
|
||||
'event_wizard-current_step': 'copy',
|
||||
'event_wizard-prefix': 'event_wizard',
|
||||
'copy-copy_from_event': ''
|
||||
})
|
||||
|
||||
with scopes_disabled():
|
||||
ev = Event.objects.get(slug='33c3')
|
||||
assert ev.name == LazyI18nString({'en': '33C3'})
|
||||
assert ev.settings.locales == ['en']
|
||||
assert ev.settings.locale == 'en'
|
||||
assert ev.currency == 'EUR'
|
||||
assert ev.settings.timezone == 'UTC'
|
||||
assert ev.organizer == self.orga1
|
||||
assert ev.location == LazyI18nString({'en': 'Hamburg'})
|
||||
team = Team.objects.filter(limit_events=ev, members=self.user).first()
|
||||
assert team == self.team2
|
||||
assert ev.date_from == datetime.datetime(2016, 12, 27, 10, 0, 0, tzinfo=pytz.utc)
|
||||
assert ev.date_to is None
|
||||
assert ev.presale_start is None
|
||||
assert ev.presale_end is None
|
||||
|
||||
def test_create_event_missing_date_from(self):
|
||||
# date_from is mandatory
|
||||
self.post_doc('/control/events/add', {
|
||||
|
||||
@@ -20,7 +20,8 @@ class BadgeLayoutFormTest(SoupTest):
|
||||
)
|
||||
self.item1 = Item.objects.create(event=self.event1, name="Standard", default_price=0, position=1)
|
||||
t = Team.objects.create(organizer=self.orga1, can_change_event_settings=True, can_view_orders=True,
|
||||
can_change_items=True, all_events=True, can_create_events=True)
|
||||
can_change_items=True, all_events=True, can_create_events=True,
|
||||
can_change_orders=True, can_change_vouchers=True)
|
||||
t.members.add(self.user)
|
||||
t.limit_events.add(self.event1)
|
||||
self.client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
|
||||
@@ -21,7 +21,8 @@ class TicketLayoutFormTest(SoupTest):
|
||||
)
|
||||
self.item1 = Item.objects.create(event=self.event1, name="Standard", default_price=0, position=1)
|
||||
t = Team.objects.create(organizer=self.orga1, can_change_event_settings=True, can_view_orders=True,
|
||||
can_change_items=True, all_events=True, can_create_events=True)
|
||||
can_change_items=True, all_events=True, can_create_events=True,
|
||||
can_change_vouchers=True, can_change_orders=True)
|
||||
t.members.add(self.user)
|
||||
t.limit_events.add(self.event1)
|
||||
self.client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
|
||||
Reference in New Issue
Block a user