Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Eventyay Common: Create an event dashboard #505

Open
wants to merge 23 commits into
base: development
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 15 additions & 6 deletions src/pretix/base/models/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@
from ..settings import settings_hierarkey
from .organizer import Organizer, OrganizerBillingModel, Team

TALK_HOSTNAME = settings.TALK_HOSTNAME


class EventMixin:

Expand Down Expand Up @@ -1134,20 +1136,27 @@ def has_paid_things(self):

@property
def talk_schedule_url(self):
talk_host = settings.TALK_HOSTNAME
url = urljoin(talk_host, f"{self.slug}/schedule")
url = urljoin(TALK_HOSTNAME, f"{self.slug}/schedule")
return url

@property
def talk_session_url(self):
talk_host = settings.TALK_HOSTNAME
url = urljoin(talk_host, f"{self.slug}/talk")
url = urljoin(TALK_HOSTNAME, f"{self.slug}/talk")
return url

@property
def talk_speaker_url(self):
talk_host = settings.TALK_HOSTNAME
url = urljoin(talk_host, f"{self.slug}/speaker")
url = urljoin(TALK_HOSTNAME, f"{self.slug}/speaker")
return url

@property
def talk_dashboard_url(self):
url = urljoin(TALK_HOSTNAME, f"orga/event/{self.slug}")
return url

@property
def talk_settings_url(self):
url = urljoin(TALK_HOSTNAME, f"orga/event/{self.slug}/settings")
return url

@cached_property
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
{% load i18n %}
<div class="navigation-button">
{% if request.organizer and request.event %}
<a href='{% url "eventyay_common:event.update" organizer=request.organizer.slug event=request.event.slug %}' class="header-nav btn btn-outline-success">
<a href='{% url "eventyay_common:event.index" organizer=request.organizer.slug event=request.event.slug %}' class="header-nav btn btn-outline-success">
<i class="fa fa-home"></i> {% trans "Home" %}
</a>
<a href="#" class="header-nav btn btn-outline-success active">
<i class="fa fa-ticket"></i> {% trans "Tickets" %}
</a>
{% if is_talk_event_created %}
<a href="{{ talk_edit_url }}" class="header-nav btn btn-outline-success">
<a href="{{ request.event.talk_dashboard_url }}" class="header-nav btn btn-outline-success">
<i class="fa fa-group"></i> {% trans "Talk" %}
</a>
{% endif %}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ <h3 class="panel-title">
{% trans "Event organizer" %}
</h3>
</div>
<div class="panel-body timeline">
<div class="panel-body timeline overide-panel-body">
<div class="info-row">
{% trans "This event is organized by" %} <strong><a href="{% url 'control:organizer' request.organizer.slug %}">{{ request.organizer }}</a></strong>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ <h3 class="panel-title">
{% trans "Your timeline" %}
</h3>
</div>
<div class="panel-body timeline">
<div class="panel-body timeline overide-panel-body">
{% regroup timeline by date as tl_list %}
{% for day in tl_list %}
<div class="row {% if day.grouper < today %}text-muted{% endif %}">
Expand Down
164 changes: 93 additions & 71 deletions src/pretix/eventyay_common/context.py
Original file line number Diff line number Diff line change
@@ -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
109 changes: 109 additions & 0 deletions src/pretix/eventyay_common/navigation.py
Original file line number Diff line number Diff line change
@@ -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
Loading
Loading