Allow logged in users to propose slides for meeting sessions. Fixes #2547 and #2403. Commit ready for merge.

- Legacy-Id: 16102
This commit is contained in:
Robert Sparks 2019-03-25 16:10:00 +00:00
parent 7224e06f3d
commit becad91b0b
16 changed files with 11353 additions and 10877 deletions

View file

@ -0,0 +1,29 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.20 on 2019-03-25 06:11
from __future__ import unicode_literals
from django.db import migrations
def forward(apps, schema_editor):
MailTrigger = apps.get_model('mailtrigger', 'MailTrigger')
Recipient = apps.get_model('mailtrigger', 'Recipient')
changed = MailTrigger.objects.create(
slug = 'slides_proposed',
desc = 'Recipients when slides are proposed for a given session',
)
changed.to.set(Recipient.objects.filter(slug__in=['group_chairs', 'group_responsible_directors', 'group_secretaries']))
def reverse(apps, schema_editor):
MailTrigger = apps.get_model('mailtrigger','MailTrigger')
MailTrigger.objects.filter(slug='slides_proposed').delete()
class Migration(migrations.Migration):
dependencies = [
('mailtrigger', '0004_ballot_rfceditornote_changed_postapproval'),
]
operations = [
migrations.RunPython(forward,reverse)
]

View file

@ -2,7 +2,7 @@ from django.contrib import admin
from ietf.meeting.models import (Meeting, Room, Session, TimeSlot, Constraint, Schedule,
SchedTimeSessAssignment, ResourceAssociation, FloorPlan, UrlResource,
SessionPresentation, ImportantDate, )
SessionPresentation, ImportantDate, SlideSubmission, )
class UrlResourceAdmin(admin.ModelAdmin):
@ -128,6 +128,9 @@ admin.site.register(SessionPresentation, SessionPresentationAdmin)
class ImportantDateAdmin(admin.ModelAdmin):
model = ImportantDate
list_display = ['meeting', 'name', 'date']
admin.site.register(ImportantDate,ImportantDateAdmin)
class SlideSubmissionAdmin(admin.ModelAdmin):
model = SlideSubmission
list_display = ['session', 'submitter', 'title']
admin.site.register(SlideSubmission, SlideSubmissionAdmin)

View file

@ -4,7 +4,7 @@ import datetime
from django.core.files.base import ContentFile
from ietf.meeting.models import Meeting, Session, Schedule, TimeSlot, SessionPresentation, FloorPlan, Room
from ietf.meeting.models import Meeting, Session, Schedule, TimeSlot, SessionPresentation, FloorPlan, Room, SlideSubmission
from ietf.group.factories import GroupFactory
from ietf.person.factories import PersonFactory
@ -157,3 +157,16 @@ class FloorPlanFactory(factory.DjangoModelFactory):
), 'floorplan.jpg'
)
)
class SlideSubmissionFactory(factory.DjangoModelFactory):
class Meta:
model = SlideSubmission
session = factory.SubFactory(SessionFactory)
title = factory.Faker('sentence')
filename = factory.Sequence(lambda n: 'test_slide_%d'%n)
submitter = factory.SubFactory(PersonFactory)
make_file = factory.PostGeneration(
lambda obj, create, extracted, **kwargs: open(obj.staged_filepath(),'a').close()
)

View file

@ -0,0 +1,29 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.20 on 2019-03-23 07:41
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
import ietf.utils.models
class Migration(migrations.Migration):
dependencies = [
('person', '0009_auto_20190118_0725'),
('meeting', '0011_auto_20190114_0550'),
]
operations = [
migrations.CreateModel(
name='SlideSubmission',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(max_length=255)),
('filename', models.CharField(max_length=255)),
('apply_to_all', models.BooleanField(default=False)),
('session', ietf.utils.models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='meeting.Session')),
('submitter', ietf.utils.models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='person.Person')),
],
),
]

View file

@ -1152,3 +1152,16 @@ class ImportantDate(models.Model):
def __unicode__(self):
return u'%s : %s : %s' % ( self.meeting, self.name, self.date )
class SlideSubmission(models.Model):
session = ForeignKey(Session)
title = models.CharField(max_length=255)
filename = models.CharField(max_length=255)
apply_to_all = models.BooleanField(default=False)
submitter = ForeignKey(Person)
def staged_filepath(self):
return os.path.join(settings.SLIDE_STAGING_PATH , self.filename)
def staged_url(self):
return "".join([settings.SLIDE_STAGING_URL, self.filename])

