add tests. fix form validations
- Legacy-Id: 11219
This commit is contained in:
parent
6ea92cc5a0
commit
ed371396c1
|
@ -137,7 +137,7 @@ class GroupModelChoiceField(forms.ModelChoiceField):
|
|||
|
||||
|
||||
class InterimMeetingModelForm(forms.ModelForm):
|
||||
group = GroupModelChoiceField(queryset=Group.objects.filter(type__in=('wg', 'rg'), state='active').order_by('acronym'))
|
||||
group = GroupModelChoiceField(queryset=Group.objects.filter(type__in=('wg', 'rg'), state__in=('active', 'proposed')).order_by('acronym'), required=False)
|
||||
in_person = forms.BooleanField(required=False)
|
||||
meeting_type = forms.ChoiceField(choices=(
|
||||
("single", "Single"),
|
||||
|
@ -172,9 +172,16 @@ class InterimMeetingModelForm(forms.ModelForm):
|
|||
self.fields['approved'].initial = False
|
||||
self.fields['approved'].widget.attrs['disabled'] = True
|
||||
|
||||
def clean(self):
|
||||
super(InterimMeetingModelForm, self).clean()
|
||||
cleaned_data = self.cleaned_data
|
||||
if not cleaned_data.get('group'):
|
||||
raise forms.ValidationError("You must select a group")
|
||||
|
||||
return self.cleaned_data
|
||||
|
||||
def set_group_options(self):
|
||||
'''Set group options based on user accessing the form'''
|
||||
|
||||
if has_role(self.user, "Secretariat"):
|
||||
return # don't reduce group options
|
||||
if has_role(self.user, "Area Director"):
|
||||
|
@ -215,9 +222,9 @@ class InterimMeetingModelForm(forms.ModelForm):
|
|||
|
||||
class InterimSessionModelForm(forms.ModelForm):
|
||||
date = DatepickerDateField(date_format="yyyy-mm-dd", picker_settings={"autoclose": "1"}, label='Date', required=False)
|
||||
time = forms.TimeField(widget=forms.TimeInput(format='%H:%M'), required=False)
|
||||
time = forms.TimeField(widget=forms.TimeInput(format='%H:%M'), required=True)
|
||||
time_utc = forms.TimeField(required=False)
|
||||
requested_duration = DurationField(required=False)
|
||||
requested_duration = DurationField(required=True)
|
||||
end_time = forms.TimeField(required=False)
|
||||
end_time_utc = forms.TimeField(required=False)
|
||||
remote_instructions = forms.CharField(max_length=1024, required=True)
|
||||
|
@ -247,6 +254,14 @@ class InterimSessionModelForm(forms.ModelForm):
|
|||
path = os.path.join(doc.get_file_path(), doc.filename_with_rev())
|
||||
self.initial['agenda'] = get_document_content(os.path.basename(path), path, markup=False)
|
||||
|
||||
def clean_date(self):
|
||||
'''Date field validator. We can't use required on the input because
|
||||
it is a datepicker widget'''
|
||||
date = self.cleaned_data.get('date')
|
||||
if not date:
|
||||
raise forms.ValidationError('Required field')
|
||||
return date
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
"""NOTE: as the baseform of an inlineformset self.save(commit=True)
|
||||
never gets called"""
|
||||
|
@ -291,68 +306,8 @@ class InterimSessionModelForm(forms.ModelForm):
|
|||
with open(path, "w") as file:
|
||||
file.write(self.cleaned_data['agenda'])
|
||||
|
||||
'''
|
||||
class InterimSessionForm(forms.Form):
|
||||
date = DatepickerDateField(date_format="yyyy-mm-dd", picker_settings={"autoclose": "1"}, label='Date', required=False)
|
||||
time = forms.TimeField(required=False)
|
||||
time_utc = forms.TimeField(required=False)
|
||||
duration = DurationField(required=False)
|
||||
end_time = forms.TimeField(required=False)
|
||||
end_time_utc = forms.TimeField(required=False)
|
||||
remote_instructions = forms.CharField(max_length=1024, required=False)
|
||||
agenda = forms.CharField(required=False, widget=forms.Textarea)
|
||||
agenda_note = forms.CharField(max_length=255, required=False)
|
||||
|
||||
def save(self, request, group, meeting, is_approved):
|
||||
person = request.user.person
|
||||
agenda = self.cleaned_data.get('agenda')
|
||||
agenda_note = self.cleaned_data.get('agenda_note')
|
||||
date = self.cleaned_data.get('date')
|
||||
time = self.cleaned_data.get('time')
|
||||
duration = self.cleaned_data.get('duration')
|
||||
remote_instructions = self.cleaned_data.get('remote_instructions')
|
||||
time = datetime.datetime.combine(date, time)
|
||||
if is_approved:
|
||||
status_id = 'scheda'
|
||||
else:
|
||||
status_id = 'apprw'
|
||||
session = Session.objects.create(
|
||||
meeting=meeting,
|
||||
group=group,
|
||||
requested_by=person,
|
||||
requested_duration=duration,
|
||||
status_id=status_id,
|
||||
type_id='session',
|
||||
remote_instructions=remote_instructions,
|
||||
agenda_note=agenda_note,)
|
||||
assign_interim_session(session, time)
|
||||
|
||||
if agenda:
|
||||
# create objects
|
||||
filename = 'agenda-interim-%s-%s' % (group.acronym, time.strftime("%Y-%m-%d-%H%M"))
|
||||
doc = Document.objects.create(type_id='agenda', group=group, name=filename, rev='00')
|
||||
doc.set_state(State.objects.get(type=doc.type, slug='active'))
|
||||
DocAlias.objects.create(name=doc.name, document=doc)
|
||||
session.sessionpresentation_set.create(document=doc, rev=doc.rev)
|
||||
NewRevisionDocEvent.objects.create(
|
||||
type='new_revision',
|
||||
by=request.user.person,
|
||||
doc=doc,
|
||||
rev=doc.rev,
|
||||
desc='New revision available')
|
||||
# write file
|
||||
path = os.path.join(get_upload_root(meeting), 'agenda', doc.filename_with_rev())
|
||||
directory = os.path.dirname(path)
|
||||
if not os.path.exists(directory):
|
||||
os.makedirs(directory)
|
||||
with open(path, "w") as file:
|
||||
file.write(agenda)
|
||||
|
||||
return session
|
||||
'''
|
||||
|
||||
class InterimAnnounceForm(forms.ModelForm):
|
||||
|
||||
class Meta:
|
||||
model = Message
|
||||
fields = ('to', 'frm', 'cc', 'bcc', 'reply_to', 'subject', 'body')
|
||||
|
@ -367,11 +322,11 @@ class InterimAnnounceForm(forms.ModelForm):
|
|||
|
||||
|
||||
class InterimCancelForm(forms.Form):
|
||||
group = forms.CharField(max_length=255,required=False)
|
||||
group = forms.CharField(max_length=255, required=False)
|
||||
date = forms.DateField(required=False)
|
||||
comments = forms.CharField(required=False, widget=forms.Textarea(attrs={'placeholder': 'enter optional comments here'}))
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(InterimCancelForm, self).__init__(*args, **kwargs)
|
||||
self.fields['group'].widget.attrs['disabled'] = True
|
||||
self.fields['date'].widget.attrs['disabled'] = True
|
||||
self.fields['date'].widget.attrs['disabled'] = True
|
||||
|
|
|
@ -337,11 +337,21 @@ def can_approve_interim_request(meeting, user):
|
|||
|
||||
def can_edit_interim_request(meeting, user):
|
||||
'''Returns True if the user can edit the interim meeting request'''
|
||||
|
||||
if can_approve_interim_request(meeting, user):
|
||||
if meeting.type.slug != 'interim':
|
||||
return False
|
||||
if has_role(user, 'Secretariat'):
|
||||
return True
|
||||
|
||||
return False
|
||||
person = get_person_for_user(user)
|
||||
session = meeting.session_set.first()
|
||||
if not session:
|
||||
return False
|
||||
group = session.group
|
||||
if group.role_set.filter(name='chair', person=person):
|
||||
return True
|
||||
elif can_approve_interim_request(meeting, user):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def can_request_interim_meeting(user):
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import json
|
||||
import os
|
||||
import shutil
|
||||
import datetime
|
||||
|
@ -349,67 +350,67 @@ class InterimTests(TestCase):
|
|||
# no logged in - no tabs
|
||||
r = self.client.get(url)
|
||||
q = PyQuery(r.content)
|
||||
self.assertEqual(len(q("ul.nav-tabs")),0)
|
||||
self.assertEqual(len(q("ul.nav-tabs")), 0)
|
||||
# plain user - no tabs
|
||||
username = "plain"
|
||||
self.client.login(username=username, password= username + "+password")
|
||||
self.client.login(username=username, password=username + "+password")
|
||||
r = self.client.get(url)
|
||||
q = PyQuery(r.content)
|
||||
self.assertEqual(len(q("ul.nav-tabs")),0)
|
||||
self.assertEqual(len(q("ul.nav-tabs")), 0)
|
||||
self.client.logout()
|
||||
# privileged user
|
||||
username = "ad"
|
||||
self.client.login(username=username, password= username + "+password")
|
||||
self.client.login(username=username, password=username + "+password")
|
||||
r = self.client.get(url)
|
||||
q = PyQuery(r.content)
|
||||
self.assertEqual(len(q("a:contains('Pending')")),1)
|
||||
self.assertEqual(len(q("a:contains('Announce')")),0)
|
||||
self.assertEqual(len(q("a:contains('Pending')")), 1)
|
||||
self.assertEqual(len(q("a:contains('Announce')")), 0)
|
||||
self.client.logout()
|
||||
# secretariat
|
||||
username = "secretary"
|
||||
self.client.login(username=username, password= username + "+password")
|
||||
self.client.login(username=username, password=username + "+password")
|
||||
r = self.client.get(url)
|
||||
q = PyQuery(r.content)
|
||||
self.assertEqual(len(q("a:contains('Pending')")),1)
|
||||
self.assertEqual(len(q("a:contains('Announce')")),1)
|
||||
self.assertEqual(len(q("a:contains('Pending')")), 1)
|
||||
self.assertEqual(len(q("a:contains('Announce')")), 1)
|
||||
self.client.logout()
|
||||
|
||||
def test_interim_announce(self):
|
||||
make_meeting_test_data()
|
||||
url = urlreverse("ietf.meeting.views.interim_announce")
|
||||
meeting = Meeting.objects.filter(type='interim',session__group__acronym='mars').first()
|
||||
meeting = Meeting.objects.filter(type='interim', session__group__acronym='mars').first()
|
||||
session = meeting.session_set.first()
|
||||
session.status = SessionStatusName.objects.get(slug='scheda')
|
||||
session.save()
|
||||
login_testing_unauthorized(self,"secretary",url)
|
||||
login_testing_unauthorized(self, "secretary", url)
|
||||
r = self.client.get(url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertTrue(meeting.number in r.content)
|
||||
|
||||
def test_interim_send_announcement(self):
|
||||
make_meeting_test_data()
|
||||
meeting = Meeting.objects.filter(type='interim',session__status='apprw',session__group__acronym='mars').first()
|
||||
url = urlreverse("ietf.meeting.views.interim_send_announcement", kwargs={'number':meeting.number})
|
||||
login_testing_unauthorized(self,"secretary",url)
|
||||
meeting = Meeting.objects.filter(type='interim', session__status='apprw', session__group__acronym='mars').first()
|
||||
url = urlreverse("ietf.meeting.views.interim_send_announcement", kwargs={'number': meeting.number})
|
||||
login_testing_unauthorized(self, "secretary", url)
|
||||
r = self.client.get(url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
initial = r.context['form'].initial
|
||||
# send announcement
|
||||
len_before = len(outbox)
|
||||
r = self.client.post(url,initial)
|
||||
self.assertRedirects(r,urlreverse('ietf.meeting.views.interim_announce'))
|
||||
self.assertEqual(len(outbox),len_before+1)
|
||||
r = self.client.post(url, initial)
|
||||
self.assertRedirects(r, urlreverse('ietf.meeting.views.interim_announce'))
|
||||
self.assertEqual(len(outbox), len_before + 1)
|
||||
self.assertTrue('WG Virtual Meeting' in outbox[-1]['Subject'])
|
||||
|
||||
def test_interim_approve(self):
|
||||
make_meeting_test_data()
|
||||
meeting = Meeting.objects.filter(type='interim',session__status='apprw',session__group__acronym='mars').first()
|
||||
url = urlreverse('ietf.meeting.views.interim_request_details',kwargs={'number':meeting.number})
|
||||
login_testing_unauthorized(self,"secretary",url)
|
||||
r = self.client.post(url,{'approve':'approve'})
|
||||
self.assertRedirects(r,urlreverse('ietf.meeting.views.interim_send_announcement',kwargs={'number':meeting.number}))
|
||||
meeting = Meeting.objects.filter(type='interim', session__status='apprw', session__group__acronym='mars').first()
|
||||
url = urlreverse('ietf.meeting.views.interim_request_details', kwargs={'number': meeting.number})
|
||||
login_testing_unauthorized(self, "secretary", url)
|
||||
r = self.client.post(url, {'approve': 'approve'})
|
||||
self.assertRedirects(r, urlreverse('ietf.meeting.views.interim_send_announcement', kwargs={'number': meeting.number}))
|
||||
for session in meeting.session_set.all():
|
||||
self.assertEqual(session.status.slug,'scheda')
|
||||
self.assertEqual(session.status.slug, 'scheda')
|
||||
|
||||
def test_upcoming(self):
|
||||
make_meeting_test_data()
|
||||
|
@ -478,8 +479,8 @@ class InterimTests(TestCase):
|
|||
r = self.client.get("/meeting/interim/request/")
|
||||
self.assertEqual(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertEqual(Group.objects.filter(type__in=('wg','rg'),state='active').count(),
|
||||
len(q("#id_group option")) -1 ) # -1 for options placeholder
|
||||
self.assertEqual(Group.objects.filter(type__in=('wg', 'rg'), state__in=('active', 'proposed')).count(),
|
||||
len(q("#id_group option")) - 1) # -1 for options placeholder
|
||||
|
||||
|
||||
def test_interim_request_single(self):
|
||||
|
@ -882,4 +883,29 @@ class InterimTests(TestCase):
|
|||
length_before = len(outbox)
|
||||
send_interim_minutes_reminder(meeting=meeting)
|
||||
self.assertEqual(len(outbox),length_before+1)
|
||||
self.assertTrue('Action Required: Minutes' in outbox[-1]['Subject'])
|
||||
self.assertTrue('Action Required: Minutes' in outbox[-1]['Subject'])
|
||||
|
||||
|
||||
class AjaxTests(TestCase):
|
||||
def test_ajax_get_utc(self):
|
||||
# test bad queries
|
||||
url = urlreverse('ietf.meeting.views.ajax_get_utc') + "?date=2016-1-1&time=badtime&timezone=UTC"
|
||||
r = self.client.get(url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
data = json.loads(r.content)
|
||||
self.assertEqual(data["error"], True)
|
||||
url = urlreverse('ietf.meeting.views.ajax_get_utc') + "?date=2016-1-1&time=25:99&timezone=UTC"
|
||||
r = self.client.get(url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
data = json.loads(r.content)
|
||||
self.assertEqual(data["error"], True)
|
||||
# test good query
|
||||
url = urlreverse('ietf.meeting.views.ajax_get_utc') + "?date=2016-1-1&time=12:00&timezone=US/Pacific"
|
||||
r = self.client.get(url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
data = json.loads(r.content)
|
||||
self.assertTrue('timezone' in data)
|
||||
self.assertTrue('time' in data)
|
||||
self.assertTrue('utc' in data)
|
||||
self.assertTrue('error' not in data)
|
||||
self.assertEqual(data['utc'], '20:00')
|
||||
|
|
|
@ -22,6 +22,7 @@ from django.db.models import Min, Max
|
|||
from django.conf import settings
|
||||
from django.forms.models import modelform_factory, inlineformset_factory
|
||||
from django.forms import ModelForm
|
||||
from django.template.loader import render_to_string
|
||||
from django.utils.functional import curry
|
||||
from django.views.decorators.csrf import ensure_csrf_cookie
|
||||
|
||||
|
@ -39,6 +40,7 @@ from ietf.meeting.helpers import get_meeting, get_schedule, agenda_permissions,
|
|||
from ietf.meeting.helpers import preprocess_assignments_for_agenda, read_agenda_file
|
||||
from ietf.meeting.helpers import convert_draft_to_pdf, get_earliest_session_date
|
||||
from ietf.meeting.helpers import can_view_interim_request, can_approve_interim_request
|
||||
from ietf.meeting.helpers import can_edit_interim_request
|
||||
from ietf.meeting.helpers import can_request_interim_meeting, get_announcement_initial
|
||||
from ietf.meeting.helpers import sessions_post_save, is_meeting_approved
|
||||
from ietf.meeting.helpers import send_interim_cancellation_notice
|
||||
|
@ -910,8 +912,17 @@ def ajax_get_utc(request):
|
|||
'''Ajax view that takes arguments time and timezone and returns UTC'''
|
||||
time = request.GET.get('time')
|
||||
timezone = request.GET.get('timezone')
|
||||
date = request.GET.get('date')
|
||||
time_re = re.compile(r'^\d{2}:\d{2}')
|
||||
if not time_re.match(time):
|
||||
return HttpResponse(json.dumps({'error': True}),
|
||||
content_type='application/json')
|
||||
hour, minute = time.split(':')
|
||||
dt = datetime.datetime(2016, 1, 1, int(hour), int(minute))
|
||||
if not (int(hour) <= 23 and int(minute) <= 59):
|
||||
return HttpResponse(json.dumps({'error': True}),
|
||||
content_type='application/json')
|
||||
year, month, day = date.split('-')
|
||||
dt = datetime.datetime(int(year), int(month), int(day), int(hour), int(minute))
|
||||
tz = pytz.timezone(timezone)
|
||||
aware_dt = tz.localize(dt, is_dst=None)
|
||||
utc_dt = aware_dt.astimezone(pytz.utc)
|
||||
|
@ -1045,8 +1056,7 @@ def interim_request(request):
|
|||
|
||||
messages.success(request, 'Interim meeting request submitted')
|
||||
return redirect(upcoming)
|
||||
else:
|
||||
assert False, (form.errors, formset.errors)
|
||||
|
||||
else:
|
||||
form = InterimMeetingModelForm(request=request,
|
||||
initial={'meeting_type': 'single'})
|
||||
|
@ -1091,7 +1101,7 @@ def interim_request_details(request, number):
|
|||
'''View details of an interim meeting reqeust'''
|
||||
meeting = get_object_or_404(Meeting, number=number)
|
||||
sessions = meeting.session_set.all()
|
||||
can_edit = can_view_interim_request(meeting, request.user)
|
||||
can_edit = can_edit_interim_request(meeting, request.user)
|
||||
can_approve = can_approve_interim_request(meeting, request.user)
|
||||
|
||||
if request.method == 'POST':
|
||||
|
@ -1145,8 +1155,7 @@ def interim_request_edit(request, number):
|
|||
|
||||
messages.success(request, 'Interim meeting request saved')
|
||||
return redirect(interim_request_details, number=number)
|
||||
else:
|
||||
assert False, (form.errors, formset.errors)
|
||||
|
||||
else:
|
||||
form = InterimMeetingModelForm(request=request, instance=meeting)
|
||||
formset = SessionFormset(instance=meeting)
|
||||
|
@ -1161,7 +1170,7 @@ def upcoming(request):
|
|||
'''List of upcoming meetings'''
|
||||
today = datetime.datetime.today()
|
||||
meetings = Meeting.objects.filter(date__gte=today).exclude(
|
||||
session__status__in=('apprw', 'schedpa', 'canceledpa')).order_by('date')
|
||||
session__status__in=('apprw', 'scheda', 'canceledpa')).order_by('date')
|
||||
|
||||
# extract groups hierarchy for display filter
|
||||
seen = set()
|
||||
|
@ -1222,7 +1231,17 @@ def upcoming_ical(request):
|
|||
a.session.group.acronym in filters or
|
||||
a.session.group.parent.acronym in filters]
|
||||
|
||||
# gather vtimezones
|
||||
vtimezones = set()
|
||||
for meeting in meetings:
|
||||
if meeting.vtimezone():
|
||||
vtimezones.add(meeting.vtimezone())
|
||||
vtimezones = ''.join(vtimezones)
|
||||
|
||||
return render(request, 'meeting/upcoming.ics', {
|
||||
'assignments': assignments,
|
||||
}, content_type='text/calendar')
|
||||
# icalendar response file should have '\r\n' line endings per RFC5545
|
||||
response = render_to_string('meeting/upcoming.ics', {
|
||||
'vtimezones': vtimezones,
|
||||
'assignments': assignments})
|
||||
response = re.sub("\r(?!\n)|(?<!\r)\n", "\r\n", response)
|
||||
|
||||
return HttpResponse(response, content_type='text/calendar')
|
||||
|
|
|
@ -470,3 +470,8 @@ form.navbar-form input.form-control.input-sm { width: 141px; }
|
|||
#interim-request-form .time-field {
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
ul.errorlist {
|
||||
list-style-type: none;
|
||||
padding-left:0;
|
||||
}
|
||||
|
|
|
@ -81,6 +81,7 @@ var interimRequest = {
|
|||
}
|
||||
var url = "/meeting/ajax/get-utc";
|
||||
var fieldset = $(this).parents(".fieldset");
|
||||
var date = fieldset.find("input[name$='-date']").val();
|
||||
var timezone = interimRequest.timezone.val();
|
||||
var name = $(this).attr("id") + "_utc";
|
||||
var utc = fieldset.find("#" + name);
|
||||
|
@ -90,7 +91,8 @@ var interimRequest = {
|
|||
cache: false,
|
||||
async: true,
|
||||
dataType: 'json',
|
||||
data: {time: time,
|
||||
data: {date: date,
|
||||
time: time,
|
||||
timezone: timezone},
|
||||
success: function(response){
|
||||
if (!response.error) {
|
||||
|
|
|
@ -54,7 +54,7 @@
|
|||
<a href="{% url 'ietf.meeting.views.interim_request_details' number=meeting.number %}">IETF - {{ meeting.number }}</a>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{% if meeting.can_approve %}<span class="label label-warning">can approve</span>{% endif %}</td>
|
||||
<td>{% if meeting.can_approve %}<span class="label label-success">can approve</span>{% endif %}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
|
|
|
@ -18,6 +18,12 @@
|
|||
<form id="interim-request-form" role="form" method="post" class="form-horizontal">
|
||||
{% csrf_token %}
|
||||
|
||||
{% if form.non_field_errors %}
|
||||
<div class="form-group alert alert-danger">
|
||||
{{ form.non_field_errors }}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% bootstrap_field form.group layout='horizontal' %}
|
||||
|
||||
<div class="form-group form-inline">
|
||||
|
@ -76,16 +82,18 @@
|
|||
{% for form in formset %}
|
||||
<div class="fieldset{% if forloop.last %} template{% endif %}" >
|
||||
|
||||
<div class="form-group">
|
||||
<div class="form-group {% if form.date.errors %}alert alert-danger{% endif %}">
|
||||
<label for="id_session_set-{{ forloop.counter0 }}-date" class="col-md-2 control-label required">Date</label>
|
||||
<div class="col-md-2">{% render_field form.date class="form-control" %}</div>
|
||||
{% if form.date.errors %}<span class="help-inline">{{ form.date.errors }}</span>{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="form-group {% if form.time.errors %}alert alert-danger{% endif %}">
|
||||
<label for="id_session_set-{{ forloop.counter0 }}-time" class="col-md-2 control-label required">Start Time</label>
|
||||
<div class="col-md-3 form-inline">
|
||||
{% render_field form.time class="form-control time-field" placeholder="HH:MM" %}
|
||||
{% render_field form.time_utc class="form-control time-field computed" disabled="disabled" placeholder="UTC" %}
|
||||
{% if form.time.errors %}<span class="help-inline">{{ form.time.errors }}</span>{% endif %}
|
||||
</div>
|
||||
<label for="id_session_set-{{ forloop.counter0 }}-end_time" class="col-md-2 control-label">End Time</label>
|
||||
<div class="col-md-4 form-inline">
|
||||
|
@ -94,14 +102,16 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="form-group{% if form.requested_duration.errors %} alert alert-danger{% endif %}">
|
||||
<label for="id_session_set-{{ forloop.counter0 }}-requested_duration" class="col-md-2 control-label required">Duration</label>
|
||||
<div class="col-md-2">{% render_field form.requested_duration class="form-control time-field" placeholder="HH:MM" %}</div>
|
||||
{% if form.requested_duration.errors %}<span class="help-inline">{{ form.requested_duration.errors }}</span>{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="form-group{% if form.remote_instructions.errors %} alert alert-danger{% endif %}">
|
||||
<label for="id_session_set-{{ forloop.counter0 }}-remote_instructions" class="col-md-2 control-label required">Remote Instructions</label>
|
||||
<div class="col-md-10">{% render_field form.remote_instructions class="form-control" placeholder="Webex URL" %}<p class="help-block">"Remote participation is not supported" or "Remote participation information will be obtained at the time of approval" are acceptable values. See <a href="">here</a> for more on remote participation support.</p></div>
|
||||
{% if form.remote_instructions.errors %}<span class="help-inline">{{ form.remote_instructions.errors }}</span>{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
|
|
|
@ -31,21 +31,6 @@
|
|||
<div class="col-md-2">
|
||||
<label class="checkbox-inline">{% render_field form.approved %}<strong>Preapproved by AD</strong></label>
|
||||
</div>
|
||||
|
||||
<!--
|
||||
<div class="col-md-2 radio-inline"><strong>Meeting Type:</strong></div>
|
||||
|
||||
<label class="radio-inline">
|
||||
<input type="radio" value="single" checked="checked" name="meeting_type">Single
|
||||
</label>
|
||||
<label class="radio-inline">
|
||||
<input type="radio" value="multi-day" name="meeting_type">Multi-Day
|
||||
</label>
|
||||
<label class="radio-inline">
|
||||
<input type="radio" value="series" name="meeting_type">Series
|
||||
</label>
|
||||
-->
|
||||
|
||||
</div> <!-- col-md-offset-2 -->
|
||||
</div> <!-- form-group form-inline -->
|
||||
|
||||
|
@ -64,16 +49,18 @@
|
|||
|
||||
<input id="id_session_set-{{ forloop.counter0 }}-id" name="session_set-{{ forloop.counter0 }}-id" type="hidden" value="{{ form.instance.pk|default_if_none:"" }}">
|
||||
|
||||
<div class="form-group">
|
||||
<div class="form-group{% if form.date.errors %} alert alert-danger{% endif %}">
|
||||
<label for="id_session_set-{{ forloop.counter0 }}-date" class="col-md-2 control-label required">Date</label>
|
||||
<div class="col-md-2">{% render_field form.date class="form-control" %}</div>
|
||||
{% if form.date.errors %}<span class="help-inline">{{ form.date.errors }}</span>{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="form-group {% if form.time.errors %}alert alert-danger{% endif %}">
|
||||
<label for="id_session_set-{{ forloop.counter0 }}-time" class="col-md-2 control-label required">Start Time</label>
|
||||
<div class="col-md-3 form-inline">
|
||||
{% render_field form.time class="form-control time-field" placeholder="HH:MM" %}
|
||||
{% render_field form.time_utc class="form-control time-field computed" disabled="disabled" placeholder="UTC" %}
|
||||
{% if form.time.errors %}<span class="help-inline">{{ form.time.errors }}</span>{% endif %}
|
||||
</div>
|
||||
<label for="id_session_set-{{ forloop.counter0 }}-end_time" class="col-md-2 control-label">End Time</label>
|
||||
<div class="col-md-4 form-inline">
|
||||
|
@ -82,14 +69,16 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="form-group{% if form.requested_duration.errors %} alert alert-danger{% endif %}">
|
||||
<label for="id_session_set-{{ forloop.counter0 }}-requested_duration" class="col-md-2 control-label required">Duration</label>
|
||||
<div class="col-md-2">{% render_field form.requested_duration class="form-control time-field" placeholder="HH:MM" %}</div>
|
||||
{% if form.requested_duration.errors %}<span class="help-inline">{{ form.requested_duration.errors }}</span>{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="id_session_set-{{ forloop.counter0 }}-remote_instructions" class="col-md-2 control-label">Remote Instructions</label>
|
||||
<div class="form-group{% if form.remote_instructions.errors %} alert alert-danger{% endif %}">
|
||||
<label for="id_session_set-{{ forloop.counter0 }}-remote_instructions" class="col-md-2 control-label required">Remote Instructions</label>
|
||||
<div class="col-md-10">{% render_field form.remote_instructions class="form-control" placeholder="ie. Webex address" %}</div>
|
||||
{% if form.remote_instructions.errors %}<span class="help-inline">{{ form.remote_instructions.errors }}</span>{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{% extends "base.html" %}
|
||||
{# Copyright The IETF Trust 2015, All Rights Reserved #}
|
||||
{% load origin %}
|
||||
{% load origin ietf_filters %}
|
||||
|
||||
{% block title %}{{ meeting }} : {{ acronym }}{% endblock %}
|
||||
|
||||
|
@ -14,6 +14,9 @@
|
|||
{% if can_manage_materials %}
|
||||
{% if session.status.slug == 'sched' or session.status.slug == 'schedw' %}
|
||||
<div class="buttonlist">
|
||||
{% if meeting.type.slug == 'interim' and user|has_role:"Secretariat" %}
|
||||
<a class="btn btn-default" href="{% url 'ietf.meeting.views.interim_request_details' number=meeting.number %}">Meeting Details</a>
|
||||
{% endif %}
|
||||
<a class="btn btn-default" href="{% url 'ietf.secr.proceedings.views.upload_unified' meeting_num=session.meeting.number acronym=session.group.acronym %}">
|
||||
Upload/Edit Materials
|
||||
</a>
|
||||
|
|
|
@ -125,7 +125,7 @@
|
|||
{% endif %}
|
||||
<td>
|
||||
{% if meeting.type.slug == "interim" %}
|
||||
<a href="{% url 'ietf.meeting.views.session_details' num=meeting.number acronym=meeting.session_set.all.0.group.acronym %}">{{ meeting.number }}{% if meeting.session_set.all.0.status.slug == "canceled" %} -- CANCELLED --{% endif %}</a>
|
||||
<a href="{% url 'ietf.meeting.views.session_details' num=meeting.number acronym=meeting.session_set.all.0.group.acronym %}">{{ meeting.number }}{% if meeting.session_set.all.0.status.slug == "canceled" %}  <span class="label label-warning">CANCELLED</span>{% endif %}</a>
|
||||
{% else %}
|
||||
<a href="{% url 'ietf.meeting.views.agenda' num=meeting.number %}">IETF - {{ meeting.number }}</a>
|
||||
{% endif %}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
VERSION:2.0
|
||||
METHOD:PUBLISH
|
||||
PRODID:-//IETF//datatracker.ietf.org ical upcoming//EN
|
||||
{% for item in assignments %}BEGIN:VEVENT
|
||||
{{vtimezones}}{% for item in assignments %}BEGIN:VEVENT
|
||||
UID:ietf-{{item.session.meeting.number}}-{{item.timeslot.pk}}
|
||||
SUMMARY:{% if item.session.name %}{{item.session.name|ics_esc}}{% else %}{{item.session.group.acronym|lower}} - {{item.session.group.name}}{%endif%}
|
||||
{% if item.schedule.meeting.city %}LOCATION:{{item.schedule.meeting.city}},{{item.schedule.meeting.country}}
|
||||
|
|
Loading…
Reference in a new issue