class EventWizardDisplayForm(forms.Form): show_on_dashboard = forms.BooleanField( initial=True, required=False, label=_('Show on dashboard'), help_text=_( 'Show this event on this website\'s dashboard, once it is public?' ), ) primary_color = forms.CharField( max_length=7, label=_('Main event colour'), help_text= _('Provide a hex value like #00ff00 if you want to style pretalx in your event\'s colour scheme.' ), required=False, ) logo = ExtensionFileField( required=False, extension_whitelist=IMAGE_EXTENSIONS, label=_('Logo'), help_text= _('If you provide a logo image, we will by default not show your events name and date in the page header. ' 'We will show your logo in its full size if possible, scaled down to the full header width otherwise.' ), ) display_header_pattern = forms.ChoiceField( label=_('Frontpage header pattern'), help_text= _('Choose how the frontpage header banner will be styled. Pattern source: <a href="http://www.heropatterns.com/">heropatterns.com</a>, CC BY 4.0.' ), choices=( ('', _('Plain')), ('pcb', _('Circuits')), ('bubbles', _('Circles')), ('signal', _('Signal')), ('topo', _('Topography')), ('graph', _('Graph Paper')), ), required=False, widget=HeaderSelect, ) def __init__(self, *args, user=None, locales=None, organiser=None, **kwargs): super().__init__(*args, **kwargs) self.fields['primary_color'].widget.attrs['class'] = 'colorpickerfield'
class EventWizardDisplayForm(forms.Form): show_on_dashboard = forms.BooleanField( initial=True, required=False, label=_("Show on dashboard"), help_text=_( "Show this event on this website's dashboard, once it is public?"), ) primary_color = forms.CharField( max_length=7, label=_("Main event colour"), help_text= _("Provide a hex value like #00ff00 if you want to style pretalx in your event's colour scheme." ), required=False, ) logo = ExtensionFileField( required=False, extensions=IMAGE_EXTENSIONS, label=_("Logo"), help_text= _("If you provide a logo image, we will by default not show your event's name and date in the page header. " "We will show your logo in its full size if possible, scaled down to the full header width otherwise." ), ) display_header_pattern = forms.ChoiceField( label=_("Frontpage header pattern"), help_text= _('Choose how the frontpage header banner will be styled. Pattern source: <a href="http://www.heropatterns.com/">heropatterns.com</a>, CC BY 4.0.' ), choices=( ("", _("Plain")), ("pcb", _("Circuits")), ("bubbles", _("Circles")), ("signal", _("Signal")), ("topo", _("Topography")), ("graph", _("Graph Paper")), ), required=False, widget=HeaderSelect, ) def __init__(self, *args, user=None, locales=None, organiser=None, **kwargs): super().__init__(*args, **kwargs) self.fields["primary_color"].widget.attrs["class"] = "colorpickerfield"
class EventForm(ReadOnlyFlag, I18nModelForm): locales = forms.MultipleChoiceField( label=_("Active languages"), choices=settings.LANGUAGES, widget=MultipleLanguagesWidget, help_text=_( "Users will be able to use pretalx in these languages, and you will be able to provide all texts in these" " languages. If you don't provide a text in the language a user selects, it will be shown in your event's" " default language instead." ), ) logo = ExtensionFileField( required=False, extensions=IMAGE_EXTENSIONS, label=_("Header image"), help_text=_( "If you provide a header image, it will be displayed instead of your event's color and/or header pattern " "on top of all event pages. It will be center-aligned, so when the window shrinks, the center parts will " "continue to be displayed, and not stretched." ), ) header_image = ExtensionFileField( required=False, extensions=IMAGE_EXTENSIONS, label=_("Header image"), help_text=_( "If you provide a logo image, we will by default not show your event's name and date in the page header. " "We will show your logo in its full size if possible, scaled down to the full header width otherwise." ), ) custom_css_text = forms.CharField( required=False, widget=forms.Textarea(), label="", help_text=_("You can type in your CSS instead of uploading it, too."), ) def __init__(self, *args, **kwargs): self.is_administrator = kwargs.pop("is_administrator", False) super().__init__(*args, **kwargs) self.initial["locales"] = self.instance.locale_array.split(",") year = str(now().year) self.fields["name"].widget.attrs["placeholder"] = ( _("The name of your conference, e.g. My Conference") + " " + year ) self.fields["slug"].help_text = _( "Please contact your administrator if you need to change the short name of your event." ) self.fields["primary_color"].widget.attrs["placeholder"] = _( "A color hex value, e.g. #ab01de" ) self.fields["primary_color"].widget.attrs["class"] = "colorpickerfield" self.fields["slug"].disabled = True self.fields["date_to"].help_text = _( "Any talks you have scheduled already will be moved if you change the event dates. You will have to release a new schedule version to notify all speakers." ) def clean_custom_css(self): if self.cleaned_data.get("custom_css") or self.files.get("custom_css"): css = self.cleaned_data["custom_css"] or self.files["custom_css"] if self.is_administrator: return css try: validate_css(css.read()) return css except IsADirectoryError: self.instance.custom_css = None self.instance.save(update_fields=["custom_css"]) else: self.instance.custom_css = None self.instance.save(update_fields=["custom_css"]) return None def clean_custom_css_text(self): css = self.cleaned_data.get("custom_css_text").strip() if not css or self.is_administrator: return css validate_css(css) return css def clean(self): data = super().clean() if data.get("locale") not in data.get("locales", []): error = forms.ValidationError( _("Your default language needs to be one of your active languages."), ) self.add_error("locale", error) return data def save(self, *args, **kwargs): self.instance.locale_array = ",".join(self.cleaned_data["locales"]) if any(key in self.changed_data for key in ("date_from", "date_to")): self.change_dates() result = super().save(*args, **kwargs) css_text = self.cleaned_data["custom_css_text"] if css_text: self.instance.custom_css.save( self.instance.slug + ".css", ContentFile(css_text) ) return result def change_dates(self): """Changes dates of current WIP slots, or deschedules them.""" from pretalx.schedule.models import Availability old_instance = Event.objects.get(pk=self.instance.pk) if not self.instance.wip_schedule.talks.filter(start__isnull=False).exists(): return new_date_from = self.cleaned_data["date_from"] new_date_to = self.cleaned_data["date_to"] start_delta = new_date_from - old_instance.date_from end_delta = new_date_to - old_instance.date_to shortened = (new_date_to - new_date_from) < ( old_instance.date_to - old_instance.date_from ) if start_delta and end_delta: # The event was moved, and we will move all talks with it. for key in ("start", "end"): filt = {f"{key}__isnull": False, "event": self.instance.event} update = {key: F(key) + start_delta} self.instance.wip_schedule.talks.filter(**filt).update(**update) Availability.objects.filter(event=self.instance).filter(**filt).update( **update ) # Otherwise, the event got longer, no need to do anything. # We *could* move all talks towards the new start date, but I'm # not convinced that this is the actual use case. # I think it's more likely that people add a new day to the start. if shortened: # The event was shortened, de-schedule all talks outside the range self.instance.wip_schedule.talks.filter( Q(start__date__gt=new_date_to) | Q(start__date__lt=new_date_from), ).update(start=None, end=None, room=None) Availability.objects.filter( Q(end__date__gt=new_date_to) | Q(start__date__lt=new_date_from), event=self.instance.event, ).delete() class Meta: model = Event fields = [ "name", "slug", "date_from", "date_to", "timezone", "email", "locale", "primary_color", "custom_css", "logo", "header_image", "landing_page_text", ] widgets = { "date_from": forms.DateInput(attrs={"class": "datepickerfield"}), "date_to": forms.DateInput( attrs={"class": "datepickerfield", "data-date-after": "#id_date_from"} ), }
class EventForm(ReadOnlyFlag, I18nModelForm): locales = forms.MultipleChoiceField( label=_('Active languages'), choices=settings.LANGUAGES, widget=MultipleLanguagesWidget, ) logo = ExtensionFileField( required=False, extension_whitelist=IMAGE_EXTENSIONS, label=_('Logo'), help_text=_( 'If you provide a logo image, we will by default not show your event\'s name and date in the page header. ' 'We will show your logo in its full size if possible, scaled down to the full header width otherwise.' ), ) def __init__(self, *args, **kwargs): self.is_administrator = kwargs.pop('is_administrator', False) super().__init__(*args, **kwargs) self.initial['locales'] = self.instance.locale_array.split(',') year = str(now().year) self.fields['name'].widget.attrs['placeholder'] = ( _('The name of your conference, e.g. My Conference') + ' ' + year ) self.fields['slug'].widget.attrs['placeholder'] = ( _('A short version of your conference name, e.g. mycon') + year[2:] ) self.fields['primary_color'].widget.attrs['placeholder'] = _( 'A color hex value, e.g. #ab01de' ) self.fields['primary_color'].widget.attrs['class'] = 'colorpickerfield' self.fields['slug'].disabled = True def clean_custom_css(self): if self.cleaned_data.get('custom_css') or self.files.get('custom_css'): css = self.cleaned_data['custom_css'] or self.files['custom_css'] if self.is_administrator: return css try: validate_css(css.read()) return css except IsADirectoryError: self.instance.custom_css = None self.instance.save(update_fields=['custom_css']) else: self.instance.custom_css = None self.instance.save(update_fields=['custom_css']) return None def clean(self): data = super().clean() if data.get('locale') not in data.get('locales', []): raise forms.ValidationError( _('Your default language needs to be one of your active languages.') ) if not data.get('email'): raise forms.ValidationError( _( 'Please provide a contact address – your speakers and participants should be able to reach you easily.' ) ) return data def save(self, *args, **kwargs): self.instance.locale_array = ','.join(self.cleaned_data['locales']) return super().save(*args, **kwargs) class Meta: model = Event fields = [ 'name', 'slug', 'date_from', 'date_to', 'timezone', 'email', 'locale', 'primary_color', 'custom_css', 'logo', 'landing_page_text', ] widgets = { 'date_from': forms.DateInput(attrs={'class': 'datepickerfield'}), 'date_to': forms.DateInput( attrs={'class': 'datepickerfield', 'data-date-after': '#id_date_from'} ), }
class EventForm(ReadOnlyFlag, I18nModelForm): locales = forms.MultipleChoiceField( label=_("Active languages"), choices=settings.LANGUAGES, widget=MultipleLanguagesWidget, help_text=_( "Users will be able to use pretalx in these languages, and you will be able to provide all texts in these" " languages. If you don't provide a text in the language a user selects, it will be shown in your event's" " default language instead." ), ) logo = ExtensionFileField( required=False, extensions=IMAGE_EXTENSIONS, label=_("Header image"), help_text=_( "If you provide a header image, it will be displayed instead of your event's color and/or header pattern " "on top of all event pages. It will be center-aligned, so when the window shrinks, the center parts will " "continue to be displayed, and not stretched." ), ) header_image = ExtensionFileField( required=False, extensions=IMAGE_EXTENSIONS, label=_("Header image"), help_text=_( "If you provide a logo image, we will by default not show your event's name and date in the page header. " "We will show your logo in its full size if possible, scaled down to the full header width otherwise." ), ) custom_css_text = forms.CharField( required=False, widget=forms.Textarea(), label="", help_text=_("You can type in your CSS instead of uploading it, too."), ) def __init__(self, *args, **kwargs): self.is_administrator = kwargs.pop("is_administrator", False) super().__init__(*args, **kwargs) self.initial["locales"] = self.instance.locale_array.split(",") year = str(now().year) self.fields["name"].widget.attrs["placeholder"] = ( _("The name of your conference, e.g. My Conference") + " " + year ) self.fields["slug"].help_text = _( "Please contact your administrator if you need to change the short name of your event." ) self.fields["primary_color"].widget.attrs["placeholder"] = _( "A color hex value, e.g. #ab01de" ) self.fields["primary_color"].widget.attrs["class"] = "colorpickerfield" self.fields["slug"].disabled = True def clean_custom_css(self): if self.cleaned_data.get("custom_css") or self.files.get("custom_css"): css = self.cleaned_data["custom_css"] or self.files["custom_css"] if self.is_administrator: return css try: validate_css(css.read()) return css except IsADirectoryError: self.instance.custom_css = None self.instance.save(update_fields=["custom_css"]) else: self.instance.custom_css = None self.instance.save(update_fields=["custom_css"]) return None def clean_custom_css_text(self): css = self.cleaned_data.get("custom_css_text").strip() if not css or self.is_administrator: return css validate_css(css) return css def clean(self): data = super().clean() if data.get("locale") not in data.get("locales", []): error = forms.ValidationError( _("Your default language needs to be one of your active languages."), ) self.add_error("locale", error) return data def save(self, *args, **kwargs): self.instance.locale_array = ",".join(self.cleaned_data["locales"]) result = super().save(*args, **kwargs) css_text = self.cleaned_data["custom_css_text"] if css_text: self.instance.custom_css.save( self.instance.slug + ".css", ContentFile(css_text) ) return result class Meta: model = Event fields = [ "name", "slug", "date_from", "date_to", "timezone", "email", "locale", "primary_color", "custom_css", "logo", "header_image", "landing_page_text", ] widgets = { "date_from": forms.DateInput(attrs={"class": "datepickerfield"}), "date_to": forms.DateInput( attrs={"class": "datepickerfield", "data-date-after": "#id_date_from"} ), }
class EventForm(ReadOnlyFlag, I18nModelForm): locales = forms.MultipleChoiceField( label=_('Active languages'), choices=settings.LANGUAGES, widget=MultipleLanguagesWidget, ) logo = ExtensionFileField( required=False, extension_whitelist=IMAGE_EXTENSIONS, label=_('Header image'), help_text= _('If you provide a header image, it will be displayed instead of your event\'s color and/or header pattern ' 'on top of all event pages. It will be center-aligned, so when the window shrinks, the center parts will ' 'continue to be displayed, and not stretched.'), ) header_image = ExtensionFileField( required=False, extension_whitelist=IMAGE_EXTENSIONS, label=_('Header image'), help_text= _('If you provide a logo image, we will by default not show your event\'s name and date in the page header. ' 'We will show your logo in its full size if possible, scaled down to the full header width otherwise.' ), ) custom_css_text = forms.CharField( required=False, widget=forms.Textarea(), label='', help_text=_('You can type in your CSS instead of uploading it, too.')) def __init__(self, *args, **kwargs): self.is_administrator = kwargs.pop('is_administrator', False) super().__init__(*args, **kwargs) self.initial['locales'] = self.instance.locale_array.split(',') year = str(now().year) self.fields['name'].widget.attrs['placeholder'] = ( _('The name of your conference, e.g. My Conference') + ' ' + year) self.fields['slug'].widget.attrs['placeholder'] = ( _('A short version of your conference name, e.g. mycon') + year[2:]) self.fields['primary_color'].widget.attrs['placeholder'] = _( 'A color hex value, e.g. #ab01de') self.fields['primary_color'].widget.attrs['class'] = 'colorpickerfield' self.fields['slug'].disabled = True def clean_custom_css(self): if self.cleaned_data.get('custom_css') or self.files.get('custom_css'): css = self.cleaned_data['custom_css'] or self.files['custom_css'] if self.is_administrator: return css try: validate_css(css.read()) return css except IsADirectoryError: self.instance.custom_css = None self.instance.save(update_fields=['custom_css']) else: self.instance.custom_css = None self.instance.save(update_fields=['custom_css']) return None def clean_custom_css_text(self): css = self.cleaned_data.get('custom_css_text').strip() if not css or self.is_administrator: return css validate_css(css) return css def clean(self): data = super().clean() if data.get('locale') not in data.get('locales', []): error = forms.ValidationError( _('Your default language needs to be one of your active languages.' ), ) self.add_error('locale', error) return data def save(self, *args, **kwargs): self.instance.locale_array = ','.join(self.cleaned_data['locales']) result = super().save(*args, **kwargs) css_text = self.cleaned_data['custom_css_text'] if css_text: self.instance.custom_css.save(self.instance.slug + '.css', ContentFile(css_text)) return result class Meta: model = Event fields = [ 'name', 'slug', 'date_from', 'date_to', 'timezone', 'email', 'locale', 'primary_color', 'custom_css', 'logo', 'header_image', 'landing_page_text', ] widgets = { 'date_from': forms.DateInput(attrs={'class': 'datepickerfield'}), 'date_to': forms.DateInput(attrs={ 'class': 'datepickerfield', 'data-date-after': '#id_date_from' }), }
class InfoForm(CfPFormMixin, RequestRequire, PublicContent, forms.ModelForm): additional_speaker = forms.EmailField( label=_("Additional Speaker"), help_text=_( "If you have a co-speaker, please add their email address here, and we will invite them to create an account. If you have more than one co-speaker, you can add more speakers after finishing the proposal process." ), required=False, ) image = ExtensionFileField( required=False, extensions=IMAGE_EXTENSIONS, label=_("Session image"), help_text=_("Use this if you want an illustration to go with your proposal."), ) def __init__(self, event, **kwargs): self.event = event self.readonly = kwargs.pop("readonly", False) self.access_code = kwargs.pop("access_code", None) instance = kwargs.get("instance") initial = kwargs.pop("initial", {}) or {} if not instance or not instance.submission_type: initial["submission_type"] = ( getattr(self.access_code, "submission_type", None) or initial.get("submission_type") or self.event.cfp.default_type ) if not instance and self.access_code: initial["track"] = self.access_code.track if not instance or not instance.content_locale: initial["content_locale"] = self.event.locale super().__init__(initial=initial, **kwargs) self.fields["title"].label = _("Proposal title") if "abstract" in self.fields: self.fields["abstract"].widget.attrs["rows"] = 2 if instance and instance.pk: self.fields.pop("additional_speaker") self._set_track(instance=instance) self._set_submission_types(instance=instance) self._set_locales() self._set_slot_count(instance=instance) if self.readonly: for f in self.fields.values(): f.disabled = True def _set_track(self, instance=None): if "track" in self.fields: if ( not self.event.settings.use_tracks or instance and instance.state != SubmissionStates.SUBMITTED ): self.fields.pop("track") return access_code = self.access_code or getattr(instance, "access_code", None) if not access_code or not access_code.track: self.fields["track"].queryset = self.event.tracks.filter( requires_access_code=False ) else: self.fields["track"].queryset = self.event.tracks.filter( pk=access_code.track.pk ) if len(self.fields["track"].queryset) == 1: self.fields["track"].initial = self.fields["track"].queryset.first() self.fields["track"].widget = forms.HiddenInput() def _set_submission_types(self, instance=None): _now = now() if ( instance and instance.pk and ( instance.state != SubmissionStates.SUBMITTED or not self.event.cfp.is_open ) ): self.fields[ "submission_type" ].queryset = self.event.submission_types.filter( pk=instance.submission_type_id ) self.fields["submission_type"].disabled = True return access_code = self.access_code or getattr(instance, "access_code", None) if access_code and not access_code.submission_type: pks = set(self.event.submission_types.values_list("pk", flat=True)) elif access_code: pks = {access_code.submission_type.pk} else: queryset = self.event.submission_types.filter(requires_access_code=False) if ( not self.event.cfp.deadline or self.event.cfp.deadline >= _now ): # No global deadline or still open types = queryset.exclude(deadline__lt=_now) else: types = queryset.filter(deadline__gte=_now) pks = set(types.values_list("pk", flat=True)) if instance and instance.pk: pks |= {instance.submission_type.pk} self.fields["submission_type"].queryset = self.event.submission_types.filter( pk__in=pks ) if len(pks) == 1: self.fields["submission_type"].initial = self.event.submission_types.get( pk=pks.pop() ) self.fields["submission_type"].widget = forms.HiddenInput() def _set_locales(self): if len(self.event.locales) == 1: self.fields["content_locale"].initial = self.event.locales[0] self.fields["content_locale"].widget = forms.HiddenInput() else: locale_names = dict(settings.LANGUAGES) self.fields["content_locale"].choices = [ (a, locale_names[a]) for a in self.event.locales ] def _set_slot_count(self, instance=None): if not self.event.settings.present_multiple_times: self.fields.pop("slot_count", None) elif ( "slot_count" in self.fields and instance and instance.state in [SubmissionStates.ACCEPTED, SubmissionStates.CONFIRMED] ): self.fields["slot_count"].disabled = True self.fields["slot_count"].help_text += " " + str( _( "Please contact the organisers if you want to change how often you're presenting this proposal." ) ) class Meta: model = Submission fields = [ "title", "submission_type", "track", "content_locale", "abstract", "description", "notes", "slot_count", "do_not_record", "image", "duration", ] request_require = [ "title", "abstract", "description", "notes", "image", "do_not_record", "track", "duration", ] public_fields = ["title", "abstract", "description", "image"] widgets = { "abstract": MarkdownWidget, "description": MarkdownWidget, "notes": MarkdownWidget, "track": TrackSelectWidget, } field_classes = { "submission_type": SafeModelChoiceField, "track": SafeModelChoiceField, }