diff --git a/src/pretix/control/templates/pretixcontrol/event/index.html b/src/pretix/control/templates/pretixcontrol/event/index.html
index 08d578675c..b1a66199a4 100644
--- a/src/pretix/control/templates/pretixcontrol/event/index.html
+++ b/src/pretix/control/templates/pretixcontrol/event/index.html
@@ -110,13 +110,9 @@
{% endif %}
{% if not request.event.has_subevents or subevent %}
-
- {% include "pretixcontrol/event/fragment_timeline.html" %}
-
+ {% include "pretixcontrol/event/fragment_timeline.html" %}
{% endif %}
-
- {% include "pretixcontrol/event/fragment_info_box.html" %}
-
+ {% include "pretixcontrol/event/fragment_info_box.html" %}
{% for w in widgets %}
diff --git a/src/pretix/eventyay_common/context.py b/src/pretix/eventyay_common/context.py
index 0777589260..d6860a2419 100644
--- a/src/pretix/eventyay_common/context.py
+++ b/src/pretix/eventyay_common/context.py
@@ -1,98 +1,120 @@
-from importlib import import_module
+import logging
+from urllib.parse import urljoin
from django.conf import settings
from django.db.models import Q
-from django.urls import Resolver404, get_script_prefix, resolve, reverse
-from django.utils.translation import gettext_lazy as _
+from django.http import HttpRequest
+from django.urls import Resolver404, get_script_prefix, resolve
+from django_scopes import scope
from pretix.base.models.auth import StaffSession
from pretix.base.settings import GlobalSettingsObject
-from pretix.control.navigation import merge_in
-from pretix.control.signals import nav_global
+from pretix.eventyay_common.navigation import (
+ get_event_navigation, get_global_navigation,
+)
-SessionStore = import_module(settings.SESSION_ENGINE).SessionStore
+from ..helpers.plugin_enable import is_video_enabled
+from ..multidomain.urlreverse import get_event_domain
+from .views.event import EventCreatedFor
+logger = logging.getLogger(__name__)
-def contextprocessor(request):
- """
- Adds data to all template contexts
- """
- if not hasattr(request, '_eventyay_common_default_context'):
+
+def contextprocessor(request: HttpRequest):
+ if not hasattr(request, "_eventyay_common_default_context"):
request._eventyay_common_default_context = _default_context(request)
return request._eventyay_common_default_context
-def _default_context(request):
+def _default_context(request: HttpRequest):
try:
url = resolve(request.path_info)
except Resolver404:
return {}
- if not request.path.startswith(get_script_prefix() + 'common'):
+ if not request.path.startswith(f"{get_script_prefix()}common"):
return {}
ctx = {
- 'url_name': url.url_name,
- 'settings': settings,
- 'django_settings': settings,
- 'DEBUG': settings.DEBUG,
+ "url_name": url.url_name,
+ "settings": settings,
+ "django_settings": settings,
+ "DEBUG": settings.DEBUG,
+ "talk_hostname": settings.TALK_HOSTNAME,
}
- if getattr(request, 'event', None) and hasattr(request, 'organizer') and request.user.is_authenticated:
- ctx['nav_items'] = get_global_navigation(request)
-
- elif request.user.is_authenticated:
- ctx['nav_items'] = get_global_navigation(request)
gs = GlobalSettingsObject()
- ctx['global_settings'] = gs.settings
-
- if request.user.is_authenticated:
- ctx['staff_session'] = request.user.has_active_staff_session(request.session.session_key)
- ctx['staff_need_to_explain'] = (
- StaffSession.objects.filter(user=request.user, date_end__isnull=False).filter(
- Q(comment__isnull=True) | Q(comment="")
+ ctx["global_settings"] = gs.settings
+
+ if not request.user.is_authenticated:
+ return ctx
+
+ ctx["nav_items"] = get_global_navigation(request)
+ ctx["staff_session"] = request.user.has_active_staff_session(
+ request.session.session_key
+ )
+ ctx["staff_need_to_explain"] = (
+ StaffSession.objects.filter(user=request.user, date_end__isnull=False).filter(
+ Q(comment__isnull=True) | Q(comment="")
+ )
+ if request.user.is_staff and settings.PRETIX_ADMIN_AUDIT_COMMENTS
+ else StaffSession.objects.none()
+ )
+
+ # Verify if the request includes an event
+ event = getattr(request, "event", None)
+ if not event:
+ return ctx
+
+ ctx["talk_edit_url"] = urljoin(settings.TALK_HOSTNAME, f"orga/event/{event.slug}")
+ ctx["is_video_enabled"] = is_video_enabled(event)
+ ctx["is_talk_event_created"] = False
+ if (
+ event.settings.create_for == EventCreatedFor.BOTH.value
+ or event.settings.talk_schedule_public is not None
+ ):
+ ctx["is_talk_event_created"] = True
+
+ # Verify if the request includes an organizer
+ organizer = getattr(request, "organizer", None)
+ if not organizer:
+ return ctx
+
+ ctx["nav_items"] = get_event_navigation(request, event)
+ ctx["has_domain"] = get_event_domain(event, fallback=True) is not None
+ if not event.testmode:
+ with scope(organizer=organizer):
+ complain_testmode_orders = event.cache.get("complain_testmode_orders")
+ if complain_testmode_orders is None:
+ complain_testmode_orders = event.orders.filter(testmode=True).exists()
+ event.cache.set(
+ "complain_testmode_orders", complain_testmode_orders, 30
+ )
+ ctx["complain_testmode_orders"] = (
+ complain_testmode_orders
+ and request.user.has_event_permission(
+ organizer, event, "can_view_orders", request=request
)
- if request.user.is_staff and settings.PRETIX_ADMIN_AUDIT_COMMENTS else StaffSession.objects.none()
)
-
- ctx['talk_hostname'] = settings.TALK_HOSTNAME
+ else:
+ ctx["complain_testmode_orders"] = False
+
+ if not event.live and ctx["has_domain"]:
+ child_sess_key = f"child_session_{event.pk}"
+ child_sess = request.session.get(child_sess_key)
+
+ if not child_sess:
+ request.session[child_sess_key] = request.session.session_key
+ else:
+ ctx["new_session"] = child_sess
+ request.session["event_access"] = True
+
+ if request.GET.get("subevent", ""):
+ subevent_id = request.GET.get("subevent", "").strip()
+ try:
+ pk = int(subevent_id)
+ # Do not use .get() for lazy evaluation
+ ctx["selected_subevents"] = event.subevents.filter(pk=pk)
+ except ValueError as e:
+ logger.error("Error parsing subevent ID: %s", e)
return ctx
-
-
-def get_global_navigation(request):
- url = request.resolver_match
- if not url:
- return []
- nav = [
- {
- 'label': _('Dashboard'),
- 'url': reverse('eventyay_common:dashboard'),
- 'active': (url.url_name == 'dashboard'),
- 'icon': 'dashboard',
- },
- {
- 'label': _('My Events'),
- 'url': reverse('eventyay_common:events'),
- 'active': 'events' in url.url_name,
- 'icon': 'calendar',
- },
- {
- 'label': _('Organizers'),
- 'url': reverse('eventyay_common:organizers'),
- 'active': 'organizers' in url.url_name,
- 'icon': 'group',
- },
- {
- 'label': _('Account'),
- 'url': reverse('eventyay_common:account'),
- 'active': 'account' in url.url_name,
- 'icon': 'user',
- }
-
- ]
-
- merge_in(nav, sorted(
- sum((list(a[1]) for a in nav_global.send(request, request=request)), []),
- key=lambda r: (1 if r.get('parent') else 0, r['label'])
- ))
- return nav
diff --git a/src/pretix/eventyay_common/navigation.py b/src/pretix/eventyay_common/navigation.py
new file mode 100644
index 0000000000..80b4f9abe8
--- /dev/null
+++ b/src/pretix/eventyay_common/navigation.py
@@ -0,0 +1,109 @@
+from typing import Any, Dict, List
+
+from django.http import HttpRequest
+from django.urls import reverse
+from django.utils.translation import gettext_lazy as _
+
+from pretix.base.models import Event
+from pretix.control.navigation import merge_in
+from pretix.control.signals import nav_event, nav_global
+
+
+def get_global_navigation(request: HttpRequest) -> List[Dict[str, Any]]:
+ """Generate navigation items for global."""
+ url = request.resolver_match
+ if not url:
+ return []
+ nav = [
+ {
+ "label": _("Dashboard"),
+ "url": reverse("eventyay_common:dashboard"),
+ "active": (url.url_name == "dashboard"),
+ "icon": "dashboard",
+ },
+ {
+ "label": _("My Events"),
+ "url": reverse("eventyay_common:events"),
+ "active": "events" in url.url_name,
+ "icon": "calendar",
+ },
+ {
+ "label": _("Organizers"),
+ "url": reverse("eventyay_common:organizers"),
+ "active": "organizers" in url.url_name,
+ "icon": "group",
+ },
+ {
+ "label": _("Account"),
+ "url": reverse("eventyay_common:account"),
+ "active": "account" in url.url_name,
+ "icon": "user",
+ },
+ ]
+
+ # Merge plugin-provided navigation items
+ plugin_responses = nav_global.send(request, request=request)
+ plugin_nav_items = []
+ for receiver, response in plugin_responses:
+ if response:
+ plugin_nav_items.extend(response)
+
+ # Sort navigation items, prioritizing non-parent items and alphabetically
+ sorted_plugin_items = sorted(
+ plugin_nav_items, key=lambda r: (1 if r.get("parent") else 0, r["label"])
+ )
+
+ # Merge plugin items into default navigation
+ merge_in(nav, sorted_plugin_items)
+
+ return nav
+
+
+def get_event_navigation(request: HttpRequest, event: Event) -> List[Dict[str, Any]]:
+ """Generate navigation items for an event."""
+ url = request.resolver_match
+ if not url:
+ return []
+ nav = [
+ {
+ "label": _("Dashboard"),
+ "url": reverse(
+ "eventyay_common:event.index",
+ kwargs={
+ "event": event.slug,
+ "organizer": event.organizer.slug,
+ },
+ ),
+ "active": (url.url_name == "event.index"),
+ "icon": "dashboard",
+ },
+ {
+ "label": _("Settings"),
+ "url": reverse(
+ "eventyay_common:event.update",
+ kwargs={
+ "event": event.slug,
+ "organizer": event.organizer.slug,
+ },
+ ),
+ "active": (url.url_name == "event.update"),
+ "icon": "wrench",
+ },
+ ]
+
+ # Merge plugin-provided navigation items
+ plugin_responses = nav_event.send(event, request=request)
+ plugin_nav_items = []
+ for receiver, response in plugin_responses:
+ if response:
+ plugin_nav_items.extend(response)
+
+ # Sort navigation items, prioritizing non-parent items and alphabetically
+ sorted_plugin_items = sorted(
+ plugin_nav_items, key=lambda r: (1 if r.get("parent") else 0, r["label"])
+ )
+
+ # Merge plugin items into default navigation
+ merge_in(nav, sorted_plugin_items)
+
+ return nav
diff --git a/src/pretix/eventyay_common/templates/eventyay_common/base.html b/src/pretix/eventyay_common/templates/eventyay_common/base.html
index 669eea4b2d..f85839f212 100644
--- a/src/pretix/eventyay_common/templates/eventyay_common/base.html
+++ b/src/pretix/eventyay_common/templates/eventyay_common/base.html
@@ -144,6 +144,43 @@