Closed off the old paths for materials upload (redirecting some old entry points). Commit ready for merge.

- Legacy-Id: 12000
This commit is contained in:
Robert Sparks 2016-09-19 19:06:22 +00:00
parent c045f7ed73
commit 0f6e3f434e
14 changed files with 46 additions and 878 deletions

View file

@ -20,7 +20,7 @@ from django import forms
from django.shortcuts import render, redirect, get_object_or_404
from django.http import HttpResponse, HttpResponseRedirect, HttpResponseForbidden, Http404
from django.contrib import messages
from django.core.urlresolvers import reverse
from django.core.urlresolvers import reverse,reverse_lazy
from django.db.models import Min, Max
from django.conf import settings
from django.forms.models import modelform_factory, inlineformset_factory
@ -30,6 +30,7 @@ from django.utils.functional import curry
from django.views.decorators.cache import cache_page
from django.utils.text import slugify
from django.views.decorators.csrf import ensure_csrf_cookie
from django.views.generic import RedirectView
from django.template.defaultfilters import filesizeformat
from ietf.doc.fields import SearchableDocumentsField
@ -1989,3 +1990,6 @@ def proceedings_overview(request, num=None):
'template': template,
})
class OldUploadRedirect(RedirectView):
def get_redirect_url(self, **kwargs):
return reverse_lazy('ietf.meeting.views.session_details',kwargs=self.kwargs)

View file

@ -25,7 +25,8 @@ from ietf.secr.meetings.blue_sheets import create_blue_sheets
from ietf.secr.meetings.forms import ( BaseMeetingRoomFormSet, MeetingModelForm,
MeetingRoomForm, NewSessionForm, NonSessionEditForm, NonSessionForm, TimeSlotForm,
UploadBlueSheetForm, get_next_slot )
from ietf.secr.proceedings.views import build_choices, handle_upload_file
from ietf.secr.proceedings.views import build_choices
from ietf.secr.proceedings.utils import handle_upload_file
from ietf.secr.sreq.forms import GroupSelectForm
from ietf.secr.sreq.views import get_initial_session
from ietf.secr.utils.meeting import get_session, get_timeslot

View file

@ -1,11 +1,7 @@
import os
from django import forms
from django.conf import settings
from django.template.defaultfilters import filesizeformat
from ietf.doc.models import Document
from ietf.name.models import DocTypeName
from ietf.meeting.models import Session
@ -22,11 +18,6 @@ VALID_BLUESHEET_EXTENSIONS = ('.pdf','.jpg','.jpeg')
# Forms
#----------------------------------------------------------
class EditSlideForm(forms.ModelForm):
class Meta:
model = Document
fields = ('title',)
class RecordingForm(forms.Form):
external_url = forms.URLField(label='Url')
session = forms.ModelChoiceField(queryset=Session.objects,empty_label='')
@ -46,66 +37,3 @@ class RecordingEditForm(forms.ModelForm):
super(RecordingEditForm, self).__init__(*args, **kwargs)
self.fields['external_url'].label='Url'
class ReplaceSlideForm(forms.ModelForm):
file = forms.FileField(label='Select File')
class Meta:
model = Document
fields = ('title',)
def clean_file(self):
file = self.cleaned_data.get('file')
ext = os.path.splitext(file.name)[1].lower()
if ext not in VALID_SLIDE_EXTENSIONS:
raise forms.ValidationError('Only these file types supported for presentation slides: %s' % ','.join(VALID_SLIDE_EXTENSIONS))
if file._size > settings.SECR_MAX_UPLOAD_SIZE:
raise forms.ValidationError('Please keep filesize under %s. Current filesize %s' % (filesizeformat(settings.SECR_MAX_UPLOAD_SIZE), filesizeformat(file._size)))
return file
class UnifiedUploadForm(forms.Form):
acronym = forms.CharField(widget=forms.HiddenInput())
meeting_id = forms.CharField(widget=forms.HiddenInput())
material_type = forms.ModelChoiceField(queryset=DocTypeName.objects.filter(slug__in=('minutes','agenda','slides','bluesheets')),empty_label=None)
slide_name = forms.CharField(label='Name of Presentation',max_length=255,required=False,help_text="For presentations only")
file = forms.FileField(label='Select File',help_text='<div id="id_file_help">Note 1: You can only upload a presentation file in txt, pdf, doc, or ppt/pptx. System will not accept presentation files in any other format.<br><br>Note 2: All uploaded files will be available to the public immediately on the Preliminary Page. However, for the Proceedings, ppt/pptx files will be converted to html format and doc files will be converted to pdf format manually by the Secretariat staff.</div>')
def clean_file(self):
file = self.cleaned_data['file']
if file._size > settings.SECR_MAX_UPLOAD_SIZE:
raise forms.ValidationError('Please keep filesize under %s. Current filesize %s' % (filesizeformat(settings.SECR_MAX_UPLOAD_SIZE), filesizeformat(file._size)))
return file
def clean(self):
super(UnifiedUploadForm, self).clean()
# if an invalid file type is supplied no file attribute will exist
if self.errors:
return self.cleaned_data
cleaned_data = self.cleaned_data
material_type = cleaned_data['material_type']
slide_name = cleaned_data['slide_name']
file = cleaned_data['file']
ext = os.path.splitext(file.name)[1].lower()
if material_type.slug == 'slides' and not slide_name:
raise forms.ValidationError('ERROR: Name of Presentaion cannot be blank')
# only supporting PDFs per Alexa 04-05-2011
#if material_type == 1 and not file_ext[1] == '.pdf':
# raise forms.ValidationError('Presentations must be a PDF file')
# validate file extensions based on material type (slides,agenda,minutes,bluesheets)
# valid extensions per online documentation: meeting-materials.html
# 09-14-11 added ppt, pdf per Alexa
# 04-19-12 txt/html for agenda, +pdf for minutes per Russ
if material_type.slug == 'slides' and ext not in VALID_SLIDE_EXTENSIONS:
raise forms.ValidationError('Only these file types supported for presentation slides: %s' % ','.join(VALID_SLIDE_EXTENSIONS))
if material_type.slug == 'agenda' and ext not in VALID_AGENDA_EXTENSIONS:
raise forms.ValidationError('Only these file types supported for agendas: %s' % ','.join(VALID_AGENDA_EXTENSIONS))
if material_type.slug == 'minutes' and ext not in VALID_MINUTES_EXTENSIONS:
raise forms.ValidationError('Only these file types supported for minutes: %s' % ','.join(VALID_MINUTES_EXTENSIONS))
if material_type.slug == 'bluesheets' and ext not in VALID_BLUESHEET_EXTENSIONS:
raise forms.ValidationError('Only these file types supported for bluesheets: %s' % ','.join(VALID_BLUESHEET_EXTENSIONS))
return cleaned_data

