Add folder name suggestions
[cds-indico.git] / indico / modules / vc / forms.py
blobdd27381d6bed77e86a492b06eda5f466158d82e8
1 # This file is part of Indico.
2 # Copyright (C) 2002 - 2015 European Organization for Nuclear Research (CERN).
4 # Indico is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License as
6 # published by the Free Software Foundation; either version 3 of the
7 # License, or (at your option) any later version.
9 # Indico is distributed in the hope that it will be useful, but
10 # WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 # General Public License for more details.
14 # You should have received a copy of the GNU General Public License
15 # along with Indico; if not, see <http://www.gnu.org/licenses/>.
17 from __future__ import unicode_literals
18 import re
20 from datetime import date
21 from datetime import timedelta
22 from operator import methodcaller, attrgetter
24 from flask_pluginengine import current_plugin
25 from wtforms.ext.dateutil.fields import DateField
26 from wtforms.fields.core import BooleanField, SelectField
27 from wtforms.fields.html5 import IntegerField
28 from wtforms.fields.simple import StringField, HiddenField
29 from wtforms.validators import DataRequired, Length, NumberRange, Optional, Regexp, ValidationError
31 from indico.modules.vc.models import VCRoom, VCRoomStatus
32 from indico.modules.vc.util import full_block_id
33 from indico.util.i18n import _
34 from indico.web.flask.util import url_for
35 from indico.web.forms.base import IndicoForm, generated_data
36 from indico.web.forms.fields import PrincipalListField, IndicoRadioField, EmailListField
37 from indico.web.forms.validators import UsedIf, Exclusive
38 from indico.web.forms.widgets import JinjaWidget, SwitchWidget, TypeaheadWidget
40 ROOM_NAME_RE = re.compile(r'[\w\-]+')
43 class VCRoomField(HiddenField):
44 widget = TypeaheadWidget(min_trigger_length=3)
46 def process_formdata(self, valuelist):
47 if valuelist and valuelist[0].isdigit():
48 self.data = VCRoom.get(valuelist[0])
50 def _value(self):
51 return self.data.id if self.data is not None else None
54 class LinkingWidget(JinjaWidget):
55 """Renders a composite radio/select field"""
57 def __init__(self, **context):
58 super(LinkingWidget, self).__init__('forms/linking_widget.html', **context)
60 def __call__(self, field, **kwargs):
61 form = field._form
62 has_error = {subfield.data: (subfield.data in form.conditional_fields and form[subfield.data].errors)
63 for subfield in field}
64 return super(LinkingWidget, self).__call__(field, form=form, has_error=has_error, **kwargs)
67 class VCPluginSettingsFormBase(IndicoForm):
68 managers = PrincipalListField(_('Managers'), groups=True, serializable=False,
69 description=_('Service managers'))
70 acl = PrincipalListField(_('ACL'), groups=True, serializable=False,
71 description=_('Users and Groups authorised to create videoconference rooms'))
72 notification_emails = EmailListField(_('Notification email addresses'),
73 description=_('Notifications about videoconference rooms are sent to '
74 'these email addresses (one per line).'))
77 class VCRoomLinkFormBase(IndicoForm):
78 conditional_fields = {'contribution', 'block'}
80 linking = IndicoRadioField(_("Link to"), [DataRequired()],
81 choices=[('event', _("Event")),
82 ('contribution', _("Contribution")),
83 ('block', _("Session"))],
84 widget=LinkingWidget())
85 contribution = SelectField(_("Contribution"),
86 [UsedIf(lambda form, field: form.linking.data == 'contribution'), DataRequired()])
87 block = SelectField(_("Session block"),
88 [UsedIf(lambda form, field: form.linking.data == 'block'), DataRequired()])
90 show = BooleanField(_('Show room'),
91 widget=SwitchWidget(),
92 description=_('Display this room on the event page'))
94 def __init__(self, *args, **kwargs):
95 self.event = kwargs.pop('event')
96 super(VCRoomLinkFormBase, self).__init__(*args, **kwargs)
97 contrib_choices = [(contrib.id, contrib.title) for contrib in
98 sorted(self.event.getContributionList(), key=attrgetter('title'))]
99 block_choices = [(full_block_id(block), block.getFullTitle()) for block in
100 sorted(self.event.getSessionSlotList(), key=methodcaller('getFullTitle'))]
101 self.contribution.choices = [('', _("Please select a contribution"))] + contrib_choices
102 self.block.choices = [('', _("Please select a session block"))] + block_choices
103 self.linking._form = self
106 class VCRoomAttachFormBase(VCRoomLinkFormBase):
107 room = VCRoomField(
108 _("Room to link"), [DataRequired()],
109 description=_("Please start writing the name of the room you would like to attach. "
110 "Indico will suggest existing rooms."))
112 def __init__(self, *args, **kwargs):
113 super(VCRoomAttachFormBase, self).__init__(*args, **kwargs)
114 self.room.widget.search_url = url_for('.manage_vc_rooms_search', self.event, service=kwargs.pop('service'))
117 class VCRoomFormBase(VCRoomLinkFormBase):
118 advanced_fields = {'show'}
119 skip_fields = advanced_fields | VCRoomLinkFormBase.conditional_fields
121 name = StringField(_('Name'), [DataRequired(), Length(min=3, max=60), Regexp(ROOM_NAME_RE)],
122 description=_('The name of the room. It can contain only alphanumerical characters, underscores '
123 'and dashes. No spaces allowed.'))
125 def validate_name(self, field):
126 if field.data:
127 room = VCRoom.find_first(VCRoom.name == field.data, VCRoom.status != VCRoomStatus.deleted,
128 VCRoom.type == self.service_name)
129 if room and room != self.vc_room:
130 raise ValidationError(_("There is already a room with this name"))
132 def __init__(self, *args, **kwargs):
133 super(VCRoomFormBase, self).__init__(*args, **kwargs)
134 self.vc_room = kwargs.pop('vc_room')
135 self.service_name = current_plugin.service_name
138 class VCRoomListFilterForm(IndicoForm):
139 direction = SelectField(_('Sort direction'), [DataRequired()],
140 choices=[('asc', _('Ascending')), ('desc', _('Descending'))])
141 abs_start_date = DateField(_('Start Date'), [Optional(), Exclusive('rel_start_date')],
142 parse_kwargs={'dayfirst': True})
143 abs_end_date = DateField(_('End Date'), [Optional(), Exclusive('rel_end_date')],
144 parse_kwargs={'dayfirst': True})
145 rel_start_date = IntegerField(_('Days in the past'), [Optional(), Exclusive('abs_start_date'), NumberRange(min=0)],
146 default=0)
147 rel_end_date = IntegerField(_('Days in the future'), [Optional(), Exclusive('abs_end_date'), NumberRange(min=0)],
148 default=7)
150 @generated_data
151 def start_date(self):
152 if self.abs_start_date.data is None and self.rel_start_date.data is None:
153 return None
154 return self.abs_start_date.data or (date.today() - timedelta(days=self.rel_start_date.data))
156 @generated_data
157 def end_date(self):
158 if self.abs_end_date.data is None and self.rel_end_date.data is None:
159 return None
160 return self.abs_end_date.data or (date.today() + timedelta(days=self.rel_end_date.data))