class NewsSettingsForm(fossirForm): show_recent = BooleanField( 'Show headlines', widget=SwitchWidget(), description=_( 'Whether to show the latest news headlines on the fossir home page.' )) max_entries = IntegerField( _('Max. headlines'), [HiddenUnless('show_recent'), DataRequired(), NumberRange(min=1)], description= _("The maximum number of news headlines to show on the fossir home page." )) max_age = IntegerField( _('Max. age'), [HiddenUnless('show_recent'), InputRequired(), NumberRange(min=0)], description=_( "The maximum age in days for news to show up on the fossir home page. " "Setting it to 0 will show news no matter how old they are.")) new_days = IntegerField( _('"New" threshold'), [InputRequired(), NumberRange(min=0)], description=_( 'The maximum age in days for news to be considered "new". Setting it to 0 ' 'will disable the "new" label altogether.'))
class AttachmentPackageForm(fossirForm): added_since = fossirDateField( _('Added Since'), [Optional()], description=_('Include only attachments uploaded after this date')) filter_type = fossirRadioField(_('Include'), [DataRequired()]) sessions = fossirSelectMultipleCheckboxField( _('Sessions'), [HiddenUnless('filter_type', 'sessions'), DataRequired()], description=_('Include materials from selected sessions'), coerce=int) contributions = fossirSelectMultipleCheckboxField( _('Contributions'), [HiddenUnless('filter_type', 'contributions'), DataRequired()], description=_('Include materials from selected contributions'), coerce=int) dates = fossirSelectMultipleCheckboxField( _('Events scheduled on'), [HiddenUnless('filter_type', 'dates'), DataRequired()], description=_( 'Include materials from sessions/contributions scheduled ' 'on the selected dates'))
class TimetablePDFExportForm(fossirForm): _pdf_options_fields = {'pagesize', 'fontsize', 'firstPageNumber'} advanced = BooleanField(_("Advanced timetable"), widget=SwitchWidget(), description=_("Advanced customization options")) document_settings = fossirSelectMultipleCheckboxBooleanField( _('Document settings'), [HiddenUnless('advanced')], choices=_DOCUMENT_SETTINGS_CHOICES) contribution_info = fossirSelectMultipleCheckboxBooleanField( _('Contributions related info'), [HiddenUnless('advanced')], choices=_CONTRIBUTION_CHOICES) session_info = fossirSelectMultipleCheckboxBooleanField( _('Sessions related info'), [HiddenUnless('advanced')], choices=_SESSION_CHOICES) visible_entries = fossirSelectMultipleCheckboxBooleanField( _('Breaks and contributions'), [HiddenUnless('advanced')], choices=_VISIBLE_ENTRIES_CHOICES) other = fossirSelectMultipleCheckboxBooleanField(_('Miscellaneous'), choices=_OTHER_CHOICES) pagesize = SelectField(_('Page size'), choices=[('A0', 'A0'), ('A1', 'A1'), ('A2', 'A2'), ('A3', 'A3'), ('A4', 'A4'), ('A5', 'A5'), ('Letter', 'Letter')], default='A4') fontsize = SelectField(_('Font size'), choices=[('xxx-small', _('xxx-small')), ('xx-small', _('xx-small')), ('x-small', _('x-small')), ('smaller', _('smaller')), ('small', _('small')), ('normal', _('normal')), ('large', _('large')), ('larger', _('larger'))], default='normal') firstPageNumber = IntegerField(_('Number for the first page'), [NumberRange(min=1)], default=1, widget=NumberInput(step=1)) submitted = HiddenField() def is_submitted(self): return 'submitted' in request.args @property def data_for_format(self): if not self.advanced.data: fields = ('visible_entries', ) else: fields = set( get_form_field_names(TimetablePDFExportForm) ) - self._pdf_options_fields - {'csrf_token', 'advanced'} data = {} for fieldname in fields: data.update(getattr(self, fieldname).data) return data
class NewBookingConfirmForm(NewBookingPeriodForm): room_usage = RadioField( [DataRequired()], choices=[('current_user', _("I'll be using the room myself")), ('other_user', _("I'm booking the room for someone else"))]) booked_for_user = PrincipalField( _('User'), [HiddenUnless('room_usage', 'other_user'), DataRequired()], allow_external=True) booking_reason = TextAreaField(_('Reason'), [DataRequired()]) uses_vc = BooleanField(_('I will use videoconference equipment')) used_equipment = fossirQuerySelectMultipleCheckboxField( _('VC equipment'), get_label=lambda x: x.name) needs_vc_assistance = BooleanField( _('Request assistance for the startup of the videoconference session. ' 'This support is usually performed remotely.')) needs_assistance = BooleanField( _('Request personal assistance for meeting startup')) submit_book = SubmitField(_('Create booking')) submit_prebook = SubmitField(_('Create pre-booking')) def validate_used_equipment(self, field): if field.data and not self.uses_vc.data: raise ValidationError(_('Videoconference equipment is not used.')) elif not field.data and self.uses_vc.data: raise ValidationError( _('Please select the type of videoconference that you will use.' )) def validate_needs_vc_assistance(self, field): if field.data and not self.uses_vc.data: raise ValidationError(_('Videoconference equipment is not used.'))
class TicketsForm(fossirForm): tickets_enabled = BooleanField( _('Enable Tickets'), widget=SwitchWidget(), description=_( 'Create tickets for registrations using this registration form.')) ticket_on_email = BooleanField( _('Send with an e-mail'), [HiddenUnless('tickets_enabled', preserve_data=True)], widget=SwitchWidget(), description=_( 'Attach PDF ticket to the email sent to a user after completing ' 'their registration.')) ticket_on_event_page = BooleanField( _('Download from event homepage'), [HiddenUnless('tickets_enabled', preserve_data=True)], widget=SwitchWidget(), description=_('Allow users to download their ticket from the ' 'conference homepage.')) ticket_on_summary_page = BooleanField( _('Download from summary page'), [HiddenUnless('tickets_enabled', preserve_data=True)], widget=SwitchWidget(), description=_( 'Allow users to download their ticket from the registration ' 'summary page.')) ticket_template_id = SelectField( _('Ticket template'), [HiddenUnless('tickets_enabled', preserve_data=True), Optional()], coerce=int) def __init__(self, *args, **kwargs): event = kwargs.pop('event') super(TicketsForm, self).__init__(*args, **kwargs) default_tpl = get_default_template_on_category(event.category) all_templates = set( event.designer_templates) | get_inherited_templates(event) badge_templates = [ (tpl.id, tpl.title) for tpl in all_templates if tpl.type == TemplateType.badge and tpl != default_tpl ] # Show the default template first badge_templates.insert(0, (default_tpl.id, '{} ({})'.format( default_tpl.title, _('Default category template')))) self.ticket_template_id.choices = badge_templates
class SectionForm(fossirForm): display_as_section = BooleanField( _("Display as section"), widget=SwitchWidget(), default=True, description=_( "Whether this is going to be displayed as a section or standalone") ) title = StringField(_('Title'), [ HiddenUnless('display_as_section', preserve_data=True), DataRequired() ], description=_("The title of the section.")) description = TextAreaField( _('Description'), [HiddenUnless('display_as_section', preserve_data=True)], description=_("The description text of the section."))
def _add_contrib_type_hidden_unless(cls): # In the bulk form we need to hide/disable the contrib type selector unless we want to # override the type specified in the abstract. However, multiple HiddenUnless validators # are not supported in the client-side JS so we only add it to this form - it removes # inactive fields on the server side so it still works (the JavaScript picks up the last # HiddenUnless validator) inject_validators(BulkAbstractJudgmentForm, 'accepted_contrib_type', [HiddenUnless('override_contrib_type')])
class DeadlineForm(fossirForm): deadline = fossirDateTimeField(_("Deadline"), [Optional()], default_time=time(23, 59)) enforce = BooleanField(_("Enforce deadline"), [HiddenUnless('deadline')], widget=SwitchWidget()) def __init__(self, *args, **kwargs): self.event = kwargs.pop('event') super(DeadlineForm, self).__init__(*args, **kwargs)
class MultiSelectConfigForm(object): options = MultiStringField( _('Options'), [DataRequired()], field=('option', _('option')), unique=True, uuid_field='id', sortable=True, description=_('Specify the answers the user can select')) min_choices = IntegerField( _("Minimum choices"), [HiddenUnless('is_required'), Optional(), NumberRange(min=0)], description=_("The minimum amount of options the user has to choose.")) max_choices = IntegerField( _("Maximum choices"), [HiddenUnless('is_required'), Optional(), NumberRange(min=1)], description=_("The maximum amount of options the user may choose.")) def _validate_min_max_choices(self): if (self.min_choices.data is not None and self.max_choices.data is not None and self.min_choices.data > self.max_choices.data): raise ValidationError( _('Maximum choices must be greater than minimum choices.')) def validate_min_choices(self, field): if field.data is None: return if field.data >= len(self.options.data): raise ValidationError( _("Minimum choices must be fewer than the total number of options." )) def validate_max_choices(self, field): if field.data is None: return self._validate_min_max_choices() if field.data > len(self.options.data): raise ValidationError( _("Maximum choices must be fewer or equal than the total number of options." ))
class CloneRepeatUntilFormBase(CloneRepeatOnceForm): stop_criterion = fossirRadioField( _('Clone'), [DataRequired()], default='num_times', choices=(('day', _('Until a given day (inclusive)')), ('num_times', _('A number of times')))) until_dt = fossirDateField( _('Day'), [HiddenUnless('stop_criterion', 'day'), DataRequired()]) num_times = IntegerField(_('Number of times'), [ HiddenUnless('stop_criterion', 'num_times'), DataRequired(), NumberRange(1, 100, message=_("You can clone a maximum of 100 times")) ], default=2) def __init__(self, event, **kwargs): kwargs['until_dt'] = (self._calc_start_dt(event) + timedelta(days=14)).date() super(CloneRepeatUntilFormBase, self).__init__(event, **kwargs)
def __init__(self, *args, **kwargs): if config.LOCAL_IDENTITIES: for field in ('username', 'password', 'confirm_password'): inject_validators(self, field, [HiddenUnless('create_identity')], early=True) super(AdminAccountRegistrationForm, self).__init__(*args, **kwargs) del self.comment if not config.LOCAL_IDENTITIES: del self.username del self.password del self.confirm_password del self.create_identity
class AbstractSubmissionSettingsForm(fossirForm): """Settings form for abstract submission""" announcement = fossirMarkdownField(_('Announcement'), editor=True) allow_multiple_tracks = BooleanField( _('Multiple tracks'), widget=SwitchWidget(), description=_("Allow the selection of multiple tracks")) tracks_required = BooleanField( _('Require tracks'), widget=SwitchWidget(), description=_("Make the track selection mandatory")) contrib_type_required = BooleanField( _('Require contrib. type'), widget=SwitchWidget(), description=_("Make the selection of a contribution type mandatory")) allow_attachments = BooleanField( _('Allow attachments'), widget=SwitchWidget(), description=_("Allow files to be attached to the abstract")) allow_speakers = BooleanField( _('Allow speakers'), widget=SwitchWidget(), description=_("Allow the selection of the abstract speakers")) speakers_required = BooleanField( _('Require a speaker'), [HiddenUnless('allow_speakers')], widget=SwitchWidget(), description=_( "Make the selection of at least one author as speaker mandatory")) authorized_submitters = PrincipalListField( _("Authorized submitters"), description=_("These users may always submit abstracts, even outside " "the regular submission period.")) submission_instructions = fossirMarkdownField( _('Instructions'), editor=True, description=_("These instructions will be displayed right before the " "submission form.")) @generated_data def announcement_render_mode(self): return RenderMode.markdown def __init__(self, *args, **kwargs): self.event = kwargs.pop('event') super(AbstractSubmissionSettingsForm, self).__init__(*args, **kwargs) def validate_contrib_type_required(self, field): if field.data and not self.event.contribution_types.count(): raise ValidationError( _('The event has no contribution types defined.'))
class NewBookingSimpleForm(NewBookingConfirmForm): submit_check = SubmitField(_('Check conflicts')) booking_reason = TextAreaField(_('Reason'), [ UsedIf(lambda form, field: not form.submit_check.data), DataRequired() ]) room_usage = RadioField( validators=[ UsedIf(lambda form, field: not form.submit_check.data), DataRequired() ], choices=[('current_user', _("I'll be using the room myself")), ('other_user', _("I'm booking the room for someone else"))]) booked_for_user = PrincipalField(_('User'), [ UsedIf(lambda form, field: not form.submit_check.data), HiddenUnless('room_usage', 'other_user'), DataRequired() ], allow_external=True)
class MenuBuiltinEntryForm(fossirForm): custom_title = BooleanField(_("Custom title"), widget=SwitchWidget()) title = StringField(_("Title"), [HiddenUnless('custom_title'), DataRequired()]) is_enabled = BooleanField(_("Show"), widget=SwitchWidget()) def __init__(self, *args, **kwargs): entry = kwargs.pop('entry') super(MenuBuiltinEntryForm, self).__init__(*args, **kwargs) self.custom_title.description = _( "If you customize the title, that title is used regardless of the user's " "language preference. The default title <strong>{title}</strong> is " "displayed in the user's language.").format( title=entry.default_data.title) def post_validate(self): if not self.custom_title.data: self.title.data = None
class SingleChoiceConfigForm(object): display_type = fossirRadioField( _('Display type'), [DataRequired()], description=_( 'Widget that will be used to render the available options'), choices=[('radio', _('Radio buttons')), ('select', _('Drop-down list'))], default='radio') radio_display_type = fossirRadioField( _('Alignment'), [DataRequired(), HiddenUnless('display_type', 'radio')], description=_('The arrangement of the options'), choices=[('vertical', _('Vertical')), ('horizontal', _('Horizontal'))]) options = MultiStringField( _('Options'), [DataRequired()], field=('option', _('option')), unique=True, uuid_field='id', sortable=True, description=_('Specify the options the user can choose from'))
class SurveyForm(fossirForm): _notification_fields = ('notifications_enabled', 'notify_participants', 'start_notification_emails', 'new_submission_emails') title = StringField(_("Title"), [DataRequired()], description=_("The title of the survey")) introduction = TextAreaField( _("Introduction"), description=_("An introduction to be displayed before the survey")) anonymous = BooleanField( _("Anonymous submissions"), widget=SwitchWidget(), description=_("User information will not be attached to submissions")) require_user = BooleanField( _("Only logged-in users"), [HiddenUnless('anonymous')], widget=SwitchWidget(), description=_( "Require users to be logged in for submitting the survey")) limit_submissions = BooleanField( _("Limit submissions"), widget=SwitchWidget(), description=_("Whether there is a submission cap")) submission_limit = IntegerField( _("Capacity"), [ HiddenUnless('limit_submissions'), DataRequired(), NumberRange(min=1) ], description=_("Maximum number of submissions accepted")) private = BooleanField( _("Private survey"), widget=SwitchWidget(), description=_("Only selected people can answer the survey.")) partial_completion = BooleanField( _('Partial completion'), widget=SwitchWidget(), description=_('Allow to save answers without submitting the survey.')) notifications_enabled = BooleanField( _('Enabled'), widget=SwitchWidget(), description=_( 'Send email notifications for specific events related to the ' 'survey.')) notify_participants = BooleanField( _('Participants'), [HiddenUnless('notifications_enabled', preserve_data=True)], widget=SwitchWidget(), description=_( 'Notify participants of the event when this survey starts.')) start_notification_emails = EmailListField( _('Start notification recipients'), [HiddenUnless('notifications_enabled', preserve_data=True)], description=_( 'Email addresses to notify about the start of the survey')) new_submission_emails = EmailListField( _('New submission notification recipients'), [HiddenUnless('notifications_enabled', preserve_data=True)], description=_( 'Email addresses to notify when a new submission is made')) def __init__(self, *args, **kwargs): self.event = kwargs.pop('event') super(fossirForm, self).__init__(*args, **kwargs) def validate_title(self, field): query = (Survey.query.with_parent(self.event).filter( db.func.lower(Survey.title) == field.data.lower(), Survey.title != field.object_data, ~Survey.is_deleted)) if query.count(): raise ValidationError( _('There is already a survey named "{}" on this event'.format( escape(field.data)))) def post_validate(self): if not self.anonymous.data: self.require_user.data = True
class BulkAbstractJudgmentForm(AbstractJudgmentFormBase): _order = ('judgment', 'accepted_track', 'override_contrib_type', 'accepted_contrib_type', 'session', 'duplicate_of', 'merged_into', 'merge_persons', 'judgment_comment', 'send_notifications') judgment = HiddenEnumField(enum=AbstractAction, skip={AbstractAction.change_tracks}) abstract_id = HiddenFieldList() submitted = HiddenField() override_contrib_type = BooleanField( _("Override contribution type"), [HiddenUnless('judgment', AbstractAction.accept)], widget=SwitchWidget(), description=_( "Override the contribution type for all selected abstracts")) def __init__(self, *args, **kwargs): self.event = kwargs.pop('event') super(BulkAbstractJudgmentForm, self).__init__(*args, **kwargs) if self.accepted_track: self.accepted_track.description = _( "The abstracts will be accepted in this track") if self.accepted_contrib_type: self.accepted_contrib_type.description = _( "The abstracts will be converted into a contribution of this " "type") else: del self.override_contrib_type if self.session: self.session.description = _( "The generated contributions will be allocated in this session" ) self.duplicate_of.description = _( "The selected abstracts will be marked as duplicate of the specified " "abstract") self.merged_into.description = _( "The selected abstracts will be merged into the specified abstract" ) self.merge_persons.description = _( "Authors and speakers of the selected abstracts will be added to the " "specified abstract") self.duplicate_of.excluded_abstract_ids = set(kwargs['abstract_id']) self.merged_into.excluded_abstract_ids = set(kwargs['abstract_id']) if kwargs['judgment']: self._remove_unused_fields(kwargs['judgment']) def _remove_unused_fields(self, judgment): for field in list(self): validator = next( (v for v in field.validators if isinstance(v, HiddenUnless) and v.field == 'judgment'), None) if validator is None: continue if not any(v.name == judgment for v in validator.value): delattr(self, field.name) def is_submitted(self): return super(BulkAbstractJudgmentForm, self).is_submitted() and 'submitted' in request.form @classmethod def _add_contrib_type_hidden_unless(cls): # In the bulk form we need to hide/disable the contrib type selector unless we want to # override the type specified in the abstract. However, multiple HiddenUnless validators # are not supported in the client-side JS so we only add it to this form - it removes # inactive fields on the server side so it still works (the JavaScript picks up the last # HiddenUnless validator) inject_validators(BulkAbstractJudgmentForm, 'accepted_contrib_type', [HiddenUnless('override_contrib_type')])
class AbstractReviewForm(fossirForm): """Form for reviewing an abstract""" _order = ('proposed_action', 'proposed_contribution_type', 'proposed_related_abstract', 'proposed_tracks', 'comment') comment = TextAreaField(_("Comment"), render_kw={ 'placeholder': _("You may leave a comment (only visible to " "conveners and judges)...") }) proposed_action = fossirEnumSelectField(_("Proposed Action"), [DataRequired()], enum=AbstractAction) proposed_related_abstract = AbstractField( _("Target Abstract"), [ HiddenUnless( 'proposed_action', {AbstractAction.mark_as_duplicate, AbstractAction.merge}), DataRequired() ], description= _("The current abstract should be marked as duplicate of the selected one" ), ajax_endpoint='abstracts.other_abstracts') proposed_contribution_type = QuerySelectField( _("Contribution type"), [HiddenUnless('proposed_action', AbstractAction.accept)], get_label=lambda x: x.name.title(), allow_blank=True, blank_text=_("You may propose a contribution type...")) proposed_tracks = fossirQuerySelectMultipleCheckboxField( _("Propose for tracks"), [ HiddenUnless('proposed_action', AbstractAction.change_tracks), DataRequired() ], collection_class=set, get_label='title') def __init__(self, edit=False, *args, **kwargs): abstract = kwargs.pop('abstract') super(AbstractReviewForm, self).__init__(*args, **kwargs) self.event = abstract.event if not edit: self.proposed_action.none = _("Propose an action...") self.proposed_related_abstract.excluded_abstract_ids = {abstract.id} self.proposed_contribution_type.query = ( ContributionType.query.with_parent(self.event).order_by( ContributionType.name)) if not self.proposed_contribution_type.query.count(): del self.proposed_contribution_type reviewed_for_track_ids = {t.id for t in abstract.reviewed_for_tracks} existing_prop_track_cond = (Track.id.in_( t.id for t in self.proposed_tracks.object_data) if self.proposed_tracks.object_data else False) self.proposed_tracks.query = (Track.query.with_parent( self.event).filter( db.or_(Track.id.notin_(reviewed_for_track_ids), existing_prop_track_cond)).order_by(Track.position)) if not self.proposed_tracks.query.count(): del self.proposed_tracks self.proposed_action.skip.add(AbstractAction.change_tracks) @property def split_data(self): data = self.data return { 'questions_data': {k: v for k, v in data.iteritems() if k.startswith('question_')}, 'review_data': { k: v for k, v in data.iteritems() if not k.startswith('question_') } }
class AbstractJudgmentFormBase(fossirForm): """Form base class for abstract judgment operations""" _order = ('judgment', 'accepted_track', 'accepted_contrib_type', 'session', 'duplicate_of', 'merged_into', 'merge_persons', 'judgment_comment', 'send_notifications') accepted_track = QuerySelectField( _("Track"), [HiddenUnless('judgment', AbstractAction.accept)], get_label='title', allow_blank=True, blank_text=_("Choose a track..."), description=_("The abstract will be accepted in this track")) accepted_contrib_type = QuerySelectField( _("Contribution type"), [HiddenUnless('judgment', AbstractAction.accept)], get_label=lambda x: x.name.title(), allow_blank=True, blank_text=_("You may choose a contribution type..."), description=_("The abstract will be converted " "into a contribution of this type")) session = QuerySelectField( _("Session"), [HiddenUnless('judgment', AbstractAction.accept)], get_label='title', allow_blank=True, blank_text=_("You may choose a session..."), description=_( "The generated contribution will be allocated in this session")) duplicate_of = AbstractField( _("Duplicate of"), [ HiddenUnless('judgment', AbstractAction.mark_as_duplicate), DataRequired() ], description= _("The current abstract will be marked as duplicate of the selected one" ), ajax_endpoint='abstracts.other_abstracts') merged_into = AbstractField( _("Merge into"), [HiddenUnless('judgment', AbstractAction.merge), DataRequired()], description=_( "The current abstract will be merged into the selected one"), ajax_endpoint='abstracts.other_abstracts') merge_persons = BooleanField( _("Merge persons"), [HiddenUnless('judgment', AbstractAction.merge)], description=_( "Authors and speakers of the current abstract will be added to the " "selected one")) judgment_comment = TextAreaField( _("Comment"), render_kw={'placeholder': _("Leave a comment for the submitter...")}) # TODO: show only if notifications apply? send_notifications = BooleanField(_("Send notifications to submitter"), default=True) def __init__(self, *args, **kwargs): super(AbstractJudgmentFormBase, self).__init__(*args, **kwargs) self.session.query = Session.query.with_parent(self.event).order_by( Session.title) if not self.session.query.count(): del self.session self.accepted_track.query = Track.query.with_parent( self.event).order_by(Track.position) if not self.accepted_track.query.count(): del self.accepted_track self.accepted_contrib_type.query = (ContributionType.query.with_parent( self.event).order_by(ContributionType.name)) if not self.accepted_contrib_type.query.count(): del self.accepted_contrib_type @property def split_data(self): abstract_data = self.data judgment_data = { 'judgment': abstract_data.pop('judgment'), 'send_notifications': abstract_data.pop('send_notifications'), 'contrib_session': abstract_data.pop('session', None), 'merge_persons': abstract_data.pop('merge_persons', None) } return judgment_data, abstract_data
class AbstractReviewingSettingsForm(fossirForm): """Settings form for abstract reviewing""" RATING_FIELDS = ('scale_lower', 'scale_upper') scale_lower = IntegerField( _("Scale (from)"), [UsedIf(lambda form, field: not form.has_ratings), InputRequired()]) scale_upper = IntegerField( _("Scale (to)"), [UsedIf(lambda form, field: not form.has_ratings), InputRequired()]) allow_convener_judgment = BooleanField( _("Allow track conveners to judge"), widget=SwitchWidget(), description=_( "Enabling this allows track conveners to make a judgment " "such as accepting or rejecting an abstract.")) allow_comments = BooleanField( _("Allow comments"), widget=SwitchWidget(), description=_( "Enabling this allows judges, conveners and reviewers to leave " "comments on abstracts.")) allow_contributors_in_comments = BooleanField( _("Allow contributors in comments"), [HiddenUnless('allow_comments', preserve_data=True)], widget=SwitchWidget(), description=_("Enabling this allows submitters, authors, and " "speakers to also participate in the comments.")) abstract_review_questions = ReviewQuestionsField( _("Review questions"), question_model=AbstractReviewQuestion, extra_fields=[{ 'id': 'no_score', 'caption': _("Exclude from score"), 'type': 'checkbox' }]) reviewing_instructions = fossirMarkdownField( _('Reviewing Instructions'), editor=True, description=_("These instructions will be displayed right before the " "reviewing form.")) judgment_instructions = fossirMarkdownField( _('Judgment Instructions'), editor=True, description=_("These instructions will be displayed right before the " "decision box.")) def __init__(self, *args, **kwargs): self.event = kwargs.pop('event') self.has_ratings = kwargs.pop('has_ratings', False) super(AbstractReviewingSettingsForm, self).__init__(*args, **kwargs) if self.has_ratings: self.scale_upper.warning = _( "Some reviewers have already submitted ratings so the scale cannot be changed " "anymore.") def validate_scale_upper(self, field): lower = self.scale_lower.data upper = self.scale_upper.data if lower is None or upper is None: return if lower >= upper: raise ValidationError( _("The scale's 'to' value must be greater than the 'from' value." )) if upper - lower > 20: raise ValidationError( _("The difference between 'to' and' from' may not be greater than 20." )) @property def data(self): data = super(AbstractReviewingSettingsForm, self).data if self.has_ratings: for key in self.RATING_FIELDS: del data[key] return data
class ReminderForm(fossirForm): default_widget_attrs = {'absolute_time': {'placeholder': 'HH:MM'}} recipient_fields = {'recipients', 'send_to_participants'} schedule_fields = {'schedule_type', 'absolute_date', 'absolute_time', 'relative_delta'} schedule_recipient_fields = recipient_fields | schedule_fields # Schedule schedule_type = fossirRadioField(_('Type'), [DataRequired()], choices=[('relative', _("Relative to the event start time")), ('absolute', _("Fixed date/time")), ('now', _('Send immediately'))]) relative_delta = TimeDeltaField(_('Offset'), [HiddenUnless('schedule_type', 'relative'), DataRequired()]) absolute_date = fossirDateField(_('Date'), [HiddenUnless('schedule_type', 'absolute'), DataRequired()]) absolute_time = TimeField(_('Time'), [HiddenUnless('schedule_type', 'absolute'), InputRequired()]) # Recipients recipients = EmailListField(_('Email addresses'), description=_('One email address per line.')) send_to_participants = BooleanField(_('Participants'), description=_('Send the reminder to all participants/registrants ' 'of the event.')) # Misc reply_to_address = SelectField(_('Sender'), [DataRequired()], description=_('The email address that will show up as the sender.')) message = TextAreaField(_('Note'), description=_('A custom message to include in the email.')) include_summary = BooleanField(_('Include agenda'), description=_("Includes a simple text version of the event's agenda in the email.")) def __init__(self, *args, **kwargs): self.event = kwargs.pop('event') super(ReminderForm, self).__init__(*args, **kwargs) self.absolute_time.description = _("The event's timezone is {tz}.").format(tz=self.event.tzinfo) self.reply_to_address.choices = (self.event .get_allowed_sender_emails(extra=self.reply_to_address.object_data) .items()) if self.event.type_ == EventType.lecture: del self.include_summary def validate_recipients(self, field): if not field.data and not self.send_to_participants.data: raise ValidationError(_('If participants are not included you need to specify recipients.')) def validate_send_to_participants(self, field): if not field.data and not self.recipients.data: raise ValidationError(_('If no recipients are specified you need to include participants.')) def validate_schedule_type(self, field): # Be graceful and allow a reminder that's in the past but on the same day. # It will be sent immediately but that way we are a little bit more user-friendly if field.data == 'now': return scheduled_dt = self.scheduled_dt.data if scheduled_dt is not None and scheduled_dt.date() < now_utc().date(): raise ValidationError(_('The specified date is in the past')) def validate_absolute_date(self, field): if self.schedule_type.data == 'absolute' and field.data < date.today(): raise ValidationError(_('The specified date is in the past')) @generated_data def scheduled_dt(self): if self.schedule_type.data == 'absolute': if self.absolute_date.data is None or self.absolute_time.data is None: return None dt = datetime.combine(self.absolute_date.data, self.absolute_time.data) return self.event.tzinfo.localize(dt).astimezone(pytz.utc) elif self.schedule_type.data == 'relative': if self.relative_delta.data is None: return None return self.event.start_dt - self.relative_delta.data elif self.schedule_type.data == 'now': return now_utc() @generated_data def event_start_delta(self): return self.relative_delta.data if self.schedule_type.data == 'relative' else None
class ConferenceLayoutForm(fossirForm): is_searchable = BooleanField( _("Enable search"), widget=SwitchWidget(), description=_("Enable search within the event")) show_nav_bar = BooleanField( _("Show navigation bar"), widget=SwitchWidget(), description=_("Show the navigation bar at the top")) show_banner = BooleanField( _("\"Now happening\""), widget=SwitchWidget(on_label=_("ON"), off_label=_("OFF")), description=_( "Show a banner with the current entries from the timetable")) show_social_badges = BooleanField(_("Show social badges"), widget=SwitchWidget()) # Style header_text_color = StringField(_("Text colour"), widget=ColorPickerWidget()) header_background_color = StringField(_("Background colour"), widget=ColorPickerWidget()) # Announcement announcement = StringField( _("Announcement"), [UsedIf(lambda form, field: form.show_announcement.data)], description=_("Short message shown below the title")) show_announcement = BooleanField( _("Show announcement"), widget=SwitchWidget(), description=_("Show the announcement message")) # Timetable timetable_by_room = BooleanField( _("Group by room"), widget=SwitchWidget(), description=_("Group the entries of the timetable by room by default")) timetable_detailed = BooleanField( _("Show detailed view"), widget=SwitchWidget(), description=_("Show the detailed view of the timetable by default.")) timetable_theme = SelectField(_('Theme'), [Optional()], coerce=lambda x: x or None) # Themes use_custom_css = BooleanField( _("Use custom CSS"), widget=SwitchWidget(), description=_( "Use a custom CSS file as a theme for the conference page. Deactivate " "this option to reveal the available fossir themes.")) theme = SelectField( _("Theme"), [Optional(), HiddenUnless('use_custom_css', False)], coerce=lambda x: (x or None), description=_( "Currently selected theme of the conference page. Click on the Preview button to " "preview and select a different one.")) def __init__(self, *args, **kwargs): self.event = kwargs.pop('event') super(ConferenceLayoutForm, self).__init__(*args, **kwargs) self.timetable_theme.choices = [ ('', _('Default')) ] + _get_timetable_theme_choices(self.event) self.theme.choices = _get_conference_theme_choices() def validate_use_custom_css(self, field): if field.data and not self.event.has_stylesheet: raise ValidationError( _('Cannot enable custom stylesheet unless there is one.'))
class RegistrationFormForm(fossirForm): _price_fields = ('currency', 'base_price') _registrant_notification_fields = ('notification_sender_address', 'message_pending', 'message_unpaid', 'message_complete') _manager_notification_fields = ('manager_notifications_enabled', 'manager_notification_recipients') _special_fields = _price_fields + _registrant_notification_fields + _manager_notification_fields title = StringField(_("Title"), [DataRequired()], description=_("The title of the registration form")) introduction = TextAreaField( _("Introduction"), description= _("Introduction to be displayed when filling out the registration form" )) contact_info = StringField( _("Contact info"), description= _("How registrants can get in touch with somebody for extra information" )) moderation_enabled = BooleanField( _("Moderated"), widget=SwitchWidget(), description=_("If enabled, registrations require manager approval")) require_login = BooleanField( _("Only logged-in users"), widget=SwitchWidget(), description=_("Users must be logged in to register")) require_user = BooleanField( _("Registrant must have account"), widget=SwitchWidget(), description=_( "Registrations emails must be associated with an fossir account")) limit_registrations = BooleanField( _("Limit registrations"), widget=SwitchWidget(), description=_("Whether there is a limit of registrations")) registration_limit = IntegerField( _("Capacity"), [ HiddenUnless('limit_registrations'), DataRequired(), NumberRange(min=1) ], description=_("Maximum number of registrations")) modification_mode = fossirEnumSelectField( _("Modification allowed"), enum=ModificationMode, description=_("Will users be able to modify their data? When?")) publish_registrations_enabled = BooleanField( _('Publish registrations'), widget=SwitchWidget(), description=_("Registrations from this form will be displayed in the " "event page")) publish_registration_count = BooleanField( _("Publish number of registrations"), widget=SwitchWidget(), description=_("Number of registered participants will be displayed in " "the event page")) publish_checkin_enabled = BooleanField( _('Publish check-in status'), widget=SwitchWidget(), description=_( "Check-in status will be shown publicly on the event page")) base_price = DecimalField( _('Registration fee'), [NumberRange(min=0), Optional(), _check_if_payment_required], filters=[lambda x: x if x is not None else 0], widget=NumberInput(step='0.01'), description=_("A fixed fee all users have to pay when registering.")) currency = SelectField(_('Currency'), [DataRequired()], description=_('The currency for new registrations')) notification_sender_address = StringField(_('Notification sender address'), [fossirEmail()], filters=[lambda x: (x or None)]) message_pending = TextAreaField( _("Message for pending registrations"), description=_("Text included in emails sent to pending registrations")) message_unpaid = TextAreaField( _("Message for unpaid registrations"), description=_("Text included in emails sent to unpaid registrations")) message_complete = TextAreaField( _("Message for complete registrations"), description=_( "Text included in emails sent to complete registrations")) manager_notifications_enabled = BooleanField( _('Enabled'), widget=SwitchWidget(), description=_("Enable notifications to managers about registrations")) manager_notification_recipients = EmailListField( _('List of recipients'), [ HiddenUnless('manager_notifications_enabled', preserve_data=True), DataRequired() ], description=_("Email addresses that will receive notifications")) def __init__(self, *args, **kwargs): self.event = kwargs.pop('event') super(fossirForm, self).__init__(*args, **kwargs) self._set_currencies() self.notification_sender_address.description = _( 'Email address set as the sender of all ' 'notifications sent to users. If empty, ' 'then {0} is used.'.format(config.NO_REPLY_EMAIL)) def _set_currencies(self): currencies = [(c['code'], '{0[code]} ({0[name]})'.format(c)) for c in payment_settings.get('currencies')] self.currency.choices = sorted(currencies, key=lambda x: x[1].lower())