View file

@ -9,7 +9,7 @@ from ietf import api
from ietf.meeting.models import ( Meeting, ResourceAssociation, Constraint, Room, Schedule, Session,
TimeSlot, SchedTimeSessAssignment, SessionPresentation, FloorPlan,
UrlResource, ImportantDate )
UrlResource, ImportantDate, SlideSubmission )
from ietf.name.resources import MeetingTypeNameResource
class MeetingResource(ModelResource):
@ -301,3 +301,23 @@ class ImportantDateResource(ModelResource):
"name": ALL_WITH_RELATIONS,
}
api.meeting.register(ImportantDateResource())
from ietf.person.resources import PersonResource
class SlideSubmissionResource(ModelResource):
session = ToOneField(SessionResource, 'session')
submitter = ToOneField(PersonResource, 'submitter')
class Meta:
queryset = SlideSubmission.objects.all()
serializer = api.Serializer()
cache = SimpleCache()
#resource_name = 'slidesubmission'
filtering = {
"id": ALL,
"title": ALL,
"filename": ALL,
"apply_to_all": ALL,
"session": ALL_WITH_RELATIONS,
"submitter": ALL_WITH_RELATIONS,
}
api.meeting.register(SlideSubmissionResource())

View file

@ -25,7 +25,7 @@ from ietf.meeting.helpers import can_approve_interim_request, can_view_interim_r
from ietf.meeting.helpers import send_interim_approval_request
from ietf.meeting.helpers import send_interim_cancellation_notice
from ietf.meeting.helpers import send_interim_minutes_reminder, populate_important_dates, update_important_dates
from ietf.meeting.models import Session, TimeSlot, Meeting, SchedTimeSessAssignment, Schedule, SessionPresentation
from ietf.meeting.models import Session, TimeSlot, Meeting, SchedTimeSessAssignment, Schedule, SessionPresentation, SlideSubmission
from ietf.meeting.test_data import make_meeting_test_data, make_interim_meeting
from ietf.meeting.utils import finalize
from ietf.name.models import SessionStatusName, ImportantDateName
@ -34,9 +34,9 @@ from ietf.utils.mail import outbox, empty_outbox
from ietf.utils.text import xslugify
from ietf.person.factories import PersonFactory
from ietf.group.factories import GroupFactory, GroupEventFactory
from ietf.group.factories import GroupFactory, GroupEventFactory, RoleFactory
from ietf.meeting.factories import ( SessionFactory, SessionPresentationFactory, ScheduleFactory,
MeetingFactory, FloorPlanFactory, TimeSlotFactory )
MeetingFactory, FloorPlanFactory, TimeSlotFactory, SlideSubmissionFactory )
from ietf.doc.factories import DocumentFactory
from ietf.submit.tests import submission_file
@ -2007,6 +2007,116 @@ class MaterialsTests(TestCase):
self.assertEqual(0,session.sessionpresentation_set.count())
self.assertEqual(2,doc.docevent_set.count())
def test_propose_session_slides(self):
for type_id in ['ietf','interim']:
session = SessionFactory(meeting__type_id=type_id)
chair = RoleFactory(group=session.group,name_id='chair').person
session.meeting.importantdate_set.create(name_id='revsub',date=datetime.date.today()+datetime.timedelta(days=20))
newperson = PersonFactory()
session_overview_url = urlreverse('ietf.meeting.views.session_details',kwargs={'num':session.meeting.number,'acronym':session.group.acronym})
propose_url = urlreverse('ietf.meeting.views.propose_session_slides', kwargs={'session_id':session.pk, 'num': session.meeting.number})
r = self.client.get(session_overview_url)
self.assertEqual(r.status_code,200)
q = PyQuery(r.content)
self.assertFalse(q('#uploadslides'))
self.assertFalse(q('#proposeslides'))
self.client.login(username=newperson.user.username,password=newperson.user.username+"+password")
r = self.client.get(session_overview_url)
self.assertEqual(r.status_code,200)
q = PyQuery(r.content)
self.assertTrue(q('#proposeslides'))
self.client.logout()
login_testing_unauthorized(self,newperson.user.username,propose_url)
r = self.client.get(propose_url)
self.assertEqual(r.status_code,200)
test_file = StringIO('this is not really a slide')
test_file.name = 'not_really.txt'
empty_outbox()
r = self.client.post(propose_url,dict(file=test_file,title='a test slide file',apply_to_all=True))
self.assertEqual(r.status_code, 302)
session = Session.objects.get(pk=session.pk)
self.assertEqual(session.slidesubmission_set.count(),1)
self.assertEqual(len(outbox),1)
r = self.client.get(session_overview_url)
self.assertEqual(r.status_code, 200)
q = PyQuery(r.content)
self.assertEqual(len(q('#proposedslidelist p')), 1)
SlideSubmissionFactory(session = session)
self.client.logout()
self.client.login(username=chair.user.username, password=chair.user.username+"+password")
r = self.client.get(session_overview_url)
self.assertEqual(r.status_code, 200)
q = PyQuery(r.content)
self.assertEqual(len(q('#proposedslidelist p')), 2)
self.client.logout()
def test_disapprove_proposed_slides(self):
submission = SlideSubmissionFactory()
submission.session.meeting.importantdate_set.create(name_id='revsub',date=datetime.date.today()+datetime.timedelta(days=20))
chair = RoleFactory(group=submission.session.group,name_id='chair').person
url = urlreverse('ietf.meeting.views.approve_proposed_slides', kwargs={'slidesubmission_id':submission.pk,'num':submission.session.meeting.number})
login_testing_unauthorized(self, chair.user.username, url)
r = self.client.get(url)
self.assertEqual(r.status_code,200)
r = self.client.post(url,dict(title='some title',disapprove="disapprove"))
self.assertEqual(r.status_code,302)
self.assertEqual(SlideSubmission.objects.count(), 0)
def test_approve_proposed_slides(self):
submission = SlideSubmissionFactory()
session = submission.session
session.meeting.importantdate_set.create(name_id='revsub',date=datetime.date.today()+datetime.timedelta(days=20))
chair = RoleFactory(group=submission.session.group,name_id='chair').person
url = urlreverse('ietf.meeting.views.approve_proposed_slides', kwargs={'slidesubmission_id':submission.pk,'num':submission.session.meeting.number})
login_testing_unauthorized(self, chair.user.username, url)
r = self.client.get(url)
self.assertEqual(r.status_code,200)
r = self.client.post(url,dict(title='different title',approve='approve'))
self.assertEqual(r.status_code,302)
self.assertEqual(SlideSubmission.objects.count(), 0)
self.assertEqual(session.sessionpresentation_set.count(),1)
self.assertEqual(session.sessionpresentation_set.first().document.title,'different title')
def test_approve_proposed_slides_multisession_apply_one(self):
submission = SlideSubmissionFactory(session__meeting__type_id='ietf')
session1 = submission.session
session2 = SessionFactory(group=submission.session.group, meeting=submission.session.meeting)
submission.session.meeting.importantdate_set.create(name_id='revsub',date=datetime.date.today()+datetime.timedelta(days=20))
chair = RoleFactory(group=submission.session.group,name_id='chair').person
url = urlreverse('ietf.meeting.views.approve_proposed_slides', kwargs={'slidesubmission_id':submission.pk,'num':submission.session.meeting.number})
login_testing_unauthorized(self, chair.user.username, url)
r = self.client.get(url)
self.assertEqual(r.status_code,200)
q = PyQuery(r.content)
self.assertTrue(q('#id_apply_to_all'))
r = self.client.post(url,dict(title='yet another title',approve='approve'))
self.assertEqual(r.status_code,302)
self.assertEqual(session1.sessionpresentation_set.count(),1)
self.assertEqual(session2.sessionpresentation_set.count(),0)
def test_approve_proposed_slides_multisession_apply_all(self):
submission = SlideSubmissionFactory(session__meeting__type_id='ietf')
session1 = submission.session
session2 = SessionFactory(group=submission.session.group, meeting=submission.session.meeting)
submission.session.meeting.importantdate_set.create(name_id='revsub',date=datetime.date.today()+datetime.timedelta(days=20))
chair = RoleFactory(group=submission.session.group,name_id='chair').person
url = urlreverse('ietf.meeting.views.approve_proposed_slides', kwargs={'slidesubmission_id':submission.pk,'num':submission.session.meeting.number})
login_testing_unauthorized(self, chair.user.username, url)
r = self.client.get(url)
self.assertEqual(r.status_code,200)
r = self.client.post(url,dict(title='yet another title',apply_to_all=1,approve='approve'))
self.assertEqual(r.status_code,302)
self.assertEqual(session1.sessionpresentation_set.count(),1)
self.assertEqual(session2.sessionpresentation_set.count(),1)
class SessionTests(TestCase):
def test_meeting_requests(self):

