forked from CGM_Public/pretix_original
Event series calendar: Allow to hide all past events (#3142)
This commit is contained in:
@@ -693,6 +693,7 @@ class EventSettingsSerializer(SettingsSerializer):
|
||||
'frontpage_subevent_ordering',
|
||||
'event_list_type',
|
||||
'event_list_available_only',
|
||||
'event_calendar_future_only',
|
||||
'frontpage_text',
|
||||
'event_info_text',
|
||||
'attendee_names_asked',
|
||||
|
||||
@@ -1397,6 +1397,19 @@ DEFAULTS = {
|
||||
'serializer_class': serializers.BooleanField,
|
||||
'form_kwargs': dict(
|
||||
label=_("Hide all unavailable dates from calendar or list views"),
|
||||
help_text=_("This option currently only affects the calendar of this event series, not the organizer-wide "
|
||||
"calendar.")
|
||||
)
|
||||
},
|
||||
'event_calendar_future_only': {
|
||||
'default': 'False',
|
||||
'type': bool,
|
||||
'form_class': forms.BooleanField,
|
||||
'serializer_class': serializers.BooleanField,
|
||||
'form_kwargs': dict(
|
||||
label=_("Hide all past dates from calendar"),
|
||||
help_text=_("This option currently only affects the calendar of this event series, not the organizer-wide "
|
||||
"calendar.")
|
||||
)
|
||||
},
|
||||
'allow_modifications_after_checkin': {
|
||||
|
||||
@@ -511,6 +511,7 @@ class EventSettingsForm(SettingsForm):
|
||||
'low_availability_percentage',
|
||||
'event_list_type',
|
||||
'event_list_available_only',
|
||||
'event_calendar_future_only',
|
||||
'frontpage_text',
|
||||
'event_info_text',
|
||||
'attendee_names_asked',
|
||||
@@ -602,6 +603,7 @@ class EventSettingsForm(SettingsForm):
|
||||
del self.fields['frontpage_subevent_ordering']
|
||||
del self.fields['event_list_type']
|
||||
del self.fields['event_list_available_only']
|
||||
del self.fields['event_calendar_future_only']
|
||||
|
||||
# create "virtual" fields for better UX when editing <name>_asked and <name>_required fields
|
||||
self.virtual_keys = []
|
||||
|
||||
@@ -315,6 +315,9 @@
|
||||
{% if sform.event_list_available_only %}
|
||||
{% bootstrap_field sform.event_list_available_only layout="control" %}
|
||||
{% endif %}
|
||||
{% if sform.event_calendar_future_only %}
|
||||
{% bootstrap_field sform.event_calendar_future_only layout="control" %}
|
||||
{% endif %}
|
||||
{% bootstrap_field sform.low_availability_percentage layout="control" addon_after="%" %}
|
||||
|
||||
{% url "control:organizer.edit" organizer=request.organizer.slug as org_url %}
|
||||
|
||||
@@ -4,11 +4,13 @@
|
||||
<nav aria-label="{% trans "calendar navigation" %}">
|
||||
<ul class="row calendar-nav">
|
||||
<li class="col-sm-4 col-xs-2 text-left flip">
|
||||
<a href="?{% url_replace request "date" subevent_list.before|date:"Y-m" %}"
|
||||
class="btn btn-default" data-save-scrollpos aria-label="{% blocktrans with month=subevent_list.before|date:"F Y" %}Show previous month, {{ month }}{% endblocktrans %}">
|
||||
<span class="fa fa-arrow-left" aria-hidden="true"></span>
|
||||
<span class="hidden-xs">{{ subevent_list.before|date:"F Y" }}</span>
|
||||
</a>
|
||||
{% if subevent_list.has_before %}
|
||||
<a href="?{% url_replace request "date" subevent_list.before|date:"Y-m" %}"
|
||||
class="btn btn-default" data-save-scrollpos aria-label="{% blocktrans with month=subevent_list.before|date:"F Y" %}Show previous month, {{ month }}{% endblocktrans %}">
|
||||
<span class="fa fa-arrow-left" aria-hidden="true"></span>
|
||||
<span class="hidden-xs">{{ subevent_list.before|date:"F Y" }}</span>
|
||||
</a>
|
||||
{% endif %}
|
||||
</li>
|
||||
<li class="col-sm-4 col-xs-8 text-center">
|
||||
<form class="form-inline" method="get" id="monthselform" action="{% eventurl event "presale:event.index" cart_namespace=cart_namespace %}">
|
||||
@@ -32,11 +34,13 @@
|
||||
</form>
|
||||
</li>
|
||||
<li class="col-sm-4 col-xs-2 text-right flip">
|
||||
<a href="?{% url_replace request "date" subevent_list.after|date:"Y-m" %}"
|
||||
class="btn btn-default" data-save-scrollpos aria-label="{% blocktrans with month=subevent_list.after|date:"F Y" %}Show next month, {{ month }}{% endblocktrans %}">
|
||||
<span class="hidden-xs">{{ subevent_list.after|date:"F Y" }}</span>
|
||||
<span class="fa fa-arrow-right" aria-hidden="true"></span>
|
||||
</a>
|
||||
{% if subevent_list.has_after %}
|
||||
<a href="?{% url_replace request "date" subevent_list.after|date:"Y-m" %}"
|
||||
class="btn btn-default" data-save-scrollpos aria-label="{% blocktrans with month=subevent_list.after|date:"F Y" %}Show next month, {{ month }}{% endblocktrans %}">
|
||||
<span class="hidden-xs">{{ subevent_list.after|date:"F Y" }}</span>
|
||||
<span class="fa fa-arrow-right" aria-hidden="true"></span>
|
||||
</a>
|
||||
{% endif %}
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
@@ -4,11 +4,13 @@
|
||||
<nav aria-label="{% trans "calendar navigation" %}">
|
||||
<ul class="row calendar-nav">
|
||||
<li class="col-sm-4 col-xs-2 text-left flip">
|
||||
<a href="?{% url_replace request "date" subevent_list.before|date:"o-\WW" %}"
|
||||
class="btn btn-default" data-save-scrollpos aria-label="{% blocktrans with week=subevent_list.before|date:subevent_list.week_format %}Show previous week, {{ week }}{% endblocktrans %}">
|
||||
<span class="fa fa-arrow-left" aria-hidden="true"></span>
|
||||
<span class="hidden-xs">{{ subevent_list.before|date:subevent_list.week_format }}</span>
|
||||
</a>
|
||||
{% if subevent_list.has_before %}
|
||||
<a href="?{% url_replace request "date" subevent_list.before|date:"o-\WW" %}"
|
||||
class="btn btn-default" data-save-scrollpos aria-label="{% blocktrans with week=subevent_list.before|date:subevent_list.week_format %}Show previous week, {{ week }}{% endblocktrans %}">
|
||||
<span class="fa fa-arrow-left" aria-hidden="true"></span>
|
||||
<span class="hidden-xs">{{ subevent_list.before|date:subevent_list.week_format }}</span>
|
||||
</a>
|
||||
{% endif %}
|
||||
</li>
|
||||
<li class="col-sm-4 col-xs-8 text-center">
|
||||
<form class="form-inline" method="get" id="monthselform" action="{% eventurl event "presale:event.index" cart_namespace=cart_namespace %}">
|
||||
@@ -36,11 +38,13 @@
|
||||
</form>
|
||||
</li>
|
||||
<li class="col-sm-4 col-xs-2 text-right flip">
|
||||
<a href="?{% url_replace request "date" subevent_list.after|date:"o-\WW" %}"
|
||||
class="btn btn-default" data-save-scrollpos aria-label="{% blocktrans with week=subevent_list.after|date:subevent_list.week_format %}Show next week, {{ week }}{% endblocktrans %}">
|
||||
<span class="hidden-xs">{{ subevent_list.after|date:subevent_list.week_format }}</span>
|
||||
<span class="fa fa-arrow-right" aria-hidden="true"></span>
|
||||
</a>
|
||||
{% if subevent_list.has_after %}
|
||||
<a href="?{% url_replace request "date" subevent_list.after|date:"o-\WW" %}"
|
||||
class="btn btn-default" data-save-scrollpos aria-label="{% blocktrans with week=subevent_list.after|date:subevent_list.week_format %}Show next week, {{ week }}{% endblocktrans %}">
|
||||
<span class="hidden-xs">{{ subevent_list.after|date:subevent_list.week_format }}</span>
|
||||
<span class="fa fa-arrow-right" aria-hidden="true"></span>
|
||||
</a>
|
||||
{% endif %}
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
@@ -63,7 +63,7 @@ from pretix.base.channels import get_all_sales_channels
|
||||
from pretix.base.models import (
|
||||
ItemVariation, Quota, SeatCategoryMapping, Voucher,
|
||||
)
|
||||
from pretix.base.models.event import SubEvent
|
||||
from pretix.base.models.event import Event, SubEvent
|
||||
from pretix.base.models.items import (
|
||||
ItemAddOn, ItemBundle, SubEventItem, SubEventItemVariation,
|
||||
)
|
||||
@@ -74,7 +74,7 @@ from pretix.presale.ical import get_public_ical
|
||||
from pretix.presale.signals import item_description
|
||||
from pretix.presale.views.organizer import (
|
||||
EventListMixin, add_subevents_for_days, days_for_template,
|
||||
filter_qs_by_attr, weeks_for_template,
|
||||
filter_qs_by_attr, has_before_after, weeks_for_template,
|
||||
)
|
||||
|
||||
from ...helpers.formats.en.formats import SHORT_MONTH_DAY_FORMAT, WEEK_FORMAT
|
||||
@@ -586,6 +586,11 @@ class EventIndex(EventViewMixin, EventListMixin, CartMixin, TemplateView):
|
||||
before = datetime(self.year, self.month, 1, 0, 0, 0, tzinfo=tz) - timedelta(days=1)
|
||||
after = datetime(self.year, self.month, ndays, 0, 0, 0, tzinfo=tz) + timedelta(days=1)
|
||||
|
||||
if self.request.event.settings.event_calendar_future_only:
|
||||
limit_before = now().astimezone(tz)
|
||||
else:
|
||||
limit_before = before
|
||||
|
||||
context['date'] = date(self.year, self.month, 1)
|
||||
context['before'] = before
|
||||
context['after'] = after
|
||||
@@ -593,7 +598,7 @@ class EventIndex(EventViewMixin, EventListMixin, CartMixin, TemplateView):
|
||||
ebd = defaultdict(list)
|
||||
add_subevents_for_days(
|
||||
filter_qs_by_attr(self.request.event.subevents_annotated(self.request.sales_channel.identifier).using(settings.DATABASE_REPLICA), self.request),
|
||||
before, after, ebd, set(), self.request.event,
|
||||
limit_before, after, ebd, set(), self.request.event,
|
||||
self.kwargs.get('cart_namespace'),
|
||||
voucher,
|
||||
)
|
||||
@@ -606,9 +611,22 @@ class EventIndex(EventViewMixin, EventListMixin, CartMixin, TemplateView):
|
||||
lambda: len(set(str(n) for n in self.request.event.subevents.order_by().values_list('name', flat=True).annotate(c=Count('*'))[:250])) != 1,
|
||||
timeout=120,
|
||||
)
|
||||
context['weeks'] = weeks_for_template(ebd, self.year, self.month)
|
||||
context['weeks'] = weeks_for_template(ebd, self.year, self.month, future_only=self.request.event.settings.event_calendar_future_only)
|
||||
context['months'] = [date(self.year, i + 1, 1) for i in range(12)]
|
||||
context['years'] = range(now().year - 2, now().year + 3)
|
||||
if self.request.event.settings.event_calendar_future_only:
|
||||
context['years'] = range(now().year, now().year + 3)
|
||||
else:
|
||||
context['years'] = range(now().year - 2, now().year + 3)
|
||||
|
||||
context['has_before'], context['has_after'] = has_before_after(
|
||||
Event.objects.none(),
|
||||
SubEvent.objects.filter(
|
||||
event=self.request.event,
|
||||
),
|
||||
before,
|
||||
after,
|
||||
future_only=self.request.event.settings.event_calendar_future_only
|
||||
)
|
||||
elif context['list_type'] == "week":
|
||||
self._set_week_year()
|
||||
tz = self.request.event.timezone
|
||||
@@ -620,6 +638,11 @@ class EventIndex(EventViewMixin, EventListMixin, CartMixin, TemplateView):
|
||||
week.sunday().year, week.sunday().month, week.sunday().day, 0, 0, 0, tzinfo=tz
|
||||
) + timedelta(days=1)
|
||||
|
||||
if self.request.event.settings.event_calendar_future_only:
|
||||
limit_before = now().astimezone(tz)
|
||||
else:
|
||||
limit_before = before
|
||||
|
||||
context['date'] = week.monday()
|
||||
context['before'] = before
|
||||
context['after'] = after
|
||||
@@ -627,7 +650,7 @@ class EventIndex(EventViewMixin, EventListMixin, CartMixin, TemplateView):
|
||||
ebd = defaultdict(list)
|
||||
add_subevents_for_days(
|
||||
filter_qs_by_attr(self.request.event.subevents_annotated(self.request.sales_channel.identifier).using(settings.DATABASE_REPLICA), self.request),
|
||||
before, after, ebd, set(), self.request.event,
|
||||
limit_before, after, ebd, set(), self.request.event,
|
||||
self.kwargs.get('cart_namespace'),
|
||||
voucher,
|
||||
)
|
||||
@@ -640,13 +663,15 @@ class EventIndex(EventViewMixin, EventListMixin, CartMixin, TemplateView):
|
||||
lambda: len(set(str(n) for n in self.request.event.subevents.order_by().values_list('name', flat=True).annotate(c=Count('*'))[:250])) != 1,
|
||||
timeout=120,
|
||||
)
|
||||
context['days'] = days_for_template(ebd, week)
|
||||
context['days'] = days_for_template(ebd, week, future_only=self.request.event.settings.event_calendar_future_only)
|
||||
years = (self.year - 1, self.year, self.year + 1)
|
||||
weeks = []
|
||||
for year in years:
|
||||
weeks += [
|
||||
(date_fromisocalendar(year, i + 1, 1), date_fromisocalendar(year, i + 1, 7))
|
||||
for i in range(53 if date(year, 12, 31).isocalendar()[1] == 53 else 52)
|
||||
if not self.request.event.settings.event_calendar_future_only or
|
||||
date_fromisocalendar(year, i + 1, 7) > now().astimezone(tz).replace(tzinfo=None)
|
||||
]
|
||||
context['weeks'] = [[w for w in weeks if w[0].year == year] for year in years]
|
||||
context['week_format'] = get_format('WEEK_FORMAT')
|
||||
@@ -655,6 +680,16 @@ class EventIndex(EventViewMixin, EventListMixin, CartMixin, TemplateView):
|
||||
context['short_month_day_format'] = get_format('SHORT_MONTH_DAY_FORMAT')
|
||||
if context['short_month_day_format'] == 'SHORT_MONTH_DAY_FORMAT':
|
||||
context['short_month_day_format'] = SHORT_MONTH_DAY_FORMAT
|
||||
|
||||
context['has_before'], context['has_after'] = has_before_after(
|
||||
Event.objects.none(),
|
||||
SubEvent.objects.filter(
|
||||
event=self.request.event,
|
||||
),
|
||||
before,
|
||||
after,
|
||||
future_only=self.request.event.settings.event_calendar_future_only
|
||||
)
|
||||
else:
|
||||
context['subevent_list'] = self.request.event.subevents_sorted(
|
||||
filter_qs_by_attr(self.request.event.subevents_annotated(self.request.sales_channel.identifier).using(settings.DATABASE_REPLICA), self.request)
|
||||
|
||||
@@ -401,11 +401,11 @@ class OrganizerIndex(OrganizerViewMixin, EventListMixin, ListView):
|
||||
return ctx
|
||||
|
||||
|
||||
def has_before_after(eventqs, subeventqs, before, after):
|
||||
def has_before_after(eventqs, subeventqs, before, after, future_only=False):
|
||||
eqs = eventqs.filter(is_public=True, live=True, has_subevents=False)
|
||||
sqs = subeventqs.filter(active=True, is_public=True)
|
||||
return (
|
||||
eqs.filter(Q(date_from__lte=before)).exists() or sqs.filter(Q(date_from__lte=before)).exists(),
|
||||
(not future_only or before > now()) and (eqs.filter(Q(date_from__lte=before)).exists() or sqs.filter(Q(date_from__lte=before)).exists()),
|
||||
eqs.filter(Q(date_to__gte=after) | Q(date_from__gte=after)).exists() or sqs.filter(Q(date_to__gte=after) | Q(date_from__gte=after)).exists()
|
||||
)
|
||||
|
||||
@@ -413,7 +413,6 @@ def has_before_after(eventqs, subeventqs, before, after):
|
||||
def add_events_for_days(request, baseqs, before, after, ebd, timezones):
|
||||
qs = baseqs.filter(is_public=True, live=True, has_subevents=False).filter(
|
||||
Q(Q(date_to__gte=before) & Q(date_from__lte=after)) |
|
||||
Q(Q(date_from__lte=after) & Q(date_to__gte=before)) |
|
||||
Q(Q(date_to__isnull=True) & Q(date_from__gte=before) & Q(date_from__lte=after))
|
||||
).order_by(
|
||||
'date_from'
|
||||
@@ -471,7 +470,6 @@ def add_events_for_days(request, baseqs, before, after, ebd, timezones):
|
||||
def add_subevents_for_days(qs, before, after, ebd, timezones, event=None, cart_namespace=None, voucher=None):
|
||||
qs = qs.filter(active=True, is_public=True).filter(
|
||||
Q(Q(date_to__gte=before) & Q(date_from__lte=after)) |
|
||||
Q(Q(date_from__lte=after) & Q(date_to__gte=before)) |
|
||||
Q(Q(date_to__isnull=True) & Q(date_from__gte=before) & Q(date_from__lte=after))
|
||||
).order_by(
|
||||
'date_from'
|
||||
@@ -562,7 +560,7 @@ def sort_ev(e):
|
||||
return e['time'] or time(0, 0, 0), str(e['event'].name)
|
||||
|
||||
|
||||
def days_for_template(ebd, week):
|
||||
def days_for_template(ebd, week, future_only=False):
|
||||
day_format = get_format('WEEK_DAY_FORMAT')
|
||||
if day_format == 'WEEK_DAY_FORMAT':
|
||||
day_format = 'SHORT_DATE_FORMAT'
|
||||
@@ -574,11 +572,13 @@ def days_for_template(ebd, week):
|
||||
'events': sorted(ebd.get(day), key=sort_ev) if day in ebd else []
|
||||
}
|
||||
for day in week.days()
|
||||
if not future_only or day > now().astimezone(get_current_timezone()).date()
|
||||
]
|
||||
|
||||
|
||||
def weeks_for_template(ebd, year, month):
|
||||
def weeks_for_template(ebd, year, month, future_only=False):
|
||||
calendar.setfirstweekday(0) # TODO: Configurable
|
||||
today = now().astimezone(get_current_timezone()).date()
|
||||
return [
|
||||
[
|
||||
{
|
||||
@@ -594,6 +594,9 @@ def weeks_for_template(ebd, year, month):
|
||||
for day in week
|
||||
]
|
||||
for week in calendar.monthcalendar(year, month)
|
||||
if not future_only or (
|
||||
any(day != 0 and date(year, month, day) > today for day in week)
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
|
||||
@@ -497,6 +497,10 @@ class WidgetAPIProductList(EventListMixin, View):
|
||||
tz = self.request.organizer.timezone
|
||||
before = datetime(self.year, self.month, 1, 0, 0, 0, tzinfo=tz) - timedelta(days=1)
|
||||
after = datetime(self.year, self.month, ndays, 0, 0, 0, tzinfo=tz) + timedelta(days=1)
|
||||
if hasattr(self.request, 'event') and self.request.event.settings.event_calendar_future_only:
|
||||
limit_before = min(after, now().astimezone(tz))
|
||||
else:
|
||||
limit_before = before
|
||||
|
||||
ebd = defaultdict(list)
|
||||
|
||||
@@ -507,7 +511,7 @@ class WidgetAPIProductList(EventListMixin, View):
|
||||
event__sales_channels__contains=self.request.sales_channel.identifier
|
||||
), self.request
|
||||
),
|
||||
before, after, ebd, set(), self.request.event,
|
||||
limit_before, after, ebd, set(), self.request.event,
|
||||
kwargs.get('cart_namespace')
|
||||
)
|
||||
else:
|
||||
@@ -519,7 +523,7 @@ class WidgetAPIProductList(EventListMixin, View):
|
||||
sales_channels__contains=self.request.sales_channel.identifier
|
||||
), self.request
|
||||
),
|
||||
before, after, ebd, timezones
|
||||
limit_before, after, ebd, timezones
|
||||
)
|
||||
add_subevents_for_days(filter_qs_by_attr(SubEvent.annotated(SubEvent.objects.filter(
|
||||
event__organizer=self.request.organizer,
|
||||
@@ -528,7 +532,7 @@ class WidgetAPIProductList(EventListMixin, View):
|
||||
event__sales_channels__contains=self.request.sales_channel.identifier
|
||||
).prefetch_related(
|
||||
'event___settings_objects', 'event__organizer___settings_objects'
|
||||
)), self.request), before, after, ebd, timezones)
|
||||
)), self.request), limit_before, after, ebd, timezones)
|
||||
|
||||
data['weeks'] = weeks_for_template(ebd, self.year, self.month)
|
||||
for w in data['weeks']:
|
||||
@@ -553,11 +557,16 @@ class WidgetAPIProductList(EventListMixin, View):
|
||||
week.sunday().year, week.sunday().month, week.sunday().day, 0, 0, 0, tzinfo=tz
|
||||
) + timedelta(days=1)
|
||||
|
||||
if hasattr(self.request, 'event') and self.request.event.settings.event_calendar_future_only:
|
||||
limit_before = now().astimezone(tz)
|
||||
else:
|
||||
limit_before = before
|
||||
|
||||
ebd = defaultdict(list)
|
||||
if hasattr(self.request, 'event'):
|
||||
add_subevents_for_days(
|
||||
filter_qs_by_attr(self.request.event.subevents_annotated('web'), self.request),
|
||||
before, after, ebd, set(), self.request.event,
|
||||
limit_before, after, ebd, set(), self.request.event,
|
||||
kwargs.get('cart_namespace')
|
||||
)
|
||||
else:
|
||||
@@ -565,7 +574,7 @@ class WidgetAPIProductList(EventListMixin, View):
|
||||
add_events_for_days(
|
||||
self.request,
|
||||
filter_qs_by_attr(Event.annotated(self.request.organizer.events, 'web'), self.request),
|
||||
before, after, ebd, timezones
|
||||
limit_before, after, ebd, timezones
|
||||
)
|
||||
add_subevents_for_days(filter_qs_by_attr(SubEvent.annotated(SubEvent.objects.filter(
|
||||
event__organizer=self.request.organizer,
|
||||
@@ -573,7 +582,7 @@ class WidgetAPIProductList(EventListMixin, View):
|
||||
event__live=True,
|
||||
).prefetch_related(
|
||||
'event___settings_objects', 'event__organizer___settings_objects'
|
||||
)), self.request), before, after, ebd, timezones)
|
||||
)), self.request), limit_before, after, ebd, timezones)
|
||||
|
||||
data['days'] = days_for_template(ebd, week)
|
||||
for d in data['days']:
|
||||
@@ -582,9 +591,8 @@ class WidgetAPIProductList(EventListMixin, View):
|
||||
offset = int(self.request.GET.get("offset", 0))
|
||||
limit = 50
|
||||
if hasattr(self.request, 'event'):
|
||||
evs = self.request.event.subevents_sorted(
|
||||
filter_qs_by_attr(self.request.event.subevents_annotated(self.request.sales_channel.identifier), self.request)
|
||||
)
|
||||
evs = filter_qs_by_attr(self.request.event.subevents_annotated(self.request.sales_channel.identifier), self.request)
|
||||
evs = self.request.event.subevents_sorted(evs)
|
||||
ordering = self.request.event.settings.get('frontpage_subevent_ordering', default='date_ascending', as_type=str)
|
||||
data['has_more_events'] = False
|
||||
if ordering in ("date_ascending", "date_descending"):
|
||||
|
||||
Reference in New Issue
Block a user