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:
parent
7224e06f3d
commit
becad91b0b
29
ietf/mailtrigger/migrations/0005_slides_proposed.py
Normal file
29
ietf/mailtrigger/migrations/0005_slides_proposed.py
Normal 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)
|
||||
]
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
)
|
29
ietf/meeting/migrations/0012_add_slide_submissions.py
Normal file
29
ietf/meeting/migrations/0012_add_slide_submissions.py
Normal 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')),
|
||||
],
|
||||
),
|
||||
]
|
|
@ -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])
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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)
|
||||
]
|
||||
|
||||
|
||||
|
|
|
@ -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
|
@ -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'
|
||||
|
|
29
ietf/templates/meeting/approve_proposed_slides.html
Normal file
29
ietf/templates/meeting/approve_proposed_slides.html
Normal 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 %}
|
24
ietf/templates/meeting/propose_session_slides.html
Normal file
24
ietf/templates/meeting/propose_session_slides.html
Normal 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 %}
|
|
@ -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 #}
|
||||
|
|
|
@ -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 %}
|
||||
|
|
12
ietf/templates/meeting/slides_proposed.txt
Normal file
12
ietf/templates/meeting/slides_proposed.txt
Normal 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 %}
|
Loading…
Reference in a new issue