View file

@ -2,20 +2,19 @@ import debug # pyflakes:ignore
import os
import shutil
from StringIO import StringIO
from django.core.urlresolvers import reverse
from django.conf import settings
from django.core.urlresolvers import reverse
from ietf.doc.models import Document
from ietf.group.models import Group
from ietf.meeting.models import Meeting, Session
from ietf.meeting.models import Session
from ietf.meeting.test_data import make_meeting_test_data
from ietf.utils.test_data import make_test_data
from ietf.utils.test_utils import TestCase, unicontent
from ietf.utils.test_utils import TestCase
from ietf.name.models import SessionStatusName
from ietf.secr.utils.meeting import get_proceedings_path
from ietf.meeting.factories import SessionFactory
from ietf.secr.proceedings.proc_utils import create_proceedings
SECR_USER='secretary'
@ -64,47 +63,23 @@ class RecordingTestCase(TestCase):
self.assertEqual(response.status_code, 200)
self.failUnless(external_url in response.content)
class BluesheetTestCase(TestCase):
class OldProceedingsTestCase(TestCase):
''' Ensure coverage of fragments of old proceedings generation until those are removed '''
def setUp(self):
self.session = SessionFactory(meeting__type_id='ietf')
self.proceedings_dir = os.path.abspath("tmp-proceedings-dir")
if not os.path.exists(self.proceedings_dir):
os.mkdir(self.proceedings_dir)
# This unintuitive bit is a consequence of the surprising implementation of meeting.get_materials_path
self.saved_agenda_path = settings.AGENDA_PATH
settings.AGENDA_PATH = self.proceedings_dir
self.interim_listing_dir = os.path.abspath("tmp-interim-listing-dir")
if not os.path.exists(self.interim_listing_dir):
os.mkdir(self.interim_listing_dir)
self.saved_secr_interim_listing_dir = settings.SECR_INTERIM_LISTING_DIR
settings.SECR_INTERIM_LISTING_DIR = self.interim_listing_dir
settings.AGENDA_PATH= self.proceedings_dir
target_path = self.session.meeting.get_materials_path()
if not os.path.exists(target_path):
os.makedirs(target_path)
def tearDown(self):
settings.AGENDA_PATH = self.saved_agenda_path
shutil.rmtree(self.proceedings_dir)
settings.SECR_INTERIM_LISTING_DIR = self.saved_secr_interim_listing_dir
shutil.rmtree(self.interim_listing_dir)
def test_upload(self):
make_meeting_test_data()
meeting = Meeting.objects.filter(type='interim',session__status='sched').first()
#self.assertTrue(meeting)
group = Group.objects.get(acronym='mars')
#Session.objects.create(meeting=meeting,group=group,requested_by_id=1,status_id='sched',type_id='session')
url = reverse('proceedings_upload_unified', kwargs={'meeting_num':meeting.number,'acronym':'mars'})
upfile = StringIO('dummy file')
upfile.name = "scan1.pdf"
self.client.login(username="marschairman", password="marschairman+password")
r = self.client.post(url,
dict(acronym='mars',meeting_id=meeting.id,material_type='bluesheets',file=upfile),follow=True)
self.assertEqual(r.status_code, 200)
doc = Document.objects.get(type='bluesheets')
self.failUnless(doc.external_url in unicontent(r))
self.failUnless(os.path.exists(os.path.join(doc.get_file_path(),doc.external_url)))
# test that proceedings has bluesheets on it
path = get_proceedings_path(meeting,group)
self.failUnless(os.path.exists(path))
with open(path) as f:
data = f.read()
self.failUnless(doc.external_url.encode('utf-8') in data)
settings.AGENDA_PATH = self.saved_agenda_path
def test_old_generate(self):
create_proceedings(self.session.meeting,self.session.group,is_final=True)

View file

