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:
Maico Timmerman
2019-12-05 12:20:40 +01:00
committed by Raphael Michel
parent f88a09a78c
commit dddf91d3bf
9 changed files with 127 additions and 21 deletions

View File

@@ -683,6 +683,10 @@ Your {event} team"""))
'default': '',
'type': LazyI18nString
},
'event_team_provisioning': {
'default': 'True',
'type': bool
},
'update_check_ack': {
'default': 'False',
'type': bool

View File

@@ -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):

View File

@@ -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,

View File

@@ -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 %}

View File

@@ -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>

View File

@@ -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(

View File

@@ -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', {

View File

@@ -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')

View File

@@ -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')