diff --git a/README.rst b/README.rst index 2dec55b..599b841 100644 --- a/README.rst +++ b/README.rst @@ -62,6 +62,21 @@ Development setup 6. Restart your local pretix server. You can now use the plugin from this repository for your events by enabling it in the 'plugins' tab in the settings. + +Changelog +--------- + +1.1.0 +~~~~~ + +- Add subevent-level "Language" field and use it to emit ```` per subevent (defaults to ``none``). + +1.0.0 +~~~~~ + +- Initial release + + License ------- diff --git a/pretix_congressschedule/__init__.py b/pretix_congressschedule/__init__.py index 5becc17..6849410 100644 --- a/pretix_congressschedule/__init__.py +++ b/pretix_congressschedule/__init__.py @@ -1 +1 @@ -__version__ = "1.0.0" +__version__ = "1.1.0" diff --git a/pretix_congressschedule/api.py b/pretix_congressschedule/api.py index 73d533c..eff2d24 100644 --- a/pretix_congressschedule/api.py +++ b/pretix_congressschedule/api.py @@ -1,6 +1,10 @@ from django.http import HttpResponse from rest_framework import views from pretix.base.models import Event, SubEvent +try: + from pretix.base.models import SubEventMetaValue +except Exception: # pragma: no cover + SubEventMetaValue = None import xml.etree.ElementTree as ET from collections import defaultdict from datetime import timedelta @@ -182,10 +186,31 @@ class CongressScheduleView(views.APIView): # track – use room name as a simple track assignment ET.SubElement(ev_el, 'track').text = slugify(room_name) or 'general' - # Optional elements: keep minimal but include language if available - lang = getattr(ev.settings, 'locale', None) - if lang: - ET.SubElement(ev_el, 'language').text = str(lang) + # Optional elements: language – per subevent via SubEventMetaValue + def _get_lang(subevent: SubEvent) -> str: + if SubEventMetaValue is not None: + try: + v = ( + SubEventMetaValue.objects + .filter(subevent=subevent, key='congressschedule_language') + .values_list('value', flat=True) + .first() + ) + return (v or 'none').strip() or 'none' + except Exception: + pass + # Fallbacks for environments without pretix DB access + md = getattr(subevent, 'meta_data', None) or {} + if isinstance(md, dict) and 'congressschedule_language' in md: + return (md.get('congressschedule_language') or 'none') + se_settings = getattr(subevent, 'settings', None) + try: + return se_settings.get('congressschedule_language', 'none') if se_settings is not None else 'none' + except Exception: + return 'none' + + lang = _get_lang(se) + ET.SubElement(ev_el, 'language').text = str(lang or 'none') # Leave optional complex children (persons, recording, links, attachments) empty for now @@ -257,11 +282,13 @@ class HackertoursMarkdownView(views.APIView): title = _localize(se.name) tmin = to_minutes(se.date_from) hhmm = se.date_from.strftime('%H:%M') - try: - se_settings = getattr(se, 'settings', None) - lang = se_settings.get('congressschedule_language', 'de') if se_settings is not None else 'none' - except Exception: - lang = 'none' + v = ( + SubEventMetaValue.objects + .filter(subevent=se, property__name='congressschedule_language') + .values_list('value', flat=True) + .first() + ) + lang = (v or 'deen').strip() or 'deen' link = f"./{slugify(title)}/" md_item = f"{hhmm} [{title}]({link}) {{< lang {lang} >}}" day_slots[d][tmin].append(md_item) diff --git a/pretix_congressschedule/signals.py b/pretix_congressschedule/signals.py index e69de29..dd5c0e5 100644 --- a/pretix_congressschedule/signals.py +++ b/pretix_congressschedule/signals.py @@ -0,0 +1,87 @@ +from django import forms +from django.utils.translation import gettext_lazy as _ +try: + # Available in pretix runtime + from pretix.base.models import SubEventMetaValue +except Exception: # pragma: no cover - during docs build or without pretix + SubEventMetaValue = None + + +class SubEventLanguageForm(forms.Form): + language = forms.ChoiceField( + label=_("Language"), + required=False, + help_text=_("Select the language for this tour."), + choices=[ + ('deen', _("Bilingual")), + ('de', _("German")), + ('en', _("English")), + ], + ) + + def __init__(self, *args, **kwargs): + self.event = kwargs.pop('event') + self.subevent = kwargs.pop('subevent', None) + super().__init__(*args, **kwargs) + # Pre-fill from subevent meta if available + if self.subevent: + val = ( + SubEventMetaValue.objects + .filter(subevent=self.subevent, property__name='congressschedule_language') + .values_list('value', flat=True) + .first() + ) + self.fields['language'].initial = val or '' + elif self.subevent and hasattr(self.subevent, 'settings'): + # Fallback (older pretix): might be event-wide, keep as last resort + self.fields['language'].initial = "deen" + + @property + def title(self): + return _("Language") + + def save(self): + if not self.subevent: + return + val = (self.cleaned_data.get('language') or '').strip() or 'none' + # Persist as real subevent meta value so it's scoped per subevent + from pretix.base.models import EventMetaProperty + + property_obj, _ = EventMetaProperty.objects.get_or_create( + name='congressschedule_language', + defaults={'default': '', 'organizer': self.event.organizer} + ) + SubEventMetaValue.objects.update_or_create( + subevent=self.subevent, + property=property_obj, + defaults={'value': val}, + ) + + +def subevent_forms(sender, request, subevent, **kwargs): + # Provide our additional subevent form + import logging + logger = logging.getLogger(__name__) + logger.debug("Providing congressschedule subevent form for event %s, subevent %s", sender.slug, getattr(subevent, 'name', 'no-subevent')) + form = SubEventLanguageForm( + data=request.POST if request.method == 'POST' else None, + event=sender, + subevent=subevent, + prefix='congressschedule', + ) + return form + + +def connect_signals(): + # Connect to pretix.control.signals.subevent_forms at import time + try: + from pretix.control import signals as control_signals + + control_signals.subevent_forms.connect(subevent_forms, dispatch_uid='pretix_congressschedule_subevent_language') + except Exception: + # Pretix not fully loaded in some contexts (e.g., docs build) + pass + + +# Connect immediately when module is imported via AppConfig.ready() +connect_signals()