def validate_body(self, field): missing = get_missing_placeholders('cern-access-email', field.data, regform=self.regform, registration=None) if missing: raise ValidationError( _('Missing placeholders: {}').format(', '.join(missing)))
class RegistrationFormPersonalDataForm(AccessIdentityDataForm): request_cern_access = BooleanField(_('Request access to the CERN site'), widget=SwitchWidget()) @classmethod def _add_fields_hidden_unless(cls): for field_name in ('birth_date', 'nationality', 'birth_place'): inject_validators(RegistrationFormPersonalDataForm, field_name, [UsedIf(lambda form, field: form.request_cern_access.data)], early=True)
def _print_badge_template(self, template, regform, **kwargs): access_tpl = self.settings.get('access_ticket_template') if not access_tpl: return if template == access_tpl or template.backside_template == access_tpl: if (not regform.cern_access_request or (regform.cern_access_request and regform.cern_access_request.request_state != CERNAccessRequestState.active)): raise Forbidden(_('This badge cannot be printed because it uses the CERN access ticket ' 'template without an active CERN access request'))
def __init__(self, *args, **kwargs): reset_text = (Markup('<a id="reset-cern-access-email">{}</a><br>') .format(_('Click here to reset subject and body to the default text.'))) super(GrantAccessEmailForm, self).__init__(*args, recipients=[], **kwargs) self.body.description = reset_text + render_placeholder_info('cern-access-email', regform=self.regform, registration=None) del self.cc_addresses del self.copy_for_sender del self.attach_ticket del self.recipients
class GrantAccessEmailForm(EmailRegistrantsForm): save_default = BooleanField(_("Save as default"), widget=SwitchWidget(), description=_("Save this email's content as the default that will be used the next " "time a CERN access request is sent for a registrant in this event.")) def __init__(self, *args, **kwargs): reset_text = (Markup('<a id="reset-cern-access-email">{}</a><br>') .format(_('Click here to reset subject and body to the default text.'))) super(GrantAccessEmailForm, self).__init__(*args, recipients=[], **kwargs) self.body.description = reset_text + render_placeholder_info('cern-access-email', regform=self.regform, registration=None) del self.cc_addresses del self.copy_for_sender del self.attach_ticket del self.recipients def validate_body(self, field): missing = get_missing_placeholders('cern-access-email', field.data, regform=self.regform, registration=None) if missing: raise ValidationError(_('Missing placeholders: {}').format(', '.join(missing)))
class AccessIdentityDataForm(IndicoForm): birth_date = IndicoDateField(_('Birth date'), [DataRequired()]) nationality = SelectField(_('Country of birth'), [DataRequired()]) birth_place = StringField(_('Place of birth'), [DataRequired()]) by_car = BooleanField(_('Are you bringing your own car?'), [Optional()], widget=SwitchWidget()) license_plate = StringField( _('License plate'), [ HiddenUnless('by_car'), Length(min=3), IndicoRegexp(r'^[0-9A-Za-z]+([- ][ ]*[0-9A-Za-z]+)*$', message=_('Wrong format. Only letters and numbers separated by dashes (-) or spaces allowed')) ] ) def __init__(self, *args, **kwargs): super(AccessIdentityDataForm, self).__init__(*args, **kwargs) self.nationality.choices = [('', '')] + sorted(get_countries().iteritems(), key=itemgetter(1)) def validate_birth_date(self, field): if field.data > datetime.now().date(): raise ValidationError(_('The specified date is in the future')) def validate_license_plate(self, field): if self.by_car.data and not sanitize_license_plate(field.data): raise ValidationError(_('Please insert a valid license plate number!'))
def _process(self): if not self.registrations: raise NoReportError.wrap_exc(BadRequest(_("The selected registrants have been removed."))) registration = self.registrations[0] email_body = replace_placeholders('cern-access-email', request.form['body'], regform=self.regform, registration=registration) email_subject = replace_placeholders('cern-access-email', request.form['subject'], regform=self.regform, registration=registration) tpl = get_template_module('cern_access:emails/identity_data_form_email.html', registration=registration, email_subject=email_subject, email_body=email_body) html = render_template('events/registration/management/email_preview.html', subject=tpl.get_subject(), body=tpl.get_body()) return jsonify(html=html)
def _process(self): access_request = self.registration.cern_access_request if not access_request or access_request.has_identity_info: raise UserValueError(_('The personal data for this registrant has already been entered')) form = AccessIdentityDataForm() if form.validate_on_submit(): form.populate_obj(access_request) db.session.flush() send_ticket(self.registration) return jsonify_data(html=render_plugin_template('cern_access_status.html', registration=self.registration, header=False)) return jsonify_template('identity_data_form_management.html', render_plugin_template, form=form, registration=self.registration)
def _send_adams_http_request(method, data): from indico_cern_access.plugin import CERNAccessPlugin url = CERNAccessPlugin.settings.get('adams_url') credentials = (CERNAccessPlugin.settings.get('username'), CERNAccessPlugin.settings.get('password')) request_headers = {'Content-Type': 'application/json'} try: r = requests.request(method, url, data=json.dumps(data), headers=request_headers, auth=credentials) r.raise_for_status() except requests.exceptions.RequestException: CERNAccessPlugin.logger.exception('Request to ADAMS failed (%r)', data) raise AdamsError(_('Sending request to ADAMS failed')) return r.status_code == requests.codes.all_ok
def _form_validated(self, form, **kwargs): """ Forbid to disable the tickets when access to CERN is requested and to use CERN access ticket template with regforms without active CERN access request. """ if not isinstance(form, TicketsForm): return regform = RegistrationForm.get_or_404(request.view_args['reg_form_id']) if regform.cern_access_request and regform.cern_access_request.is_active and not form.tickets_enabled.data: err = _('This form is used to grant CERN site access so ticketing must be enabled') form.tickets_enabled.errors.append(err) return False access_tpl = self.settings.get('access_ticket_template') ticket_template = DesignerTemplate.get_or_404(form.ticket_template_id.data) if not access_tpl: return if ticket_template == access_tpl or ticket_template.backside_template == access_tpl: if (not regform.cern_access_request or (regform.cern_access_request and regform.cern_access_request.request_state != CERNAccessRequestState.active)): form.ticket_template_id.errors.append(_('The selected template can only be used with an ' 'active CERN access request')) return False
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
def validate_birth_date(self, field): if field.data > datetime.now().date(): raise ValidationError(_('The specified date is in the future'))
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.' ))
class CERNAccessRequestState(RichIntEnum): __titles__ = [_('Not requested'), _('Accepted'), _('Withdrawn')] not_requested = 0 active = 1 withdrawn = 2
class PluginSettingsForm(IndicoForm): adams_url = URLField(_('ADaMS URL'), [DataRequired()], description=_('The URL of the ADaMS REST API')) username = StringField( _('Username'), [DataRequired()], description=_('The login used to authenticate with ADaMS service')) password = IndicoPasswordField( _('Password'), [DataRequired()], description=_('The password used to authenticate with ADaMS service')) secret_key = IndicoPasswordField( _('Secret key'), [DataRequired()], description=_('Secret key to sign ADaMS requests')) authorized_users = PrincipalListField( _('Authorized users'), allow_groups=True, description=_('List of users/groups who can send requests')) excluded_categories = MultipleItemsField('Excluded categories', fields=[{ 'id': 'id', 'caption': 'Category ID' }]) access_ticket_template = QuerySelectField( _("Access ticket template"), allow_blank=True, blank_text=_("No access ticket selected"), get_label='title', description=_("Ticket template allowing access to CERN")) earliest_start_dt = IndicoDateTimeField( _("Earliest start date"), [Optional()], default_time=time(0, 0), description=_( "The earliest date an event can start to qualify for CERN " "access badges")) delete_personal_data_after = TimeDeltaField( _('Delete personal data'), [DataRequired()], units=('days', ), description=_( 'Personal data will be deleted once the event has ' 'finished and the duration specified here has been ' 'exceeded. Once the data has been deleted, access badges ' 'will not be accessible anymore.')) api_username = StringField(_('Username'), [DataRequired()], description=_('The username to access the API')) api_password = IndicoPasswordField( _('Password'), [DataRequired()], toggle=True, description=_('The password to access the API')) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.access_ticket_template.query = (DesignerTemplate.query.filter( DesignerTemplate.category_id == 0, DesignerTemplate.type == TemplateType.badge).order_by( db.func.lower(DesignerTemplate.title)))
class CERNAccessRequestDefinition(RequestDefinitionBase): name = 'cern-access' title = _('CERN Visitor Badges') form = CERNAccessForm @classmethod def create_form(cls, event, existing_request=None): default_data = cls.form_defaults if existing_request: default_data = dict(existing_request.data) if default_data['start_dt_override']: default_data['start_dt_override'] = dateutil.parser.parse( default_data['start_dt_override']) if default_data['end_dt_override']: default_data['end_dt_override'] = dateutil.parser.parse( default_data['end_dt_override']) with plugin_context(cls.plugin): return cls.form(prefix='request-', obj=FormDefaults(default_data), event=event, request=existing_request) @classmethod def render_form(cls, event, **kwargs): from indico_cern_access.plugin import CERNAccessPlugin kwargs['user_authorized'] = is_authorized_user(session.user) kwargs['category_blacklisted'] = is_category_blacklisted( event.category) kwargs['event_too_early'] = is_event_too_early(event) kwargs['earliest_start_dt'] = CERNAccessPlugin.settings.get( 'earliest_start_dt') return super(CERNAccessRequestDefinition, cls).render_form(event, **kwargs) @classmethod def can_be_managed(cls, user): return False @classmethod def send(cls, req, data): check_access(req) start_dt = data['start_dt_override'] or req.event.start_dt end_dt = data['end_dt_override'] or req.event.end_dt if data['start_dt_override']: data['start_dt_override'] = data['start_dt_override'].isoformat() if data['end_dt_override']: data['end_dt_override'] = data['end_dt_override'].isoformat() times_changed = False if req.id is not None: old_start_dt, old_end_dt = get_access_dates(req) if old_start_dt != start_dt or old_end_dt != end_dt: times_changed = True super(CERNAccessRequestDefinition, cls).send(req, data) update_access_request(req) req.state = RequestState.accepted if times_changed: handle_event_time_update(req.event) @classmethod def withdraw(cls, req, notify_event_managers=False): check_access(req) withdraw_event_access_request(req) super(CERNAccessRequestDefinition, cls).withdraw(req, notify_event_managers)
def validate_license_plate(self, field): if self.by_car.data and not sanitize_license_plate(field.data): raise ValidationError(_('Please insert a valid license plate number!'))
class RegformDataMode(RichIntEnum): __titles__ = [_('No'), _('Yes'), _('Yes (required)')] after_registration = 0 during_registration = 1 during_registration_required = 2
class PluginSettingsForm(IndicoForm): adams_url = URLField(_('ADaMS URL'), [DataRequired()], description=_('The URL of the ADaMS REST API')) username = StringField( _('Username'), [DataRequired()], description=_('The login used to authenticate with ADaMS service')) password = IndicoPasswordField( _('Password'), [DataRequired()], description=_('The password used to authenticate with ADaMS service')) secret_key = IndicoPasswordField( _('Secret key'), [DataRequired()], description=_('Secret key to sign ADaMS requests')) authorized_users = PrincipalListField( _('Authorized users'), groups=True, description=_('List of users/groups who can send requests')) excluded_categories = MultipleItemsField('Excluded categories', fields=[{ 'id': 'id', 'caption': 'Category ID' }]) access_ticket_template = QuerySelectField( _("Access ticket template"), allow_blank=True, blank_text=_("No access ticket selected"), get_label='title', description=_("Ticket template allowing access to CERN")) earliest_start_dt = IndicoDateTimeField( _("Earliest start date"), [Optional()], default_time=time(0, 0), description=_( "The earliest date an event can start to qualify for CERN " "access badges")) def __init__(self, *args, **kwargs): super(PluginSettingsForm, self).__init__(*args, **kwargs) self.access_ticket_template.query = (DesignerTemplate.query.filter( DesignerTemplate.category_id == 0, DesignerTemplate.type == TemplateType.badge).order_by( db.func.lower(DesignerTemplate.title)))
class CERNAccessRequestDefinition(RequestDefinitionBase): name = 'cern-access' title = _('CERN Visitor Badges') form = CERNAccessForm form_defaults = { 'during_registration': True, 'during_registration_preselected': False, 'during_registration_required': False } @classmethod def create_form(cls, event, existing_request=None): default_data = cls.form_defaults if existing_request: default_data = dict(existing_request.data) if default_data['start_dt_override']: default_data['start_dt_override'] = dateutil.parser.parse( default_data['start_dt_override']) if default_data['end_dt_override']: default_data['end_dt_override'] = dateutil.parser.parse( default_data['end_dt_override']) with plugin_context(cls.plugin): return cls.form(prefix='request-', obj=FormDefaults(default_data), event=event, request=existing_request) @classmethod def render_form(cls, event, **kwargs): from indico_cern_access.plugin import CERNAccessPlugin kwargs['user_authorized'] = is_authorized_user(session.user) kwargs['category_blacklisted'] = is_category_blacklisted( event.category) kwargs['event_too_early'] = is_event_too_early(event) kwargs['earliest_start_dt'] = CERNAccessPlugin.settings.get( 'earliest_start_dt') return super(CERNAccessRequestDefinition, cls).render_form(event, **kwargs) @classmethod def can_be_managed(cls, user): return False @classmethod def send(cls, req, data): check_access(req) start_dt = data['start_dt_override'] or req.event.start_dt end_dt = data['end_dt_override'] or req.event.end_dt if data['start_dt_override']: data['start_dt_override'] = data['start_dt_override'].isoformat() if data['end_dt_override']: data['end_dt_override'] = data['end_dt_override'].isoformat() times_changed = False if req.id is not None: old_start_dt, old_end_dt = get_access_dates(req) if old_start_dt != start_dt or old_end_dt != end_dt: times_changed = True super(CERNAccessRequestDefinition, cls).send(req, data) update_access_request(req) req.state = RequestState.accepted if times_changed: handle_event_time_update(req.event) link = "https://indico-user-docs.web.cern.ch/indico-user-docs/cern/cern_access/#granting-access-to-participants" message = _( 'Please note that even though your request has been accepted, you still have to ' 'request badges for each one of your participants. {link}More details here.{endlink}' ).format(link='<a href="{}">'.format(link), endlink='</a>') flash(Markup(message), 'warning') @classmethod def withdraw(cls, req, notify_event_managers=False): withdraw_event_access_request(req) super(CERNAccessRequestDefinition, cls).withdraw(req, notify_event_managers)