@ -1,26 +1,16 @@
from django.conf.urls import patterns, url
from django.conf import settings
from ietf.meeting.views import OldUploadRedirect
urlpatterns = patterns('ietf.secr.proceedings.views',
url(r'^$', 'main', name='proceedings'),
url(r'^ajax/generate-proceedings/(?P<meeting_num>\d{1,3})/$', 'ajax_generate_proceedings', name='proceedings_ajax_generate_proceedings'),
url(r'^ajax/order-slide/$', 'ajax_order_slide', name='proceedings_ajax_order_slide'),
# special offline URL for testing proceedings build
url(r'^build/(?P<meeting_num>\d{1,3}|interim-\d{4}-[A-Za-z0-9_\-\+]+)/(?P<acronym>[-a-z0-9]+)/$',
'build', name='proceedings_build'),
url(r'^delete/(?P<slide_id>[A-Za-z0-9._\-\+]+)/$', 'delete_material', name='proceedings_delete_material'),
url(r'^edit-slide/(?P<slide_id>[A-Za-z0-9._\-\+]+)/$', 'edit_slide', name='proceedings_edit_slide'),
url(r'^move-slide/(?P<slide_id>[A-Za-z0-9._\-\+]+)/(?P<direction>(up|down))/$',
'move_slide', name='proceedings_move_slide'),
url(r'^process-pdfs/(?P<meeting_num>\d{1,3})/$', 'process_pdfs', name='proceedings_process_pdfs'),
url(r'^progress-report/(?P<meeting_num>\d{1,3})/$', 'progress_report', name='proceedings_progress_report'),
url(r'^replace-slide/(?P<slide_id>[A-Za-z0-9._\-\+]+)/$', 'replace_slide', name='proceedings_replace_slide'),
url(r'^(?P<meeting_num>\d{1,3})/$', 'select', name='proceedings_select'),
url(r'^(?P<meeting_num>\d{1,3})/recording/$', 'recording', name='proceedings_recording'),
url(r'^(?P<meeting_num>\d{1,3})/recording/edit/(?P<name>[A-Za-z0-9_\-\+]+)$', 'recording_edit', name='proceedings_recording_edit'),
# NOTE: we have two entries here which both map to upload_unified, passing session_id or acronym
url(r'^(?P<meeting_num>\d{1,3}|interim-\d{4}-[A-Za-z0-9_\-\+]+)/(?P<session_id>\d{1,6})/$',
'upload_unified', name='proceedings_upload_unified'),
url(r'^(?P<meeting_num>\d{1,3}|interim-\d{4}-[A-Za-z0-9_\-\+]+)/%(acronym)s/$' % settings.URL_REGEXPS,
'upload_unified', name='proceedings_upload_unified'),
url(r'^(?P<num>\d{1,3}|interim-\d{4}-[A-Za-z0-9_\-\+]+)/%(acronym)s/$' % settings.URL_REGEXPS,
OldUploadRedirect.as_view()),
)

View file