View file

@ -13,11 +13,13 @@ safe_for_all_meeting_types = [
url(r'^session/(?P<session_id>\d+)/bluesheets$', views.upload_session_bluesheets),
url(r'^session/(?P<session_id>\d+)/minutes$', views.upload_session_minutes),
url(r'^session/(?P<session_id>\d+)/agenda$', views.upload_session_agenda),
url(r'^session/(?P<session_id>\d+)/propose_slides$', views.propose_session_slides),
url(r'^session/(?P<session_id>\d+)/slides(?:/%(name)s)?$' % settings.URL_REGEXPS, views.upload_session_slides),
url(r'^session/(?P<session_id>\d+)/slides/%(name)s/order$' % settings.URL_REGEXPS, views.set_slide_order),
url(r'^session/(?P<session_id>\d+)/doc/%(name)s/remove$' % settings.URL_REGEXPS, views.remove_sessionpresentation),
url(r'^session/(?P<session_id>\d+)\.ics$', views.ical_agenda),
url(r'^sessions/(?P<acronym>[-a-z0-9]+)\.ics$', views.ical_agenda),
url(r'^slidesubmission/(?P<slidesubmission_id>\d+)$', views.approve_proposed_slides)
]

View file

@ -42,7 +42,8 @@ from ietf.doc.models import Document, State, DocEvent, NewRevisionDocEvent
from ietf.group.models import Group
from ietf.group.utils import can_manage_materials
from ietf.ietfauth.utils import role_required, has_role
from ietf.meeting.models import Meeting, Session, Schedule, FloorPlan, SessionPresentation, TimeSlot
from ietf.mailtrigger.utils import gather_address_lists
from ietf.meeting.models import Meeting, Session, Schedule, FloorPlan, SessionPresentation, TimeSlot, SlideSubmission
from ietf.meeting.helpers import get_areas, get_person_by_email, get_schedule_by_name
from ietf.meeting.helpers import build_all_agenda_slices, get_wg_name_list
from ietf.meeting.helpers import get_all_assignments_from_schedule
@ -59,6 +60,7 @@ from ietf.meeting.helpers import send_interim_cancellation_notice
from ietf.meeting.helpers import send_interim_approval_request
from ietf.meeting.helpers import send_interim_announcement_request
from ietf.meeting.utils import finalize
from ietf.message.utils import infer_message
from ietf.secr.proceedings.utils import handle_upload_file
from ietf.secr.proceedings.proc_utils import (get_progress_stats, post_process, import_audio_files,
create_recording)
@ -1094,9 +1096,17 @@ def session_details(request, num, acronym ):
scheduled_sessions=[s for s in sessions if s.status_id=='sched']
unscheduled_sessions = [s for s in sessions if s.status_id!='sched']
pending_suggestions = None
if request.user.is_authenticated:
if can_manage:
pending_suggestions = session.slidesubmission_set.all()
else:
pending_suggestions = session.slidesubmission_set.filter(submitter=request.user.person)
return render(request, "meeting/session_details.html",
{ 'scheduled_sessions':scheduled_sessions ,
'unscheduled_sessions':unscheduled_sessions ,
'pending_suggestions' : pending_suggestions,
'meeting' :meeting ,
'acronym' :acronym,
'can_manage_materials' : can_manage,
@ -1540,6 +1550,63 @@ def upload_session_slides(request, session_id, num, name):
'slides_sp' : slides_sp,
'form': form,
})
@login_required
def propose_session_slides(request, session_id, num):
session = get_object_or_404(Session,pk=session_id)
if session.is_material_submission_cutoff() and not has_role(request.user, "Secretariat"):
return HttpResponseForbidden("The materials cutoff for this session has passed. Contact the secretariat for further action.")
session_number = None
sessions = get_sessions(session.meeting.number,session.group.acronym)
show_apply_to_all_checkbox = len(sessions) > 1 if session.type_id == 'session' else False
if len(sessions) > 1:
session_number = 1 + sessions.index(session)
if request.method == 'POST':
form = UploadSlidesForm(show_apply_to_all_checkbox,request.POST,request.FILES)
if form.is_valid():
file = request.FILES['file']
_, ext = os.path.splitext(file.name)
apply_to_all = session.type_id == 'session'
if show_apply_to_all_checkbox:
apply_to_all = form.cleaned_data['apply_to_all']
title = form.cleaned_data['title']
if session.meeting.type_id=='ietf':
name = 'slides-%s-%s' % (session.meeting.number,
session.group.acronym)
if not apply_to_all:
name += '-%s' % (session.docname_token(),)
else:
name = 'slides-%s-%s' % (session.meeting.number, session.docname_token())
name = name + '-' + slugify(title).replace('_', '-')[:128]
filename = '%s-00%s'% (name, ext)
destination = open(os.path.join(settings.SLIDE_STAGING_PATH, filename),'wb+')
for chunk in file.chunks():
destination.write(chunk)
destination.close()
submission = SlideSubmission.objects.create(session = session, title = title, filename = filename, apply_to_all = apply_to_all, submitter=request.user.person)
(to, cc) = gather_address_lists('slides_proposed', group=session.group).as_strings()
msg_txt = render_to_string("meeting/slides_proposed.txt", {
"to": to,
"cc": cc,
"submission": submission,
"settings": settings,
})
msg = infer_message(msg_txt)
msg.by = request.user.person
msg.save()
send_mail_message(request, msg)
return redirect('ietf.meeting.views.session_details',num=num,acronym=session.group.acronym)
else:
initial = {}
form = UploadSlidesForm(show_apply_to_all_checkbox, initial=initial)
return render(request, "meeting/propose_session_slides.html",
{'session': session,
'session_number': session_number,
'form': form,
})
def remove_sessionpresentation(request, session_id, num, name):
sp = get_object_or_404(SessionPresentation,session_id=session_id,document__name=name)
@ -2335,3 +2402,93 @@ def request_minutes(request, num=None):
form = RequestMinutesForm(initial=initial)
context = {'meeting':meeting, 'form': form}
return render(request, 'meeting/request_minutes.html', context)
class ApproveSlidesForm(forms.Form):
title = forms.CharField(max_length=255)
apply_to_all = forms.BooleanField(label='Apply to all group sessions at this meeting',initial=False,required=False)
def __init__(self, show_apply_to_all_checkbox, *args, **kwargs):
super(ApproveSlidesForm, self).__init__(*args, **kwargs )
if not show_apply_to_all_checkbox:
self.fields.pop('apply_to_all')
def approve_proposed_slides(request, slidesubmission_id, num):
submission = get_object_or_404(SlideSubmission,pk=slidesubmission_id)
if not submission.session.can_manage_materials(request.user):
return HttpResponseForbidden("You don't have permission to manage slides for this session.")
if submission.session.is_material_submission_cutoff() and not has_role(request.user, "Secretariat"):
return HttpResponseForbidden("The materials cutoff for this session has passed. Contact the secretariat for further action.")
session_number = None
sessions = get_sessions(submission.session.meeting.number,submission.session.group.acronym)
show_apply_to_all_checkbox = len(sessions) > 1 if submission.session.type_id == 'session' else False
if len(sessions) > 1:
session_number = 1 + sessions.index(submission.session)
name, _ = os.path.splitext(submission.filename)
name = name[:-3]
existing_doc = Document.objects.filter(name=name).first()
if request.method == 'POST':
form = ApproveSlidesForm(show_apply_to_all_checkbox, request.POST)
if form.is_valid():
apply_to_all = submission.session.type_id == 'session'
if show_apply_to_all_checkbox:
apply_to_all = form.cleaned_data['apply_to_all']
if request.POST.get('approve'):
title = form.cleaned_data['title']
if existing_doc:
doc = Document.objects.get(name=name)
doc.rev = '%02d' % (int(doc.rev)+1)
doc.title = form.cleaned_data['title']
else:
doc = Document.objects.create(
name = name,
type_id = 'slides',
title = title,
group = submission.session.group,
rev = '00',
)
doc.docalias_set.create(name=doc.name)
doc.states.add(State.objects.get(type_id='slides',slug='active'))
doc.states.add(State.objects.get(type_id='reuse_policy',slug='single'))
if submission.session.sessionpresentation_set.filter(document=doc).exists():
sp = submission.session.sessionpresentation_set.get(document=doc)
sp.rev = doc.rev
sp.save()
else:
max_order = submission.session.sessionpresentation_set.filter(document__type='slides').aggregate(Max('order'))['order__max'] or 0
submission.session.sessionpresentation_set.create(document=doc,rev=doc.rev,order=max_order+1)
if apply_to_all:
for other_session in sessions:
if other_session != submission.session and not other_session.sessionpresentation_set.filter(document=doc).exists():
max_order = other_session.sessionpresentation_set.filter(document__type='slides').aggregate(Max('order'))['order__max'] or 0
other_session.sessionpresentation_set.create(document=doc,rev=doc.rev,order=max_order+1)
doc.uploaded_filename = submission.filename
e = NewRevisionDocEvent.objects.create(doc=doc,by=submission.submitter,type='new_revision',desc='New revision available: %s'%doc.rev,rev=doc.rev)
doc.save_with_history([e])
path = os.path.join(submission.session.meeting.get_materials_path(),'slides')
if not os.path.exists(path):
os.makedirs(path)
os.rename(submission.staged_filepath(), os.path.join(path, submission.filename))
acronym = submission.session.group.acronym
submission.delete()
return redirect('ietf.meeting.views.session_details',num=num,acronym=acronym)
elif request.POST.get('disapprove'):
os.unlink(submission.staged_filepath())
acronym = submission.session.group.acronym
submission.delete()
return redirect('ietf.meeting.views.session_details',num=num,acronym=acronym)
else:
pass
else:
initial = {
'title': submission.title,
'apply_to_all' : submission.apply_to_all,
}
form = ApproveSlidesForm(show_apply_to_all_checkbox, initial=initial )
return render(request, "meeting/approve_proposed_slides.html",
{'submission': submission,
'session_number': session_number,
'existing_doc' : existing_doc,
'form': form,
})

