class AVRequestManagerForm(RequestManagerForm): custom_webcast_url = URLField( _('Webcast URL'), description=_( "Custom URL to view the webcast. Can contain {event_id} which will be " "replaced with the ID of this event.")) webcast_hidden = BooleanField( _('Hide webcast'), description=_('Do not show a link to the webcast on the event page'))
def _extend_top_menu(self, sender, **kwargs): if not session.user or not is_av_manager(session.user): return return TopMenuItem('services-cern-audiovisual', _('Webcast/Recording'), url_for_plugin('audiovisual.request_list'), section='services')
def _update_audiences(self): audiences = [ ('', _("No restriction - everyone can watch the public webcast")) ] audiences += sorted( (x['audience'], x['audience']) for x in current_plugin.settings.get('webcast_audiences')) self.webcast_audience.choices = audiences
class TalkPlaceholder(Placeholder): name = 'talk_title' required = True description = _("The title of the user's talk") @classmethod def render(cls, definition, agreement): return _talk_info_from_agreement_data(agreement.event, agreement.data)[2]
class AVRequestForm(RequestFormBase): services = IndicoSelectMultipleCheckboxField(_('Services'), [DataRequired()], choices=SERVICES.items(), widget=JinjaWidget('service_type_widget.html', 'audiovisual'), description=_("Please choose whether you want a webcast, recording or " "both.")) all_contributions = BooleanField(_('All contributions'), description=_('Uncheck this if you want to select only certain contributions.')) contributions = IndicoSelectMultipleCheckboxField(_('Contributions'), [UsedIf(lambda form, field: not form.all_contributions.data), DataRequired()], widget=JinjaWidget('contribution_list_widget.html', 'audiovisual', SubContribution=SubContribution)) webcast_audience = SelectField(_('Webcast Audience'), description=_("Select the audience to which the webcast will be restricted")) comments = TextAreaField(_('Comments'), description=_('If you have any additional comments or instructions, please write them ' 'down here.')) def __init__(self, *args, **kwargs): super(AVRequestForm, self).__init__(*args, **kwargs) self._update_audiences() self._update_contribution_fields() def _update_audiences(self): audiences = [('', _("No restriction - everyone can watch the public webcast"))] audiences += sorted((x['audience'], x['audience']) for x in current_plugin.settings.get('webcast_audiences')) self.webcast_audience.choices = audiences def _update_contribution_fields(self): if self.event.type == 'lecture': # lectures don't have contributions del self.all_contributions del self.contributions else: choices = self.contributions.choices = [] disabled_contribs = self.contributions._disabled_contributions = [] contributions = self.contributions._contributions = {} is_manager = is_av_manager(session.user) selected = set(self.request.data.get('contributions', [])) if self.request else set() for contrib, capable, custom_room in get_contributions(self.event): is_subcontrib = isinstance(contrib, SubContribution) id_ = contribution_id(contrib) contributions[id_] = contrib line = Markup(render_template('audiovisual:contribution_list_entry.html', contrib=contrib, is_subcontrib=is_subcontrib, capable=capable, custom_room=custom_room)) if not capable and not is_manager and contrib.id not in selected: disabled_contribs.append((contrib, line)) else: choices.append((id_, line))
def _talk_info_from_agreement_data(event, data): if data['type'] == 'lecture_speaker': return 'lecture', event.url, event.title elif data['type'] != 'contribution': raise ValueError('Unexpected data type: {}'.format(data['type'])) obj = contribution_by_id(event, data['contribution']) if not obj: raise RuntimeError(_('Contribution deleted')) if isinstance(obj, SubContribution): return 'subcontribution', url_for('contributions.display_subcontribution', obj), obj.title else: return 'contribution', url_for('contributions.display_contribution', obj), obj.title
def _get_breadcrumbs(self): return render_breadcrumbs(_('Webcast/Recording'))
class PluginSettingsForm(IndicoForm): managers = PrincipalListField( _('Managers'), groups=True, description=_( 'List of users who can manage recording/webcast requests.')) notification_emails = EmailListField( _('Notification email addresses'), description=_( 'Notifications about recording/webcast requests are sent to ' 'these email addresses (one per line).')) webcast_audiences = MultipleItemsField( _('Webcast Audiences'), fields=[{ 'id': 'audience', 'caption': _('Audience'), 'required': True }], unique_field='audience', description=_('List of audiences for non-public webcasts.')) webcast_ping_url = URLField( _('Webcast Ping URL'), description=_( "A ping is sent via HTTP GET to this URL whenever a webcast request " "enters/leaves the 'accepted' state.")) webcast_url = URLField( _('Webcast URL'), [DataRequired()], description=_( "The URL to watch the webcast for an event. Can contain {event_id} which " "will be replaced with the ID of the event.")) agreement_paper_url = URLField( _('Agreement Paper URL'), description=_( "The URL to the agreement that can be printed and signed offline.") ) recording_cds_url = URLField( _('CDS URL'), description=_( "The URL used when creating recording links. Must contain the {cds_id} " "placeholder.")) def validate_recording_cds_url(self, field): if field.data and '{cds_id}' not in field.data: raise ValidationError('{cds_id} placeholder is missing')
class RequestListFilterForm(IndicoForm): direction = SelectField(_('Sort direction'), [DataRequired()], choices=[('asc', _('Ascending')), ('desc', _('Descending'))]) granularity = SelectField(_('Granularity'), [DataRequired()], choices=[('events', _('Events')), ('talks', _('Talks'))]) state = IndicoEnumSelectField(_('Request state'), enum=RequestState, skip={RequestState.withdrawn}, none=_('Any state')) abs_start_date = IndicoDateField( _('Start Date'), [Optional(), Exclusive('rel_start_date')]) abs_end_date = IndicoDateField( _('End Date'), [Optional(), Exclusive('rel_end_date')]) rel_start_date = IntegerField( _('Days in the past'), [Optional(), Exclusive('abs_start_date'), NumberRange(min=0)]) rel_end_date = IntegerField( _('Days in the future'), [Optional(), Exclusive('abs_end_date'), NumberRange(min=0)]) @generated_data def start_date(self): if self.abs_start_date.data is None and self.rel_start_date.data is None: return None return self.abs_start_date.data or ( date.today() - timedelta(days=self.rel_start_date.data)) @generated_data def end_date(self): if self.abs_end_date.data is None and self.rel_end_date.data is None: return None return self.abs_end_date.data or ( date.today() + timedelta(days=self.rel_end_date.data))
def _get_breadcrumbs(self): return render_breadcrumbs(_('Videoconference assistance'))
class AVRequest(RequestDefinitionBase): name = 'webcast-recording' title = _('Webcast / Recording') form = AVRequestForm manager_form = AVRequestManagerForm form_defaults = {'all_contributions': True} # needed for templates where we only have access to the definition util = { 'count_capable_contributions': count_capable_contributions, 'get_av_capable_rooms': get_av_capable_rooms, 'event_has_empty_sessions': event_has_empty_sessions, 'get_selected_contributions': get_selected_contributions, 'get_selected_services': get_selected_services, 'all_agreements_signed': all_agreements_signed } @classmethod def can_be_managed(cls, user): return is_av_manager(user) @classmethod def get_manager_notification_emails(cls): return set(current_plugin.settings.get('notification_emails')) @classmethod def get_notification_reply_email(cls): return (current_plugin.settings.get('notification_reply_email') or super(AVRequest, cls).get_notification_reply_email()) @classmethod def get_notification_template(cls, name, **context): context['SubContribution'] = SubContribution return super(AVRequest, cls).get_notification_template(name, **context) @classmethod def render_form(cls, event, **kwargs): kwargs['default_webcast_url'] = cls.plugin.settings.get('webcast_url') return super(AVRequest, cls).render_form(event, **kwargs) @classmethod def create_manager_form(cls, req): form = super(AVRequest, cls).create_manager_form(req) if 'webcast' not in req.data['services']: del form.custom_webcast_url del form.webcast_hidden return form @classmethod def send(cls, req, data): if (req.id is not None and req.state == RequestState.accepted and ('webcast' in req.data['services']) != ('webcast' in data['services'])): send_webcast_ping.delay() super(AVRequest, cls).send(req, data) req.data['identifiers'] = get_data_identifiers(req) flag_modified(req, 'data') @classmethod def withdraw(cls, req, notify_event_managers=True): if req.state == RequestState.accepted and 'webcast' in req.data[ 'services']: send_webcast_ping.delay() super(AVRequest, cls).withdraw(req, notify_event_managers) @classmethod def accept(cls, req, data, user): if 'webcast' in req.data['services']: send_webcast_ping.delay() super(AVRequest, cls).accept(req, data, user) @classmethod def reject(cls, req, data, user): if req.state == RequestState.accepted and 'webcast' in req.data[ 'services']: send_webcast_ping.delay() super(AVRequest, cls).reject(req, data, user) @classmethod def manager_save(cls, req, data): super(AVRequest, cls).manager_save(req, data) req.data['custom_webcast_url'] = data.get('custom_webcast_url') req.data['webcast_hidden'] = data.get('webcast_hidden', False) flag_modified(req, 'data')
class SpeakerReleaseAgreement(AgreementDefinitionBase): name = 'cern-speaker-release' title = _('Speaker Release') description = _( 'For talks to be recorded, all involved speakers need to sign the speaker release form.' ) form_template_name = 'agreement_form.html' disabled_reason = _( 'There are no agreements to sign. This means that either no recording request has been ' 'done/accepted or there are no speakers assigned to the contributions in question.' ) @classmethod def can_access_api(cls, user, event): return super(SpeakerReleaseAgreement, cls).can_access_api( user, event) or is_av_manager(user) @classmethod def extend_api_data(cls, event, person, agreement, data): data['speaker'] = { 'id': person.data['id'], 'person_id': person.data['person_id'], 'name': person.name, 'email': person.email } if person.data['type'] == 'lecture_speaker': data['contrib'] = unicode(event.id) elif person.data['type'] == 'contribution': data['contrib'] = person.data['contribution'] @classproperty @classmethod @memoize_request def paper_form_url(cls): return cls.plugin.settings.get('agreement_paper_url') @classmethod def render_form(cls, agreement, form, **kwargs): event = agreement.event contrib = None is_subcontrib = False if agreement.data['type'] == 'contribution': talk_type = 'contribution' contrib = contribution_by_id(event, agreement.data['contribution']) if not contrib: raise NotFound is_subcontrib = isinstance(contrib, SubContribution) elif agreement.data['type'] == 'lecture_speaker': talk_type = 'lecture' else: raise ValueError('Unexpected type: {}'.format( agreement.data['type'])) kwargs.update({ 'contrib': contrib, 'is_subcontrib': is_subcontrib, 'talk_type': talk_type, 'event': event }) return super(SpeakerReleaseAgreement, cls).render_form(agreement, form, **kwargs) @classmethod def render_data(cls, event, data): try: type_, url, title = _talk_info_from_agreement_data(event, data) except RuntimeError as e: return ['({})'.format(e)] return [Markup('<a href="{}">{}</a>'.format(url, escape(title)))] @classmethod def iter_people(cls, event): req = Request.find_latest_for_event(event, AVRequest.name) if not req or req.state != RequestState.accepted or 'recording' not in req.data[ 'services']: return if event.type == 'lecture': for link in event.person_links: yield SpeakerPersonInfo(link.full_name, link.email or None, data={ 'type': 'lecture_speaker', 'id': link.id, 'person_id': link.person_id }) else: contribs = [x[0] for x in get_selected_contributions(req)] for contrib in contribs: for link in contrib.person_links: if not link.is_speaker: continue yield SpeakerPersonInfo(link.full_name, link.email or None, data={ 'type': 'contribution', 'contribution': contribution_id(contrib), 'id': link.id, 'person_id': link.person_id })
class AVRequestManagerForm(RequestManagerForm): custom_webcast_url = URLField(_('Webcast URL'), description=_("Custom URL to view the webcast. Can contain {event_id} which will be " "replaced with the ID of this event."))
class PluginSettingsForm(IndicoForm): managers = PrincipalListField(_('Managers'), groups=True, description=_('List of users who can manage recording/webcast requests.')) notification_emails = EmailListField(_('Notification email addresses'), description=_('Notifications about recording/webcast requests are sent to ' 'these email addresses (one per line).')) notification_reply_email = StringField(_('E-mail notification "reply" address'), [IndicoEmail()], description=_('Notifications that are sent to event managers will use ' 'this address in their "Reply-To:" fields.')) webcast_audiences = MultipleItemsField(_('Webcast Audiences'), fields=[{'id': 'audience', 'caption': _('Audience'), 'required': True}], unique_field='audience', description=_('List of audiences for non-public webcasts.')) webcast_ping_url = URLField(_('Webcast Ping URL'), description=_("A ping is sent via HTTP GET to this URL whenever a webcast request " "enters/leaves the 'accepted' state.")) webcast_url = URLField(_('Webcast URL'), [DataRequired()], description=_("The URL to watch the webcast for an event. Can contain {event_id} which " "will be replaced with the ID of the event.")) agreement_paper_url = URLField(_('Agreement Paper URL'), description=_("The URL to the agreement that can be printed and signed offline.")) recording_cds_url = URLField(_('CDS URL'), description=_("The URL used when creating recording links. Must contain the {cds_id} " "placeholder.")) room_feature = QuerySelectField(_("Room feature"), [DataRequired()], allow_blank=True, query_factory=lambda: RoomFeature.query, get_label='title', description=_("The feature indicating that a room supports webcast/recording.")) def validate_recording_cds_url(self, field): if field.data and '{cds_id}' not in field.data: raise ValidationError('{cds_id} placeholder is missing')