Beispiel #1
0
class AbstractReviewingSettingsForm(IndicoForm):
    """Settings form for abstract reviewing."""

    scale_lower = IntegerField(_('Scale (from)'), [InputRequired()])
    scale_upper = IntegerField(_('Scale (to)'), [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_convener_track_change = BooleanField(_('Allow conveners to change tracks'),
                                               [HiddenUnless('allow_convener_judgment',
                                                             value=False, preserve_data=True)],
                                               widget=SwitchWidget(),
                                               description=_('Enabling this allows track conveners to update the track '
                                                             'an abstract is part of.'))
    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.'))
    reviewing_instructions = IndicoMarkdownField(_('Reviewing Instructions'), editor=True,
                                                 description=_('These instructions will be displayed right before the '
                                                               'reviewing form.'))
    judgment_instructions = IndicoMarkdownField(_('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().__init__(*args, **kwargs)
        if self.has_ratings:
            self.scale_upper.warning = _('Changing the ratings scale will proportionally affect existing ratings.')

    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."))
Beispiel #2
0
class AttachmentFolderForm(IndicoForm):
    title = HiddenField(_("Name"), [DataRequired()], widget=TypeaheadWidget(),
                        description=_("The name of the folder."))
    description = TextAreaField(_("Description"), description=_("Description of the folder and its content"))
    protected = BooleanField(_("Protected"), widget=SwitchWidget())
    acl = AccessControlListField(_("Access control list"), [UsedIf(lambda form, field: form.protected.data)],
                                 allow_groups=True, allow_external_users=True,
                                 default_text=_('Restrict access to this folder'),
                                 description=_("The list of users and groups allowed to access the folder"))
    is_always_visible = BooleanField(_("Always Visible"),
                                     [HiddenUnless('is_hidden', value=False)],
                                     widget=SwitchWidget(),
                                     description=_("By default, folders are always visible, even if a user cannot "
                                                   "access them. You can disable this behavior here, hiding the folder "
                                                   "for anyone who does not have permission to access it."))
    is_hidden = BooleanField(_("Always hidden"),
                             [HiddenUnless('is_always_visible', value=False)],
                             widget=SwitchWidget(),
                             description=_("Always hide the folder and its contents from public display areas of "
                                           "the event. You can use this for folders to store non-image files used "
                                           "e.g. in download links. The access permissions still apply."))

    def __init__(self, *args, **kwargs):
        self.linked_object = kwargs.pop('linked_object')
        super(AttachmentFolderForm, self).__init__(*args, **kwargs)
        self.title.choices = self._get_title_suggestions()

    def _get_title_suggestions(self):
        query = db.session.query(AttachmentFolder.title).filter_by(is_deleted=False, is_default=False,
                                                                   object=self.linked_object)
        existing = set(x[0] for x in query)
        suggestions = set(get_default_folder_names()) - existing
        if self.title.data:
            suggestions.add(self.title.data)
        return sorted(suggestions)

    def validate_is_always_visible(self, field):
        if self.is_always_visible.data and self.is_hidden.data:
            raise ValidationError('These two options cannot be used at the same time')

    validate_is_hidden = validate_is_always_visible

    @generated_data
    def protection_mode(self):
        return ProtectionMode.protected if self.protected.data else ProtectionMode.inheriting
Beispiel #3
0
class AttachmentPackageForm(IndicoForm):
    added_since = IndicoDateField(_('Added Since'), [Optional()],
                                  description=_('Include only attachments uploaded after this date'))

    filter_type = IndicoRadioField(_('Include'), [DataRequired()])

    sessions = IndicoSelectMultipleCheckboxField(_('Sessions'), [HiddenUnless('filter_type', 'sessions'),
                                                                 DataRequired()],
                                                 description=_('Include materials from selected sessions'),
                                                 coerce=int)
    contributions = IndicoSelectMultipleCheckboxField(_('Contributions'),
                                                      [HiddenUnless('filter_type', 'contributions'), DataRequired()],
                                                      description=_('Include materials from selected contributions'),
                                                      coerce=int)
    dates = IndicoSelectMultipleCheckboxField(_('Events scheduled on'), [HiddenUnless('filter_type', 'dates'),
                                                                         DataRequired()],
                                              description=_('Include materials from sessions/contributions scheduled '
                                                            'on the selected dates'))
Beispiel #4
0
class DeadlineForm(IndicoForm):
    deadline = IndicoDateTimeField(_("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)
Beispiel #5
0
class TicketsForm(IndicoForm):
    tickets_enabled = BooleanField(_('Enable Tickets'), widget=SwitchWidget(),
                                   description=_('Create tickets for registrations using this registration form.'))
    ticket_on_email = BooleanField(_('Attach to registration 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.'))
Beispiel #6
0
class AbstractSubmissionSettingsForm(IndicoForm):
    """Settings form for abstract submission."""

    announcement = IndicoMarkdownField(_('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'))
    copy_attachments = BooleanField(_('Copy attachments'), [HiddenUnless('allow_attachments')], widget=SwitchWidget(),
                                    description=_('Copy attachments to the contribution when accepting an 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'))
    allow_editing = IndicoEnumSelectField(_('Allow editing'), enum=AllowEditingType, sorted=True,
                                          description=_('Specify who will be able to edit the abstract'))
    contribution_submitters = IndicoEnumSelectField(_('Contribution submitters'),
                                                    enum=SubmissionRightsType, sorted=True,
                                                    description=_('Specify who will get contribution submission rights '
                                                                  'once an abstract has been accepted'))
    authorized_submitters = PrincipalListField(_('Authorized submitters'), event=lambda form: form.event,
                                               allow_external_users=True, allow_groups=True,
                                               allow_event_roles=True, allow_category_roles=True,
                                               description=_('These users may always submit abstracts, '
                                                             'even outside the regular submission period.'))
    submission_instructions = IndicoMarkdownField(_('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().__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.'))
Beispiel #7
0
class InvitedAbstractMixin(object):
    users_with_no_account = IndicoRadioField(_('Type of user'),
                                             [DataRequired()],
                                             default='existing',
                                             choices=(('existing',
                                                       _('Existing user')),
                                                      ('new', _('New user'))))
    submitter = PrincipalField(
        _('Submitter'),
        [HiddenUnless('users_with_no_account', 'existing'),
         DataRequired()],
        allow_external_users=True,
        description=_('The person invited to submit the abstract'))
    first_name = StringField(
        _('First name'),
        [HiddenUnless('users_with_no_account', 'new'),
         DataRequired()])
    last_name = StringField(
        _('Family name'),
        [HiddenUnless('users_with_no_account', 'new'),
         DataRequired()])
    email = EmailField(
        _('Email address'),
        [HiddenUnless('users_with_no_account', 'new'),
         DataRequired()],
        filters=[lambda x: x.lower() if x else x])

    def __init__(self, *args, **kwargs):
        self.event = kwargs['event']
        super(InvitedAbstractMixin, self).__init__(*args, **kwargs)

    def validate_email(self, field):
        if get_user_by_email(field.data):
            raise ValidationError(
                _('There is already a user with this email address'))

    def validate(self):
        from indico.modules.events.abstracts.util import can_create_invited_abstracts
        if not can_create_invited_abstracts(self.event):
            raise ValidationError(
                _('You have to create an "Invited" abstract notification template in order to '
                  'be able to create invited abstracts.'))
        else:
            return super(InvitedAbstractMixin, self).validate()
Beispiel #8
0
class MultiSelectConfigForm:
    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."
                  ))
Beispiel #9
0
class SurveyForm(IndicoForm):
    _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=_("Still 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"))
    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', None)
        super(IndicoForm, self).__init__(*args, **kwargs)

    def validate_title(self, field):
        query = Survey.find(Survey.event_id == self.event.id,
                            db.func.lower(Survey.title) == field.data.lower(),
                            Survey.title != field.object_data,
                            ~Survey.is_deleted)
        if query.count():
            raise ValidationError(_("There is already an survey named \"{}\" on this event".format(escape(field.data))))

    def post_validate(self):
        if not self.anonymous.data:
            self.require_user.data = True
Beispiel #10
0
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)
Beispiel #11
0
class UserPreferencesForm(IndicoForm):
    lang = SelectField(_('Language'))
    timezone = SelectField(_('Timezone'))

    force_timezone = BooleanField(
        _('Use my timezone'),
        widget=SwitchWidget(),
        description=_(
            "Always use my current timezone instead of an event's timezone."))

    show_future_events = BooleanField(
        _('Show future events'),
        widget=SwitchWidget(),
        description=_('Show future events by default.'))

    show_past_events = BooleanField(
        _('Show past events'),
        widget=SwitchWidget(),
        description=_('Show past events by default.'))

    name_format = IndicoEnumSelectField(
        _('Name format'),
        enum=NameFormat,
        description=_('Default format in which names are displayed'))

    use_previewer_pdf = BooleanField(
        _('Use previewer for PDF files'),
        widget=SwitchWidget(),
        description=
        _('The previewer is used by default for image and text files, but not for PDF files.'
          ))

    add_ical_alerts = BooleanField(
        _('Add alerts to iCal'),
        widget=SwitchWidget(),
        description=_('Add an event reminder to exported iCal files/URLs.'))

    add_ical_alerts_mins = IntegerField(
        _('iCal notification time'),
        [HiddenUnless('add_ical_alerts'),
         NumberRange(min=0)],
        description=_('Number of minutes to notify before an event.'))

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        locales = [(code, f'{name} ({territory})' if territory else name)
                   for code, (name, territory,
                              __) in get_all_locales().items()]
        self.lang.choices = sorted(locales, key=itemgetter(1))
        self.timezone.choices = list(zip(common_timezones, common_timezones))
        if self.timezone.object_data and self.timezone.object_data not in common_timezones_set:
            self.timezone.choices.append(
                (self.timezone.object_data, self.timezone.object_data))
Beispiel #12
0
 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
Beispiel #13
0
class CloneRepeatUntilFormBase(CloneRepeatOnceForm):
    stop_criterion = IndicoRadioField(
        _('Clone'), [DataRequired()],
        default='num_times',
        choices=(('day', _('Until a given day (inclusive)')),
                 ('num_times', _('A number of times'))))
    until_dt = IndicoDateField(
        _('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.setdefault('until_dt', (self._calc_start_dt(event) +
                                       timedelta(days=14)).date())
        super(CloneRepeatUntilFormBase, self).__init__(event, **kwargs)
Beispiel #14
0
class CERNAccessForm(RequestFormBase):
    regforms = IndicoSelectMultipleCheckboxField(_('Registration forms'),
                                                 [DataRequired(_('At least one registration form has to be selected'))],
                                                 widget=JinjaWidget('regform_list_widget.html', 'cern_access'))
    during_registration = BooleanField(_('Show during user registration'), widget=SwitchWidget(),
                                       description=_("When enabled, users can request site access while registering "
                                                     "and provide their additional personal data in the registration "
                                                     "form. In any case, site access is only granted after a manager "
                                                     "explicitly enables it for the registrants."))
    during_registration_preselected = BooleanField(_('Preselect during user registration'),
                                                   [HiddenUnless('during_registration')], widget=SwitchWidget(),
                                                   description=_("Preselect the option to request site access during "
                                                                 "registration. Recommended if most registrants will "
                                                                 "need it."))
    during_registration_required = BooleanField(_('Require during user registration'),
                                                [HiddenUnless('during_registration_preselected')],
                                                widget=SwitchWidget(),
                                                description=_("Require all users to provide data for site access. "
                                                              "Registration without entering the data will not be "
                                                              "possible."))
    start_dt_override = IndicoDateTimeField(_('Start date override'), [Optional()],
                                            description=_("If set, CERN access will be granted starting at the "
                                                          "specified date instead of the event's start date"))
    end_dt_override = IndicoDateTimeField(_('End date override'), [Optional(), LinkedDateTime('start_dt_override',
                                                                                              not_equal=True)],
                                          description=_("If set, CERN access will be granted until the specified date "
                                                        "instead of the event's end date"))

    def __init__(self, *args, **kwargs):
        super(CERNAccessForm, self).__init__(*args, **kwargs)
        regforms = get_regforms(self.event)
        self._regform_map = {unicode(rf.id): rf for rf in regforms}
        self.regforms.choices = [(unicode(rf.id), rf.title) for rf in regforms]
        self.start_dt_override.default_time = self.event.start_dt_local.time()
        self.end_dt_override.default_time = self.event.end_dt_local.time()

    def validate_start_dt_override(self, field):
        if bool(self.start_dt_override.data) != bool(self.end_dt_override.data):
            raise ValidationError(_('You need to specify both date overrides or neither of them.'))

    validate_end_dt_override = validate_start_dt_override
class EventSettingsForm(PaymentEventSettingsFormBase):

    use_event_api_keys = BooleanField(
        _('Use event API keys'),
        [Optional()],
        default=False,
        description=_('Override the organization Stripe API keys.'),
        widget=SwitchWidget(),
    )
    pub_key = StringField(
        _('Publishable key'), [
            HiddenUnless('use_event_api_keys'),
            UsedIf(lambda form, _: form.use_event_api_keys.data),
            DataRequired(),
        ],
        description=_('Publishable API key for the stripe.com account'))
    sec_key = StringField(
        _('Secret key'), [
            HiddenUnless('use_event_api_keys'),
            UsedIf(lambda form, _: form.use_event_api_keys.data),
            DataRequired(),
        ],
        description=_('Secret API key for the stripe.com account'))
    org_name = StringField(_('Organizer name'), [Optional()],
                           default='Organization',
                           description=_('Name of the event organizer'))
    description = StringField(
        _('Description'), [Optional()],
        default='Payment for conference',
        description=_(
            'A description of the product or service being purchased'))
    require_postal_code = BooleanField(
        _('Require postal code input'),
        [Optional()],
        default=False,
        description=_(
            'Require registrants to input their postal code when filling the'
            ' payment form. Enabling this will decrease the chance of the'
            ' payment being marked as fraudulent.'),
        widget=SwitchWidget(),
    )
Beispiel #16
0
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')])
Beispiel #17
0
class TimetablePDFExportForm(IndicoForm):
    _pdf_options_fields = {'pagesize', 'fontsize', 'firstPageNumber'}

    advanced = BooleanField(_("Advanced timetable"), widget=SwitchWidget(),
                            description=_("Advanced customization options"))
    document_settings = IndicoSelectMultipleCheckboxBooleanField(_('Document settings'), [HiddenUnless('advanced')],
                                                                 choices=_DOCUMENT_SETTINGS_CHOICES)
    contribution_info = IndicoSelectMultipleCheckboxBooleanField(_('Contributions related info'),
                                                                 [HiddenUnless('advanced')],
                                                                 choices=_CONTRIBUTION_CHOICES)
    session_info = IndicoSelectMultipleCheckboxBooleanField(_('Sessions related info'), [HiddenUnless('advanced')],
                                                            choices=_SESSION_CHOICES)
    visible_entries = IndicoSelectMultipleCheckboxBooleanField(_('Breaks and contributions'),
                                                               [HiddenUnless('advanced')],
                                                               choices=_VISIBLE_ENTRIES_CHOICES)
    other = IndicoSelectMultipleCheckboxBooleanField(_('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
Beispiel #18
0
class AbstractSubmissionSettingsForm(IndicoForm):
    """Settings form for abstract submission"""

    announcement = IndicoMarkdownField(_('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 = IndicoMarkdownField(
        _('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.'))
Beispiel #19
0
class SingleChoiceConfigForm(object):
    display_type = IndicoRadioField(_('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 = IndicoRadioField(_('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'))
Beispiel #20
0
class AbstractReviewingSettingsForm(IndicoForm):
    """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."))
    reviewing_instructions = IndicoMarkdownField(_('Reviewing Instructions'), editor=True,
                                                 description=_("These instructions will be displayed right before the "
                                                               "reviewing form."))
    judgment_instructions = IndicoMarkdownField(_('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
Beispiel #21
0
class AbstractJudgmentFormBase(IndicoForm):
    """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=lambda obj: obj.title_with_group,
                                      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..."),
                                                              'class': 'grow'})
    # 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
Beispiel #22
0
class MenuBuiltinEntryForm(IndicoForm):
    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
Beispiel #23
0
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'))
    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_needs_vc_assistance(self, field):
        if field.data and not self.uses_vc.data:
            raise ValidationError(_('Videoconference equipment is not used.'))
Beispiel #24
0
class ConferenceLayoutForm(LoggedLayoutForm):
    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())
    name_format = IndicoEnumSelectField(_('Name format'), enum=NameFormat, none=_('Inherit from user preferences'),
                                        description=_('Format in which names are displayed'))

    # 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 Indico 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.'))
Beispiel #25
0
    def __init__(self, *args, **kwargs):
        defaults = kwargs['obj']
        if defaults.host_user is None and defaults.host is not None:
            host = principal_from_identifier(defaults.host)
            defaults.host_choice = 'myself' if host == session.user else 'someone_else'
            defaults.host_user = None if host == session.user else host

        allow_webinars = current_plugin.settings.get('allow_webinars')

        if allow_webinars:
            for field_name in {
                    'mute_audio', 'mute_participant_video', 'waiting_room'
            }:
                inject_validators(self, field_name,
                                  [HiddenUnless('meeting_type', 'regular')])

        super().__init__(*args, **kwargs)

        if not allow_webinars:
            del self.meeting_type
Beispiel #26
0
class RegistrationFormForm(IndicoForm):
    _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 Indico 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 = IndicoEnumSelectField(
        _("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, max=999999.99),
            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'),
                                              [IndicoEmail()],
                                              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().__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 {} 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())
Beispiel #27
0
class AbstractReviewForm(IndicoForm):
    """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 = IndicoEnumSelectField(_("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 = IndicoQuerySelectMultipleCheckboxField(
        _("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_')
            }
        }
Beispiel #28
0
class VCRoomForm(VCRoomFormBase):
    """Contains all information concerning a Zoom booking."""

    advanced_fields = {
        'mute_audio', 'mute_host_video', 'mute_participant_video'
    } | VCRoomFormBase.advanced_fields

    skip_fields = advanced_fields | VCRoomFormBase.conditional_fields

    meeting_type = IndicoRadioField(
        _('Meeting Type'),
        description=_('The type of Zoom meeting to be created'),
        orientation='horizontal',
        choices=[('regular', _('Regular Meeting')), ('webinar', _('Webinar'))])

    host_choice = IndicoRadioField(_('Meeting Host'), [DataRequired()],
                                   choices=[('myself', _('Myself')),
                                            ('someone_else', _('Someone else'))
                                            ])

    host_user = PrincipalField(
        _('User'),
        [HiddenUnless('host_choice', 'someone_else'),
         DataRequired()])

    password = StringField(
        _('Passcode'),
        [DataRequired(), IndicoRegexp(r'^\d{8,10}$')],
        description=_('Meeting passcode (8-10 digits)'))

    password_visibility = IndicoRadioField(
        _('Passcode visibility'),
        description=_("Who should be able to know this meeting's passcode"),
        orientation='horizontal',
        choices=[('everyone', _('Everyone')),
                 ('logged_in', _('Logged-in users')),
                 ('registered', _('Registered participants')),
                 ('no_one', _('No one'))])

    mute_audio = BooleanField(
        _('Mute audio'),
        widget=SwitchWidget(),
        description=_('Participants will join the VC room muted by default '))

    mute_host_video = BooleanField(
        _('Mute video (host)'),
        widget=SwitchWidget(),
        description=_('The host will join the VC room with video disabled'))

    mute_participant_video = BooleanField(
        _('Mute video (participants)'),
        widget=SwitchWidget(),
        description=_(
            'Participants will join the VC room with video disabled'))

    waiting_room = BooleanField(
        _('Waiting room'),
        widget=SwitchWidget(),
        description=_(
            'Participants may be kept in a waiting room by the host'))

    description = TextAreaField(
        _('Description'), description=_('Optional description for this room'))

    def __init__(self, *args, **kwargs):
        defaults = kwargs['obj']
        if defaults.host_user is None and defaults.host is not None:
            host = principal_from_identifier(defaults.host)
            defaults.host_choice = 'myself' if host == session.user else 'someone_else'
            defaults.host_user = None if host == session.user else host

        allow_webinars = current_plugin.settings.get('allow_webinars')

        if allow_webinars:
            for field_name in {
                    'mute_audio', 'mute_participant_video', 'waiting_room'
            }:
                inject_validators(self, field_name,
                                  [HiddenUnless('meeting_type', 'regular')])

        super().__init__(*args, **kwargs)

        if not allow_webinars:
            del self.meeting_type

    def validate_host_choice(self, field):
        if field.data == 'myself':
            self._check_zoom_user(session.user)

    def validate_host_user(self, field):
        if self.host_choice.data == 'someone_else':
            self._check_zoom_user(field.data)

    def _check_zoom_user(self, user):
        if find_enterprise_email(user) is None:
            raise ValidationError(_('This user has no Zoom account'))

    def validate_name(self, field):
        # Duplicate names are fine on Zoom
        pass

    @generated_data
    def host(self):
        if self.host_choice is None:
            return None
        elif self.host_choice.data == 'myself':
            return session.user.identifier
        else:
            return self.host_user.data.identifier if self.host_user.data else None
Beispiel #29
0
class ReminderForm(IndicoForm):
    recipient_fields = {'recipients', 'send_to_participants'}
    schedule_fields = {'schedule_type', 'absolute_dt', 'relative_delta'}
    schedule_recipient_fields = recipient_fields | schedule_fields

    # Schedule
    schedule_type = IndicoRadioField(
        _('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_dt = IndicoDateTimeField(_('Date'), [
        HiddenUnless('schedule_type', 'absolute'),
        DataRequired(),
        DateTimeRange()
    ])
    # 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."
        ))
    include_description = BooleanField(
        _('Include description'),
        description=_("Includes the event's description in the email."))

    def __init__(self, *args, **kwargs):
        self.event = kwargs.pop('event')
        self.timezone = self.event.timezone
        super().__init__(*args, **kwargs)
        self.reply_to_address.choices = (list(
            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'))

    @generated_data
    def scheduled_dt(self):
        if self.schedule_type.data == 'absolute':
            if self.absolute_dt.data is None:
                return None
            return self.absolute_dt.data
        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
Beispiel #30
0
class PluginSettingsForm(VCPluginSettingsFormBase):
    _fieldsets = [
        (_('API Credentials'), ['api_key', 'api_secret', 'webhook_token']),
        (_('Zoom Account'), [
            'user_lookup_mode', 'email_domains', 'authenticators',
            'enterprise_domain', 'allow_webinars'
        ]),
        (_('Room Settings'), [
            'mute_audio', 'mute_host_video', 'mute_participant_video',
            'join_before_host', 'waiting_room'
        ]),
        (_('Notifications'),
         ['creation_email_footer', 'send_host_url', 'notification_emails']),
        (_('Access'), ['managers', 'acl'])
    ]

    api_key = StringField(_('API Key'), [DataRequired()])

    api_secret = IndicoPasswordField(_('API Secret'), [DataRequired()],
                                     toggle=True)

    webhook_token = IndicoPasswordField(
        _('Webhook Token'),
        toggle=True,
        description=_("Specify Zoom's webhook token if you want live updates"))

    user_lookup_mode = IndicoEnumSelectField(
        _('User lookup mode'), [DataRequired()],
        enum=UserLookupMode,
        description=_('Specify how Indico should look up the zoom user that '
                      'corresponds to an Indico user.'))

    email_domains = TextListField(
        _('E-mail domains'), [
            HiddenUnless('user_lookup_mode', UserLookupMode.email_domains),
            DataRequired()
        ],
        description=
        _('List of e-mail domains which can use the Zoom API. Indico attempts '
          'to find Zoom accounts using all email addresses of a user which use '
          'those domains.'))

    authenticators = TextListField(
        _('Indico identity providers'), [
            HiddenUnless('user_lookup_mode', UserLookupMode.authenticators),
            DataRequired()
        ],
        description=
        _('Identity providers from which to get usernames. '
          'Indico queries those providers using the email addresses of the user '
          'and attempts to find Zoom accounts having an email address with the '
          'format username@enterprise-domain.'))

    enterprise_domain = StringField(
        _('Enterprise domain'), [
            HiddenUnless('user_lookup_mode', UserLookupMode.authenticators),
            DataRequired()
        ],
        description=_(
            'The domain name used together with the usernames from the Indico '
            'identity provider'))

    allow_webinars = BooleanField(
        _('Allow Webinars (Experimental)'),
        widget=SwitchWidget(),
        description=_(
            'Allow webinars to be created through Indico. Use at your own risk.'
        ))

    mute_audio = BooleanField(
        _('Mute audio'),
        widget=SwitchWidget(),
        description=_('Participants will join the VC room muted by default '))

    mute_host_video = BooleanField(
        _('Mute video (host)'),
        widget=SwitchWidget(),
        description=_('The host will join the VC room with video disabled'))

    mute_participant_video = BooleanField(
        _('Mute video (participants)'),
        widget=SwitchWidget(),
        description=_(
            'Participants will join the VC room with video disabled'))

    join_before_host = BooleanField(
        _('Join Before Host'),
        widget=SwitchWidget(),
        description=_(
            'Allow participants to join the meeting before the host starts the '
            'meeting. Only used for scheduled or recurring meetings.'))

    waiting_room = BooleanField(
        _('Waiting room'),
        widget=SwitchWidget(),
        description=_(
            'Participants may be kept in a waiting room by the host'))

    creation_email_footer = TextAreaField(
        _('Creation email footer'),
        widget=CKEditorWidget(),
        description=_(
            'Footer to append to emails sent upon creation of a VC room'))

    send_host_url = BooleanField(
        _('Send host URL'),
        widget=SwitchWidget(),
        description=_(
            'Whether to send an e-mail with the Host URL to the meeting host upon '
            'creation of a meeting'))

    def validate_authenticators(self, field):
        invalid = set(field.data) - set(multipass.identity_providers)
        if invalid:
            raise ValidationError(
                _('Invalid identity providers: {}').format(
                    escape(', '.join(invalid))))