File diff suppressed because it is too large Load diff

View file

@ -696,6 +696,10 @@ OPENSSL_COMMAND = '/usr/bin/openssl'
DAYS_TO_EXPIRE_NOMINATION_LINK = ''
NOMINEE_FEEDBACK_TYPES = ['comment', 'questio', 'nomina']
# SlideSubmission settings
SLIDE_STAGING_PATH = '/a/www/www6s/staging/'
SLIDE_STAGING_URL = 'https://www.ietf.org/staging/'
# ID Submission Tool settings
IDSUBMIT_FROM_EMAIL = 'IETF I-D Submission Tool <idsubmission@ietf.org>'
IDSUBMIT_ANNOUNCE_FROM_EMAIL = 'internet-drafts@ietf.org'

View file

@ -0,0 +1,29 @@
{% extends "base.html" %}
{# Copyright The IETF Trust 2015, All Rights Reserved #}
{% load origin staticfiles bootstrap3 %}
{% block title %}Approve Slides Proposed for {{ submission.session.meeting }} : {{ submission.session.group.acronym }}{% endblock %}
{% block content %}
{% origin %}
<h1>Approve slides proposed for {{ submission.session.meeting }} : {{ submission.session.group.acronym }}{% if session.name %} : {{submission.session.name}}{% endif %}</h1>
{% if session_number %}<h2> Session {{session_number}} : {{submission.session.official_timeslotassignment.timeslot.time|date:"D M-d-Y Hi"}}</h2>{% endif %}
<p>{{submission.submitter}} proposed these slides for this session: <a href="{{submission.staged_url}}">{{submission.staged_url}}</a>.</p>
{% if existing_doc %}
<p class="alert alert-warning">Warning: If you approve this set of slides with the proposed title of "{{ submission.title }}", it will replace an existing set of slides. If that's not what you intend, please adjust the title.</p>
{% endif %}
<form enctype="multipart/form-data" method="post">
{% csrf_token %}
{% bootstrap_form form %}
{% buttons %}
<button type="submit" class="btn btn-primary" name="approve" value="approve">Approve</button>
<button type="submit" class="btn btn-warning" name="disapprove" value="disapprove">Disapprove and Delete</button>
{% endbuttons %}
</form>
{% endblock %}

View file

@ -0,0 +1,24 @@
{% extends "base.html" %}
{# Copyright The IETF Trust 2015, All Rights Reserved #}
{% load origin staticfiles bootstrap3 %}
{% block title %}Propose Slides for {{ session.meeting }} : {{ session.group.acronym }}{% endblock %}
{% block content %}
{% origin %}
<h1>Propose Slides for {{ session.meeting }} : {{ session.group.acronym }}{% if session.name %} : {{session.name}}{% endif %}</h1>
{% if session_number %}<h2> Session {{session_number}} : {{session.official_timeslotassignment.timeslot.time|date:"D M-d-Y Hi"}}</h2>{% endif %}
<p>This form will allow you to propose a slide deck to the session chairs. After you upload your proposal, mail will be sent to the session chairs asking for their approval.</p>
<form enctype="multipart/form-data" method="post">
{% csrf_token %}
{% bootstrap_form form %}
{% buttons %}
<button type="submit" class="btn btn-primary">Upload</button>
{% endbuttons %}
</form>
{% endblock %}

View file

@ -33,7 +33,7 @@
</div>
</div>
<div class="panel panel-warning">
<div class="panel-heading">Uncheduled Sessions</div>
<div class="panel-heading">Unscheduled Sessions</div>
<div class="panel-body">
{% endif %}
{% include 'meeting/session_details_panel.html' with sessions=unscheduled_sessions %}
@ -43,6 +43,22 @@
{% endif %}
{% endwith %}
{% if pending_suggestions %}
<div class="panel panel-info">
<div class="panel-heading">{% if can_manage_materials %}Proposed slides awaiting your approval{% else %}Your proposed slides awaiting chair approval{% endif %}</div>
<div id="proposedslidelist" class="panel-body">
{% for s in pending_suggestions %}
{% if can_manage_materials %}
<p><a href="{% url "ietf.meeting.views.approve_proposed_slides" slidesubmission_id=s.pk num=s.session.meeting.number %}">{{s.submitter}} - {{s.title}}</a></p>
{% else %}
<p>{{s.title}}</p>
{% endif %}
{% endfor %}
</div>
</div>
{% endif %}
{% endblock %}
{# TODO don't rely on secr/js version of jquery-ui #}

View file

@ -93,7 +93,9 @@
</tbody>
</table>
{% if can_manage_materials %}
<a class="btn btn-default pull-right" href="{% url 'ietf.meeting.views.upload_session_slides' session_id=session.pk num=session.meeting.number %}">Upload New Slides</a>
<a id="uploadslides" class="btn btn-default pull-right" href="{% url 'ietf.meeting.views.upload_session_slides' session_id=session.pk num=session.meeting.number %}">Upload New Slides</a>
{% elif request.user.is_authenticated %}
<a id="proposeslides" class="btn btn-default pull-right" href="{% url 'ietf.meeting.views.propose_session_slides' session_id=session.pk num=session.meeting.number %}">Propose Slides</a>
{% endif %}
</div>
{% if can_manage_materials %}

View file

@ -0,0 +1,12 @@
{% load ietf_filters %}{% autoescape off %}From: {{settings.DEFAULT_FROM_EMAIL}}
To: {{to}}{% if cc %}
Cc: {{cc}}{% endif %}
Subject: {{ submission.submitter }} has proposed slides for {{ submission.session.meeting }} : {{ submission.session.group.acronym }}{% if session.name %} : {{submission.session.name}}{% endif %}
{{ submission.submitter }} has proposed slides for {{ submission.session.meeting }} : {{ submission.session.group.acronym }}{% if session.name %} : {{submission.session.name}}{% endif %}
Title: {{submission.title}}
You may approve or disapprove this proposal at {{settings.IDTRACKER_BASE_URL}}{% url "ietf.meeting.views.approve_proposed_slides" slidesubmission_id=submission.pk num=submission.session.meeting.number %}
{% endautoescape %}