@ -8,31 +8,25 @@ import debug # pyflakes:ignore
from django.conf import settings
from django.contrib import messages
from django.core.exceptions import ObjectDoesNotExist
from django.core.urlresolvers import reverse
from django.db.models import Max
from django.http import HttpResponseRedirect
from django.shortcuts import render_to_response, get_object_or_404, redirect
from django.template import RequestContext
from django.utils.text import slugify
from ietf.secr.lib.template import jsonapi
from ietf.secr.sreq.forms import GroupSelectForm
from ietf.secr.utils.decorators import check_permissions, sec_only
from ietf.secr.utils.document import get_full_path
from ietf.secr.utils.group import get_my_groups, groups_by_session
from ietf.secr.utils.meeting import get_materials, get_timeslot, get_proceedings_path, get_proceedings_url
from ietf.doc.models import Document, DocAlias, DocEvent, State, NewRevisionDocEvent
from ietf.secr.utils.decorators import sec_only
from ietf.secr.utils.group import get_my_groups
from ietf.secr.utils.meeting import get_timeslot, get_proceedings_url
from ietf.doc.models import Document, DocEvent
from ietf.group.models import Group
from ietf.person.models import Person
from ietf.ietfauth.utils import has_role, role_required
from ietf.meeting.models import Meeting, Session, TimeSlot, SchedTimeSessAssignment
from ietf.meeting.models import Meeting, Session, TimeSlot
from ietf.secr.proceedings.forms import EditSlideForm, RecordingForm, RecordingEditForm, ReplaceSlideForm, UnifiedUploadForm
from ietf.secr.proceedings.forms import RecordingForm, RecordingEditForm
from ietf.secr.proceedings.proc_utils import ( gen_acknowledgement, gen_agenda, gen_areas,
gen_attendees, gen_group_pages, gen_index, gen_irtf, gen_overview, gen_plenaries,
gen_progress, gen_research, gen_training, create_proceedings, create_recording )
from ietf.secr.proceedings.utils import handle_upload_file
from ietf.utils.log import log
# -------------------------------------------------
@ -226,132 +220,10 @@ def ajax_generate_proceedings(request, meeting_num):
RequestContext(request,{}),
)
@jsonapi
def ajax_order_slide(request):
'''
Ajax function to change the order of presentation slides.
This function expects a POST request with the following parameters
order: new order of slide, 0 based
slide_name: slide primary key (name)
'''
if request.method != 'POST' or not request.POST:
return { 'success' : False, 'error' : 'No data submitted or not POST' }
slide_name = request.POST.get('slide_name',None)
order = request.POST.get('order',None)
slide = get_object_or_404(Document, name=slide_name)
# get all the slides for this session
session = slide.session_set.all()[0]
qs = session.materials.exclude(states__slug='deleted').filter(type='slides').order_by('order')
# move slide and reorder list
slides = list(qs)
index = slides.index(slide)
slides.pop(index)
slides.insert(int(order),slide)
for ord,item in enumerate(slides,start=1):
if item.order != ord:
item.order = ord
item.save()
return {'success':True,'order':order,'slide':slide_name}
# --------------------------------------------------
# STANDARD VIEW FUNCTIONS
# --------------------------------------------------
@role_required('Secretariat')
def build(request,meeting_num,acronym):
'''
This is a utility or test view. It simply rebuilds the proceedings html for the specified
meeting / group.
'''
meeting = Meeting.objects.get(number=meeting_num)
group = get_object_or_404(Group,acronym=acronym)
create_proceedings(meeting,group,is_final=True)
messages.success(request,'proceedings.html was rebuilt')
url = reverse('proceedings_upload_unified', kwargs={'meeting_num':meeting_num,'acronym':acronym})
return HttpResponseRedirect(url)
@check_permissions
def delete_material(request,slide_id):
'''
This view handles deleting meeting materials. We don't actually delete the
document object but set the state to deleted and add a 'deleted' DocEvent.
'''
doc = get_object_or_404(Document, name=slide_id)
# derive other objects
session = doc.session_set.all()[0]
meeting = session.meeting
group = session.group
path = get_full_path(doc)
if path and os.path.exists(path):
os.remove(path)
# leave it related
#session.materials.remove(doc)
state = State.objects.get(type=doc.type,slug='deleted')
doc.set_state(state)
# create deleted_document
e = DocEvent.objects.create(doc=doc,
by=request.user.person,
type='deleted',
desc="State set to deleted")
doc.save_with_history([e])
create_proceedings(meeting,group)
messages.success(request,'The material was deleted successfully')
if group.type.slug in ('wg','rg'):
url = reverse('proceedings_upload_unified', kwargs={'meeting_num':meeting.number,'acronym':group.acronym})
else:
url = reverse('proceedings_upload_unified', kwargs={'meeting_num':meeting.number,'session_id':session.id})
return HttpResponseRedirect(url)
@check_permissions
def edit_slide(request, slide_id):
'''
This view allows the user to edit the name of a slide.
'''
slide = get_object_or_404(Document, name=slide_id)
# derive other objects
session = slide.session_set.all()[0]
meeting = session.meeting
group = session.group
if group.type.slug in ('wg','rg'):
url = reverse('proceedings_upload_unified', kwargs={'meeting_num':meeting.number,'acronym':group.acronym})
else:
url = reverse('proceedings_upload_unified', kwargs={'meeting_num':meeting.number,'session_id':session.id})
if request.method == 'POST': # If the form has been submitted...
button_text = request.POST.get('submit', '')
if button_text == 'Cancel':
return HttpResponseRedirect(url)
form = EditSlideForm(request.POST, instance=slide) # A form bound to the POST data
if form.is_valid():
form.save()
# rebuild proceedings.html
create_proceedings(meeting,group)
return HttpResponseRedirect(url)
else:
form = EditSlideForm(instance=slide)
return render_to_response('proceedings/edit_slide.html',{
'group': group,
'meeting':meeting,
'slide':slide,
'form':form},
RequestContext(request, {}),
)
@role_required(*AUTHORIZED_ROLES)
def main(request):
@ -391,42 +263,6 @@ def main(request):
RequestContext(request,{}),
)
@check_permissions
def move_slide(request, slide_id, direction):
'''
This view will re-order slides. In addition to meeting, group and slide IDs it takes
a direction argument which is a string [up|down].
'''
slide = get_object_or_404(Document, name=slide_id)
# derive other objects
session = slide.session_set.all()[0]
meeting = session.meeting
group = session.group
qs = session.materials.exclude(states__slug='deleted').filter(type='slides').order_by('order')
# if direction is up and we aren't already the first slide
if direction == 'up' and slide_id != str(qs[0].pk):
index = find_index(slide_id, qs)
slide_before = qs[index-1]
slide_before.order, slide.order = slide.order, slide_before.order
slide.save()
slide_before.save()
# if direction is down, more than one slide and we aren't already the last slide
if direction == 'down' and qs.count() > 1 and slide_id != str(qs[qs.count()-1].pk):
index = find_index(slide_id, qs)
slide_after = qs[index+1]
slide_after.order, slide.order = slide.order, slide_after.order
slide.save()
slide_after.save()
if group.type.slug in ('wg','rg'):
url = reverse('proceedings_upload_unified', kwargs={'meeting_num':meeting.number,'acronym':group.acronym})
else:
url = reverse('proceedings_upload_unified', kwargs={'meeting_num':meeting.number,'session_id':session.id})
return HttpResponseRedirect(url)
@sec_only
def process_pdfs(request, meeting_num):
'''
@ -554,88 +390,14 @@ def recording_edit(request, meeting_num, name):
RequestContext(request, {}),
)
@check_permissions
def replace_slide(request, slide_id):
'''
This view allows the user to upload a new file to replace a slide.
'''
slide = get_object_or_404(Document, name=slide_id)
# derive other objects
session = slide.session_set.all()[0]
meeting = session.meeting
group = session.group
if group.type.slug in ('wg','rg'):
url = reverse('proceedings_upload_unified', kwargs={'meeting_num':meeting.number,'acronym':group.acronym})
else:
url = reverse('proceedings_upload_unified', kwargs={'meeting_num':meeting.number,'session_id':session.id})
if request.method == 'POST': # If the form has been submitted...
button_text = request.POST.get('submit', '')
if button_text == 'Cancel':
return HttpResponseRedirect(url)
form = ReplaceSlideForm(request.POST,request.FILES,instance=slide) # A form bound to the POST data
if form.is_valid():
new_slide = form.save(commit=False)
new_slide.time = datetime.datetime.now()
file = request.FILES[request.FILES.keys()[0]]
file_ext = os.path.splitext(file.name)[1]
disk_filename = new_slide.name + file_ext
handle_upload_file(file,disk_filename,meeting,'slides')
new_slide.external_url = disk_filename
# create DocEvent uploaded
e = DocEvent.objects.create(doc=slide,
by=request.user.person,
type='uploaded',
desc="Uploaded")
new_slide.save_with_history([e])
post_process(new_slide)
# rebuild proceedings.html
create_proceedings(meeting,group)
return HttpResponseRedirect(url)
else:
form = ReplaceSlideForm(instance=slide)
return render_to_response('proceedings/replace_slide.html',{
'group': group,
'meeting':meeting,
'slide':slide,
'form':form},
RequestContext(request, {}),
)
@role_required(*AUTHORIZED_ROLES)
# TODO - should probably rename this since it's not selecting groups anymore
@role_required('Secretariat')
def select(request, meeting_num):
'''
A screen to select which group you want to upload material for. Users of this view area
Secretariat staff and community (WG Chairs, ADs, etc). Only those groups with sessions
scheduled for the given meeting will appear in drop-downs. For Group and IRTF selects, the
value will be group.acronym to use in pretty URLs. Since Training sessions have no acronym
we'll use the session id.
Provide the secretariat only functions related to meeting materials management
'''
if request.method == 'POST':
if request.POST.get('group',None):
redirect_url = reverse('proceedings_upload_unified', kwargs={'meeting_num':meeting_num,'acronym':request.POST['group']})
return HttpResponseRedirect(redirect_url)
else:
messages.error(request, 'No Group selected')
meeting = get_object_or_404(Meeting, number=meeting_num)
user = request.user
try:
person = user.person
except ObjectDoesNotExist:
messages.warning(request, 'The account %s is not associated with any groups. If you have multiple Datatracker accounts you may try another or report a problem to ietf-action@ietf.org' % request.user)
return HttpResponseRedirect(reverse('proceedings'))
groups_session, groups_no_session = groups_by_session(user, meeting)
proceedings_url = get_proceedings_url(meeting)
# get the time proceedings were generated
@ -645,57 +407,13 @@ def select(request, meeting_num):
else:
last_run = None
# initialize group form
wgs = filter(lambda x: x.type_id in ('wg','ag','team'),groups_session)
group_form = GroupSelectForm(choices=build_choices(wgs))
# intialize IRTF form, only show if user is sec or irtf chair
if has_role(user,'Secretariat') or person.role_set.filter(name__slug='chair',group__type__slug__in=('irtf','rg')):
rgs = filter(lambda x: x.type_id == 'rg',groups_session)
irtf_form = GroupSelectForm(choices=build_choices(rgs))
else:
irtf_form = None
# initialize Training form, this select widget needs to have a session id, because
# it's utilmately the session that we associate material with
other_groups = filter(lambda x: x.type_id not in ('wg','ag','rg'),groups_session)
if other_groups:
add_choices = []
sessions = Session.objects.filter(meeting=meeting,group__in=other_groups)
for session in sessions:
if session.name.lower().find('plenary') != -1:
continue
if session.name:
name = (session.name[:75] + '..') if len(session.name) > 75 else session.name
add_choices.append((session.id,name))
else:
add_choices.append((session.id,session.group.name))
choices = sorted(add_choices,key=lambda x: x[1])
training_form = GroupSelectForm(choices=choices)
else:
training_form = None
# iniialize plenary form
if has_role(user,['Secretariat','IETF Chair','IETF Trust Chair','IAB Chair','IAOC Chair','IAD']):
ss = SchedTimeSessAssignment.objects.filter(schedule=meeting.agenda,timeslot__type='plenary')
choices = [ (i.session.id, i.session.name) for i in sorted(ss,key=lambda x: x.session.name) ]
plenary_form = GroupSelectForm(choices=choices)
else:
plenary_form = None
# count PowerPoint files waiting to be converted
if has_role(user,'Secretariat'):
ppt = Document.objects.filter(session__meeting=meeting,type='slides',external_url__endswith='.ppt').exclude(states__slug='deleted')
pptx = Document.objects.filter(session__meeting=meeting,type='slides',external_url__endswith='.pptx').exclude(states__slug='deleted')
ppt_count = ppt.count() + pptx.count()
else:
ppt_count = 0
# TODO : This should look at SessionPresentation instead
ppt = Document.objects.filter(session__meeting=meeting,type='slides',external_url__endswith='.ppt').exclude(states__slug='deleted')
pptx = Document.objects.filter(session__meeting=meeting,type='slides',external_url__endswith='.pptx').exclude(states__slug='deleted')
ppt_count = ppt.count() + pptx.count()
return render_to_response('proceedings/select.html', {
'group_form': group_form,
'irtf_form': irtf_form,
'training_form': training_form,
'plenary_form': plenary_form,
'meeting': meeting,
'last_run': last_run,
'proceedings_url': proceedings_url,
@ -703,151 +421,3 @@ def select(request, meeting_num):
RequestContext(request,{}),
)
@check_permissions
def upload_unified(request, meeting_num, acronym=None, session_id=None):
'''
This view is the main view for uploading / re-ordering material for regular and interim
meetings. There are two urls.py entries which map to this view. The acronym_id option is used
most often for groups of regular and interim meetings. session_id is used for uploading
material for Training sessions (where group is not a unique identifier). We could have used
session_id all the time but this makes for an ugly URL which most of the time would be
avoided by using acronym.
'''
def redirection_back(meeting, group):
if meeting.type.slug == 'interim':
url = reverse('proceedings')
else:
url = reverse('proceedings_select', kwargs={'meeting_num':meeting.number})
return HttpResponseRedirect(url)
meeting = get_object_or_404(Meeting, number=meeting_num)
now = datetime.datetime.now()
if acronym:
group = get_object_or_404(Group, acronym=acronym)
sessions = Session.objects.filter(meeting=meeting,group=group)
if not sessions.exists():
meeting_name = "IETF %s"%meeting.number if meeting.number.isdigit() else meeting.number
messages.warning(request, 'There does not seem to be a %s session in %s.' % (group.acronym, meeting_name))
return redirection_back(meeting, group)
session = sessions[0]
session_name = ''
elif session_id:
session = get_object_or_404(Session, id=int(session_id))
sessions = [session]
group = session.group
session_name = session.name
if request.method == 'POST':
button_text = request.POST.get('submit','')
if button_text == 'Back':
return redirection_back(meeting, group)
form = UnifiedUploadForm(request.POST,request.FILES)
if form.is_valid():
material_type = form.cleaned_data['material_type']
slide_name = form.cleaned_data['slide_name']
file = request.FILES[request.FILES.keys()[0]]
file_ext = os.path.splitext(file.name)[1]
# set the filename
if meeting.type.slug == 'ietf':
filename = '%s-%s-%s' % (material_type.slug,meeting.number,group.acronym)
elif meeting.type.slug == 'interim':
filename = '%s-%s' % (material_type.slug,meeting.number)
# NonSession material, use short name for shorter URLs
if session.short:
filename += "-%s" % session.short
elif session_name:
filename += "-%s" % slugify(session_name)
# --------------------------------
if material_type.slug == 'slides':
order_num = get_next_order_num(session)
slide_num = get_next_slide_num(session)
filename += "-%s" % slide_num
disk_filename = filename + file_ext
# create the Document object, in the case of slides the name will always be unique
# so you'll get a new object, agenda and minutes will reuse doc object if it exists
doc, created = Document.objects.get_or_create(type=material_type,
group=group,
name=filename)
doc.external_url = disk_filename
doc.time = now
if created:
doc.rev = '1'
else:
doc.rev = str(int(doc.rev) + 1)
if material_type.slug == 'slides':
doc.order=order_num
if slide_name:
doc.title = slide_name
else:
doc.title = doc.name
else:
doc.title = '%s for %s at %s' % (material_type.slug.capitalize(), group.acronym.upper(), meeting)
DocAlias.objects.get_or_create(name=doc.name, document=doc)
handle_upload_file(file,disk_filename,meeting,material_type.slug)
# set Doc state
if doc.type.slug=='slides':
doc.set_state(State.objects.get(type=doc.type,slug='archived'))
doc.set_state(State.objects.get(type='reuse_policy',slug='single'))
else:
doc.set_state(State.objects.get(type=doc.type,slug='active'))
# create session relationship, per Henrik we should associate documents to all sessions
# for the current meeting (until tools support different materials for diff sessions)
for s in sessions:
try:
sp = s.sessionpresentation_set.get(document=doc)
sp.rev = doc.rev
sp.save()
except ObjectDoesNotExist:
s.sessionpresentation_set.create(document=doc,rev=doc.rev)
# create NewRevisionDocEvent instead of uploaded, per Ole
e = NewRevisionDocEvent.objects.create(type='new_revision',
by=request.user.person,
doc=doc,
rev=doc.rev,
desc='New revision available')
doc.save_with_history([e])
post_process(doc)
create_proceedings(meeting,group)
messages.success(request,'File uploaded sucessfully')
else:
form = UnifiedUploadForm(initial={'meeting_id':meeting.id,'acronym':group.acronym,'material_type':'slides'})
materials = get_materials(group,meeting)
# gather DocEvents
# include deleted material to catch deleted doc events
#docs = session.materials.all()
# Don't report on draft DocEvents since the secr/materials app isn't managing them
docs = session.materials.exclude(type='draft')
docevents = DocEvent.objects.filter(doc__in=docs)
path = get_proceedings_path(meeting,group)
if os.path.exists(path):
proceedings_url = get_proceedings_url(meeting,group)
else:
proceedings_url = ''
return render_to_response('proceedings/upload_unified.html', {
'docevents': docevents,
'meeting': meeting,
'group': group,
'materials': materials,
'form': form,
'session_name': session_name, # for Tutorials, etc
'proceedings_url': proceedings_url},
RequestContext(request, {}),
)

View file

@ -1,6 +0,0 @@
<div class="button-group">
<ul>
<li><button type="submit" name="submit" value="Submit">Submit</button></li>
<li><button type="submit" name="submit" value="Back">Back</button></li>
</ul>
</div> <!-- button-group -->

View file

@ -1,25 +0,0 @@
{% load ams_filters %}
<table id="slides" class="center sortable" cellspacing="0">
<thead>
<tr>
<th>Slide</th>
<th>Filename</th>
<th>Edit</th>
<th>Replace</th>
<th>Delete</th>
</tr>
</thead>
<tbody>
{% for slide in materials.slides %}
<tr id="slide_{{ forloop.counter }}" class="{% cycle 'row1' 'row2' %}">
<td><span class="ui-icon ui-icon-arrowthick-2-n-s"></span><a href="{{ slide.get_absolute_url }}" target="_blank">{{ slide.title }}</a></td>
<td>{{ slide.external_url }}{% if slide.external_url|is_ppt %}<span class="required"> *</span>{% endif %}</td>
<td><a href="{% url "proceedings_edit_slide" slide_id=slide.pk %}">Edit</a></td>
<td><a href="{% url "proceedings_replace_slide" slide_id=slide.pk %}">Replace</a></td>
<td><a href="{% url "proceedings_delete_material" slide_id=slide.name %}">Delete</a></td>
<td class="hidden">{{ slide.pk }}</td>
</tr>
{% endfor %}
</tbody>
</table>

View file

@ -1,38 +0,0 @@
{% extends "base_site.html" %}
{% load staticfiles %}
{% block title %}Edit Slide{% endblock %}
{% block extrahead %}{{ block.super }}
<script type="text/javascript" src="{% static 'secr/js/utils.js' %}"></script>
{% endblock %}
{% block breadcrumbs %}{{ block.super }}
{% if meeting.type_id == "interim" %}
&raquo; <a href="{% url "proceedings" %}">Proceedings</a>
&raquo; <a href="{% url "proceedings_upload_unified" meeting_num=meeting.number acronym=group.acronym %}">{{ meeting }}</a>
&raquo; {{ slide.title }}
{% else %}
&raquo; <a href="{% url "proceedings" %}">Proceedings</a>
&raquo; <a href="{% url "proceedings_select" meeting_num=meeting.number %}">Select</a>
&raquo; <a href="{% url "proceedings_upload_unified" meeting_num=meeting.number acronym=group.acronym%}">{{ group.acronym }}</a>
&raquo; {{ slide.title }}
{% endif %}
{% endblock %}
{% block content %}
<div class="module interim-container">
<h2>Working Group - {{ group.acronym }}</h2>
<p><h3>Edit Slide:</h3></p>
<form enctype="multipart/form-data" action="." method="post">{% csrf_token %}
<table>
{{ form.as_table }}
</table>
{% include "includes/buttons_save_cancel.html" %}
</form>
</div> <!-- module -->
{% endblock %}

View file

@ -54,7 +54,7 @@
<tr class = "{% cycle 'row1' 'row2' %}">
<td>{{ meeting.group.acronym }}</td>
<td><a href="{% url "proceedings_upload_unified" meeting_num=meeting.number acronym=meeting.group.acronym %}">{{ meeting.date }}</a></td>
<td><a href="{% url "ietf.meeting.views.session_details" num=meeting.number acronym=meeting.group.acronym %}">{{ meeting.date }}</a></td>
</tr>
{% endfor %}
</tbody>

View file

@ -1,38 +0,0 @@
{% extends "base_site.html" %}
{% load staticfiles %}
{% block title %}Replace Slide{% endblock %}
{% block extrahead %}{{ block.super }}
<script type="text/javascript" src="{% static 'secr/js/utils.js' %}"></script>
{% endblock %}
{% block breadcrumbs %}{{ block.super }}
{% if meeting.type_id == "interim" %}
&raquo; <a href="{% url "proceedings" %}">Proceedings</a>
&raquo; <a href="{% url "proceedings_upload_unified" meeting_num=meeting.number acronym=group.acronym %}">{{ meeting }}</a>
&raquo; {{ slide.title }}
{% else %}
&raquo; <a href="{% url "proceedings" %}">Proceedings</a>
&raquo; <a href="{% url "proceedings_select" meeting_num=meeting.number %}">Select</a>
&raquo; <a href="{% url "proceedings_upload_unified" meeting_num=meeting.number acronym=group.acronym %}">{{ group.acronym }}</a>
&raquo; {{ slide.title }}
{% endif %}
{% endblock %}
{% block content %}
<div class="module interim-container">
<h2>Working Group - {{ group.acronym }}</h2>
<p><h3>Replace Slide:</h3></p>
<form enctype="multipart/form-data" action="." method="post">{% csrf_token %}
<table>
{{ form.as_table }}
</table>
{% include "includes/buttons_save_cancel.html" %}
</form>
</div> <!-- module -->
{% endblock %}

View file

@ -29,7 +29,7 @@
{% block breadcrumbs %}{{ block.super }}
&raquo; <a href="{% url "proceedings" %}">Proceedings</a>
&raquo; {{ meeting.number }} - Select Group
&raquo; {{ meeting.number }}
{% endblock %}
{% block instructions %}
@ -39,49 +39,7 @@
{% block content %}
<div class="module interim-container">
<h2>IETF {{ meeting.number }} - Upload Material</span></h2>
<div class="inline-related">
<h3><b>Select Group</b></h3>
<p>The list below includes those working groups and approved BOFs that are scheduled to
meet at IETF {{meeting.number }}. You must request a session slot in order for your group(s) to appear on
this list.</p>
<p>- To request a session slot for a working group, please use the
<a href="{% url "sessions" %}">IETF Meeting Session Request Tool</a>.
<br>
- To request a session slot for a BOF, please send a message to <a href="mailto:ietf-action@ietf.org">agenda@ietf.org</a>.
Additional information is available at
<a href="https://www.ietf.org/instructions/MTG-SLOTS.html">"Requesting Meeting Slots at IETF Meetings."</a>
<br>
- To upload meeting materials for a scheduled session, please select the session name below.</p>
<form class="internal-form" action="" method="post">{% csrf_token %}
<label for="id_group">Working Groups</label>
{{ group_form.group }}
<input type="submit" name="submit" value="Select" />
</form>
{% if irtf_form %}
<form class="internal-form" action="" method="post">{% csrf_token %}
<label for="id_group">IRTF Groups</label>
{{ irtf_form.group }}
<input type="submit" name="submit" value="Select" />
</form>
{% endif %}
{% if training_form %}
<form class="internal-form" action="" method="post">{% csrf_token %}
<label for="id_group">Training / Other</label>
{{ training_form.group }}
<input type="submit" name="submit" value="Select" />
</form>
{% endif %}
{% if plenary_form %}
<form class="internal-form" action="" method="post">{% csrf_token %}
<label for="id_group">Plenaries</label>
{{ plenary_form.group }}
<input type="submit" name="submit" value="Select" />
</form>
{% endif %}
</div> <!-- inline-related -->
{% if user|has_role:"Secretariat" %}
<div class="inline-related">
<h2>Secretariat Only Functions</h2>
<div id="private-functions">
@ -90,7 +48,6 @@
</div> <!-- private-functions -->
</div> <!-- inline-group -->
{% endif %}
</div> <!-- module -->

View file

@ -1,34 +0,0 @@
{% extends "base_site.html" %}
{% load staticfiles %}
{% block title %}Proceedings - Upload Presentations {% endblock %}
{% block extrahead %}{{ block.super }}
<script type="text/javascript" src="{% static 'secr/js/utils.js' %}"></script>
{% endblock %}
{% block breadcrumbs %}{{ block.super }}
&raquo; <a href="../../../">Proceedings</a>
&raquo; <a href="../../">{{ meeting.meeting_num }}</a>
&raquo; <a href="../../convert">Convert Material</a>
&raquo; Upload Presentation
{% endblock %}
{% block content %}
<div class="module">
<form enctype="multipart/form-data" action="" method="post">{% csrf_token %}
<h2>Proceedings - Upload (HTML-Zipped) Presentation : <font color="RED">{{ slide.group_name }}</font></h2>
<p>For Presentation : {{ slide.slide_name }}</p>
<table>
{{ upload_presentation.as_table }}
</table>
<div class="button-group">
<ul>
<li><input type="submit" value="Upload" class="standard" /></li>
<!--li><input type="submit" value="Cancel" class="standard" /></li-->
</ul>
</div> <!-- button-group -->
</form>
</div> <!-- module -->
{% endblock %}

View file

@ -1,116 +0,0 @@
{% extends "base_site.html" %}
{% load ietf_filters %}
{% load staticfiles %}
{% block title %}Proceedings{% endblock %}
{% block extrastyle %}{{ block.super }}
<link rel="stylesheet" type="text/css" href="{% static 'secr/css/jquery-ui-1.11.4.custom.css' %}" />
{% endblock %}
{% block extrahead %}{{ block.super }}
<script type="text/javascript" src="{% static 'secr/js/jquery-ui-1.11.4.custom.min.js' %}"></script>
<script type="text/javascript" src="{% static 'secr/js/utils.js' %}"></script>
{% endblock %}
{% block breadcrumbs %}{{ block.super }}
{% if meeting.type_id == "interim" %}
&raquo; <a href="{% url "proceedings" %}">Proceedings</a>
&raquo; {{ meeting }}
{% else %}
&raquo; <a href="{% url "proceedings" %}">Proceedings</a>
&raquo; <a href="{% url "proceedings_select" meeting_num=meeting.number %}">{{ meeting }}</a>
&raquo; {% if session_name %}{{ session_name }}{% else %}{{ group.acronym }}{% endif %}
{% endif %}
{% endblock %}
{% block content %}
<div class="module interim-container">
<h2>{{ meeting }} - Upload Material - {% if session_name %}{{ session_name }}{% else %}Group: {{ group.acronym }}{% endif %}
</h2>
<table class="center">
<thead>
<tr>
<th>Type</th>
<th>Filename</th>
<th>Delete</th>
</tr>
</thead>
<tbody>
<tr>
{% if proceedings_url %}
<td><a href="{{ proceedings_url|safe }}" target="_blank">Proceedings</a></td>
<td>proceedings.html</td>
{% else %}
<td>Proceedings</td>
<td>Proceedings not yet generated</td>
{% endif %}
<td></td>
</tr>
<tr>
{% if materials.minutes %}
<td><a href="{{ materials.minutes.get_absolute_url }}" target="_blank">Minutes</td>
<td>{{ materials.minutes.external_url }}</td>
<td><a href="{% url "proceedings_delete_material" slide_id=materials.minutes.name %}">Delete</a></td>
{% else %}
<td>Minutes</td>
<td>(not uploaded)</td>
<td></td>
{% endif %}
</tr>
<tr>
{% if materials.agenda %}
<td><a href="{{ materials.agenda.get_absolute_url }}" target="_blank">Agenda</a></td>
<td>{{ materials.agenda.external_url }}</td>
<td><a href="{% url "proceedings_delete_material" slide_id=materials.agenda.name %}">Delete</a></td>
{% else %}
<td>Agenda</td>
<td>(not uploaded)</td>
<td></td>
{% endif %}
</tr>
{% if materials.bluesheets %}
{% for item in materials.bluesheets %}
<tr>
<td><a href="{{ item.get_absolute_url }}" target="_blank">Bluesheet</a></td>
<td>{{ item.external_url }}</td>
<td>{% if user|has_role:"Secretariat" %}<a href="{% url "proceedings_delete_material" slide_id=item.name %}">Delete</a>{% endif %}</td>
</tr>
{% endfor %}
{% endif %}
</tbody>
</table>
<div class="inline-related">
<h2>Slides&nbsp;&nbsp;<span class="highlight">NEW! Drag and drop to re-order slides</span></h2>
{% include "includes/slides.html" %}
<p>(<span class="required"> *</span> - Waiting to be converted to PDF format.)</p>
</div> <!-- inline-group -->
<div class="inline-related">
<h2>Upload Materials</h2>
<form id="upload_materials_form" enctype="multipart/form-data" action="." method="post">{% csrf_token %}
<table class="center" id="proceedings-upload-table">
{{ form.as_table }}
</table>
{% include "includes/buttons_submit_back.html" %}
</form>
</div> <!-- inline-group -->
</div> <!-- module -->
{% if docevents %}
<br>
<div class="module interim-container">
{% include "includes/docevents.html" %}
</div>
{% endif %}
{% endblock %}
{% block footer-extras %}
{% include "includes/upload_footer.html" %}
{% endblock %}