Allow slides drag-drop between sessions for groups meeting with multiple sessions. Fixes #2700. Commit ready to merge.

- Legacy-Id: 17024
This commit is contained in:
Robert Sparks 2019-11-15 20:54:14 +00:00
parent 34f93932a2
commit 1016b3a514
7 changed files with 493 additions and 57 deletions

File diff suppressed because one or more lines are too long

View file

@ -23,6 +23,7 @@ from django.urls import reverse as urlreverse
from django.conf import settings
from django.contrib.auth.models import User
from django.test import Client
from django.db.models import F
import debug # pyflakes:ignore
@ -34,7 +35,7 @@ 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, SlideSubmission
from ietf.meeting.test_data import make_meeting_test_data, make_interim_meeting
from ietf.meeting.utils import finalize
from ietf.meeting.utils import finalize, condition_slide_order
from ietf.name.models import SessionStatusName, ImportantDateName
from ietf.utils.decorators import skip_coverage
from ietf.utils.mail import outbox, empty_outbox
@ -569,18 +570,330 @@ class MeetingTests(TestCase):
self.assertEqual(response.status_code,200)
self.assertEqual(response.get('Content-Type'), 'text/calendar')
def test_edit_slide_order(self):
session=SessionFactory(meeting__type_id='iestf',type_id='session')
slides = DocumentFactory(type_id='slides')
session.sessionpresentation_set.create(document=slides,order=0)
url = urlreverse('ietf.meeting.views.set_slide_order',kwargs={'session_id':session.id,'num':session.meeting.number,'name':slides.name})
response = self.client.put(url,{'order':2})
self.assertEqual(response.status_code, 403)
self.client.login(username='secretary', password='secretary+password')
response = self.client.post(url,{'order':'2'})
self.assertEqual(response.status_code, 200)
self.assertEqual(response.get('Content-Type'), 'application/json')
self.assertEqual(session.sessionpresentation_set.first().order,2)
class ReorderSlidesTests(TestCase):
def test_add_slides_to_session(self):
for type_id in ('ietf','interim'):
chair_role = RoleFactory(name_id='chair')
session = SessionFactory(group=chair_role.group, meeting__date=datetime.date.today()-datetime.timedelta(days=90), meeting__type_id=type_id)
slides = DocumentFactory(type_id='slides')
url = urlreverse('ietf.meeting.views.ajax_add_slides_to_session', kwargs={'session_id':session.pk, 'num':session.meeting.number})
# Not a valid user
r = self.client.post(url, {'order':1, 'name':slides.name })
self.assertEqual(r.status_code, 403)
self.assertIn('have permission', unicontent(r))
self.client.login(username=chair_role.person.user.username, password=chair_role.person.user.username+"+password")
# Past submission cutoff
r = self.client.post(url, {'order':0, 'name':slides.name })
self.assertEqual(r.status_code, 403)
self.assertIn('materials cutoff', unicontent(r))
session.meeting.date = datetime.date.today()
session.meeting.save()
# Invalid order
r = self.client.post(url, {})
self.assertEqual(r.status_code, 200)
self.assertEqual(r.json()['success'],False)
self.assertIn('No data',r.json()['error'])
r = self.client.post(url, {'garbage':'garbage'})
self.assertEqual(r.status_code, 200)
self.assertEqual(r.json()['success'],False)
self.assertIn('order is not valid',r.json()['error'])
r = self.client.post(url, {'order':0, 'name':slides.name })
self.assertEqual(r.status_code, 200)
self.assertEqual(r.json()['success'],False)
self.assertIn('order is not valid',r.json()['error'])
r = self.client.post(url, {'order':2, 'name':slides.name })
self.assertEqual(r.status_code, 200)
self.assertEqual(r.json()['success'],False)
self.assertIn('order is not valid',r.json()['error'])
r = self.client.post(url, {'order':'garbage', 'name':slides.name })
self.assertEqual(r.status_code, 200)
self.assertEqual(r.json()['success'],False)
self.assertIn('order is not valid',r.json()['error'])
# Invalid name
r = self.client.post(url, {'order':1 })
self.assertEqual(r.status_code, 200)
self.assertEqual(r.json()['success'],False)
self.assertIn('name is not valid',r.json()['error'])
r = self.client.post(url, {'order':1, 'name':'garbage' })
self.assertEqual(r.status_code, 200)
self.assertEqual(r.json()['success'],False)
self.assertIn('name is not valid',r.json()['error'])
# Valid post
r = self.client.post(url, {'order':1, 'name':slides.name })
self.assertEqual(r.status_code, 200)
self.assertEqual(r.json()['success'],True)
self.assertEqual(session.sessionpresentation_set.count(),1)
# Ingore a request to add slides that are already in a session
r = self.client.post(url, {'order':1, 'name':slides.name })
self.assertEqual(r.status_code, 200)
self.assertEqual(r.json()['success'],True)
self.assertEqual(session.sessionpresentation_set.count(),1)
session2 = SessionFactory(group=session.group, meeting=session.meeting)
SessionPresentationFactory.create_batch(3, document__type_id='slides', session=session2)
for num, sp in enumerate(session2.sessionpresentation_set.filter(document__type_id='slides'),start=1):
sp.order = num
sp.save()
url = urlreverse('ietf.meeting.views.ajax_add_slides_to_session', kwargs={'session_id':session2.pk, 'num':session2.meeting.number})
more_slides = DocumentFactory.create_batch(3, type_id='slides')
# Insert at beginning
r = self.client.post(url, {'order':1, 'name':more_slides[0].name})
self.assertEqual(r.status_code, 200)
self.assertEqual(r.json()['success'],True)
self.assertEqual(session2.sessionpresentation_set.get(document=more_slides[0]).order,1)
self.assertEqual(list(session2.sessionpresentation_set.order_by('order').values_list('order',flat=True)), range(1,5))
# Insert at end
r = self.client.post(url, {'order':5, 'name':more_slides[1].name})
self.assertEqual(r.status_code, 200)
self.assertEqual(r.json()['success'],True)
self.assertEqual(session2.sessionpresentation_set.get(document=more_slides[1]).order,5)
self.assertEqual(list(session2.sessionpresentation_set.order_by('order').values_list('order',flat=True)), range(1,6))
# Insert in middle
r = self.client.post(url, {'order':3, 'name':more_slides[2].name})
self.assertEqual(r.status_code, 200)
self.assertEqual(r.json()['success'],True)
self.assertEqual(session2.sessionpresentation_set.get(document=more_slides[2]).order,3)
self.assertEqual(list(session2.sessionpresentation_set.order_by('order').values_list('order',flat=True)), range(1,7))
def test_remove_slides_from_session(self):
for type_id in ['ietf','interim']:
chair_role = RoleFactory(name_id='chair')
session = SessionFactory(group=chair_role.group, meeting__date=datetime.date.today()-datetime.timedelta(days=90), meeting__type_id=type_id)
slides = DocumentFactory(type_id='slides')
url = urlreverse('ietf.meeting.views.ajax_remove_slides_from_session', kwargs={'session_id':session.pk, 'num':session.meeting.number})
# Not a valid user
r = self.client.post(url, {'oldIndex':1, 'name':slides.name })
self.assertEqual(r.status_code, 403)
self.assertIn('have permission', unicontent(r))
self.client.login(username=chair_role.person.user.username, password=chair_role.person.user.username+"+password")
# Past submission cutoff
r = self.client.post(url, {'oldIndex':0, 'name':slides.name })
self.assertEqual(r.status_code, 403)
self.assertIn('materials cutoff', unicontent(r))
session.meeting.date = datetime.date.today()
session.meeting.save()
# Invalid order
r = self.client.post(url, {})
self.assertEqual(r.status_code, 200)
self.assertEqual(r.json()['success'],False)
self.assertIn('No data',r.json()['error'])
r = self.client.post(url, {'garbage':'garbage'})
self.assertEqual(r.status_code, 200)
self.assertEqual(r.json()['success'],False)
self.assertIn('index is not valid',r.json()['error'])
r = self.client.post(url, {'oldIndex':0, 'name':slides.name })
self.assertEqual(r.status_code, 200)
self.assertEqual(r.json()['success'],False)
self.assertIn('index is not valid',r.json()['error'])
r = self.client.post(url, {'oldIndex':'garbage', 'name':slides.name })
self.assertEqual(r.status_code, 200)
self.assertEqual(r.json()['success'],False)
self.assertIn('index is not valid',r.json()['error'])
# No matching thing to delete
r = self.client.post(url, {'oldIndex':1, 'name':slides.name })
self.assertEqual(r.status_code, 200)
self.assertEqual(r.json()['success'],False)
self.assertIn('index is not valid',r.json()['error'])
session.sessionpresentation_set.create(document=slides, rev=slides.rev, order=1)
# Bad names
r = self.client.post(url, {'oldIndex':1})
self.assertEqual(r.status_code, 200)
self.assertEqual(r.json()['success'],False)
self.assertIn('name is not valid',r.json()['error'])
r = self.client.post(url, {'oldIndex':1, 'name':'garbage' })
self.assertEqual(r.status_code, 200)
self.assertEqual(r.json()['success'],False)
self.assertIn('name is not valid',r.json()['error'])
slides2 = DocumentFactory(type_id='slides')
# index/name mismatch
r = self.client.post(url, {'oldIndex':1, 'name':slides2.name })
self.assertEqual(r.status_code, 200)
self.assertEqual(r.json()['success'],False)
self.assertIn('SessionPresentation not found',r.json()['error'])
session.sessionpresentation_set.create(document=slides2, rev=slides2.rev, order=2)
r = self.client.post(url, {'oldIndex':1, 'name':slides2.name })
self.assertEqual(r.status_code, 200)
self.assertEqual(r.json()['success'],False)
self.assertIn('Name does not match index',r.json()['error'])
# valid removal
r = self.client.post(url, {'oldIndex':1, 'name':slides.name })
self.assertEqual(r.status_code, 200)
self.assertEqual(r.json()['success'],True)
self.assertEqual(session.sessionpresentation_set.count(),1)
session2 = SessionFactory(group=session.group, meeting=session.meeting)
sp_list = SessionPresentationFactory.create_batch(5, document__type_id='slides', session=session2)
for num, sp in enumerate(session2.sessionpresentation_set.filter(document__type_id='slides'),start=1):
sp.order = num
sp.save()
url = urlreverse('ietf.meeting.views.ajax_remove_slides_from_session', kwargs={'session_id':session2.pk, 'num':session2.meeting.number})
# delete at first of list
r = self.client.post(url, {'oldIndex':1, 'name':sp_list[0].document.name })
self.assertEqual(r.status_code, 200)
self.assertEqual(r.json()['success'],True)
self.assertFalse(session2.sessionpresentation_set.filter(pk=sp_list[0].pk).exists())
self.assertEqual(list(session2.sessionpresentation_set.order_by('order').values_list('order',flat=True)), range(1,5))
# delete in middle of list
r = self.client.post(url, {'oldIndex':4, 'name':sp_list[4].document.name })
self.assertEqual(r.status_code, 200)
self.assertEqual(r.json()['success'],True)
self.assertFalse(session2.sessionpresentation_set.filter(pk=sp_list[4].pk).exists())
self.assertEqual(list(session2.sessionpresentation_set.order_by('order').values_list('order',flat=True)), range(1,4))
# delete at end of list
r = self.client.post(url, {'oldIndex':2, 'name':sp_list[2].document.name })
self.assertEqual(r.status_code, 200)
self.assertEqual(r.json()['success'],True)
self.assertFalse(session2.sessionpresentation_set.filter(pk=sp_list[2].pk).exists())
self.assertEqual(list(session2.sessionpresentation_set.order_by('order').values_list('order',flat=True)), range(1,3))
def test_reorder_slides_in_session(self):
chair_role = RoleFactory(name_id='chair')
session = SessionFactory(group=chair_role.group, meeting__date=datetime.date.today()-datetime.timedelta(days=90))
sp_list = SessionPresentationFactory.create_batch(5, document__type_id='slides', session=session)
for num, sp in enumerate(sp_list, start=1):
sp.order = num
sp.save()
url = urlreverse('ietf.meeting.views.ajax_reorder_slides_in_session', kwargs={'session_id':session.pk, 'num':session.meeting.number})
for type_id in ['ietf','interim']:
session.meeting.type_id = type_id
session.meeting.date = datetime.date.today()-datetime.timedelta(days=90)
session.meeting.save()
# Not a valid user
r = self.client.post(url, {'oldIndex':1, 'newIndex':2 })
self.assertEqual(r.status_code, 403)
self.assertIn('have permission', unicontent(r))
self.client.login(username=chair_role.person.user.username, password=chair_role.person.user.username+"+password")
# Past submission cutoff
r = self.client.post(url, {'oldIndex':1, 'newIndex':2 })
self.assertEqual(r.status_code, 403)
self.assertIn('materials cutoff', unicontent(r))
session.meeting.date = datetime.date.today()
session.meeting.save()
# Bad index values
r = self.client.post(url, {'oldIndex':0, 'newIndex':2 })
self.assertEqual(r.status_code, 200)
self.assertEqual(r.json()['success'],False)
self.assertIn('index is not valid',r.json()['error'])
r = self.client.post(url, {'oldIndex':2, 'newIndex':6 })
self.assertEqual(r.status_code, 200)
self.assertEqual(r.json()['success'],False)
self.assertIn('index is not valid',r.json()['error'])
r = self.client.post(url, {'oldIndex':2, 'newIndex':2 })
self.assertEqual(r.status_code, 200)
self.assertEqual(r.json()['success'],False)
self.assertIn('index is not valid',r.json()['error'])
# Move from beginning
r = self.client.post(url, {'oldIndex':1, 'newIndex':3})
self.assertEqual(r.status_code, 200)
self.assertEqual(r.json()['success'],True)
self.assertEqual(list(session.sessionpresentation_set.order_by('order').values_list('pk',flat=True)),[2,3,1,4,5])
# Move to beginning
r = self.client.post(url, {'oldIndex':3, 'newIndex':1})
self.assertEqual(r.status_code, 200)
self.assertEqual(r.json()['success'],True)
self.assertEqual(list(session.sessionpresentation_set.order_by('order').values_list('pk',flat=True)),[1,2,3,4,5])
# Move from end
r = self.client.post(url, {'oldIndex':5, 'newIndex':3})
self.assertEqual(r.status_code, 200)
self.assertEqual(r.json()['success'],True)
self.assertEqual(list(session.sessionpresentation_set.order_by('order').values_list('pk',flat=True)),[1,2,5,3,4])
# Move to end
r = self.client.post(url, {'oldIndex':3, 'newIndex':5})
self.assertEqual(r.status_code, 200)
self.assertEqual(r.json()['success'],True)
self.assertEqual(list(session.sessionpresentation_set.order_by('order').values_list('pk',flat=True)),[1,2,3,4,5])
# Move beginning to end
r = self.client.post(url, {'oldIndex':1, 'newIndex':5})
self.assertEqual(r.status_code, 200)
self.assertEqual(r.json()['success'],True)
self.assertEqual(list(session.sessionpresentation_set.order_by('order').values_list('pk',flat=True)),[2,3,4,5,1])
# Move middle to middle
r = self.client.post(url, {'oldIndex':3, 'newIndex':4})
self.assertEqual(r.status_code, 200)
self.assertEqual(r.json()['success'],True)
self.assertEqual(list(session.sessionpresentation_set.order_by('order').values_list('pk',flat=True)),[2,3,5,4,1])
r = self.client.post(url, {'oldIndex':3, 'newIndex':2})
self.assertEqual(r.status_code, 200)
self.assertEqual(r.json()['success'],True)
self.assertEqual(list(session.sessionpresentation_set.order_by('order').values_list('pk',flat=True)),[2,5,3,4,1])
# Reset for next iteration in the loop
session.sessionpresentation_set.update(order=F('pk'))
self.client.logout()
def test_slide_order_reconditioning(self):
chair_role = RoleFactory(name_id='chair')
session = SessionFactory(group=chair_role.group, meeting__date=datetime.date.today()-datetime.timedelta(days=90))
sp_list = SessionPresentationFactory.create_batch(5, document__type_id='slides', session=session)
for num, sp in enumerate(sp_list, start=1):
sp.order = 2*num
sp.save()
try:
condition_slide_order(session)
except AssertionError:
pass
self.assertEqual(list(session.sessionpresentation_set.order_by('order').values_list('order',flat=True)),range(1,6))
class EditTests(TestCase):
def setUp(self):

View file

@ -1,4 +1,4 @@
# Copyright The IETF Trust 2007, All Rights Reserved
# Copyright The IETF Trust 2007-2019, All Rights Reserved
from django.conf.urls import include
from django.views.generic import RedirectView
@ -15,7 +15,9 @@ safe_for_all_meeting_types = [
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+)/add_to_session$', views.ajax_add_slides_to_session),
url(r'^session/(?P<session_id>\d+)/remove_from_session$', views.ajax_remove_slides_from_session),
url(r'^session/(?P<session_id>\d+)/reorder_in_session$', views.ajax_reorder_slides_in_session),
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),

View file

@ -19,6 +19,7 @@ from ietf.meeting.models import Session, Meeting
from ietf.group.utils import can_manage_materials
from ietf.person.models import Email
from ietf.secr.proceedings.proc_utils import import_audio_files
from ietf.utils.log import unreachable
def group_sessions(sessions):
@ -178,3 +179,15 @@ def sort_accept_tuple(accept):
tup.append((keys[0], q))
return sorted(tup, key = lambda x: float(x[1]), reverse = True)
return tup
def condition_slide_order(session):
qs = session.sessionpresentation_set.filter(document__type_id='slides').order_by('order')
order_list = qs.values_list('order',flat=True)
#assertion('list(order_list) == range(1,qs.count()+1)')
if list(order_list) != range(1,qs.count()+1):
for num, sp in enumerate(qs, start=1):
sp.order=num
sp.save()
unreachable('2019-11-15')

View file

@ -34,7 +34,7 @@ from django.contrib.auth.decorators import login_required
from django.core.exceptions import ValidationError
from django.core.validators import URLValidator
from django.urls import reverse,reverse_lazy
from django.db.models import Min, Max, Q
from django.db.models import Min, Max, Q, F
from django.forms.models import modelform_factory, inlineformset_factory
from django.template import TemplateDoesNotExist
from django.template.loader import render_to_string
@ -67,8 +67,7 @@ from ietf.meeting.helpers import sessions_post_save, is_meeting_approved
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.meeting.utils import sort_accept_tuple
from ietf.meeting.utils import finalize, sort_accept_tuple, condition_slide_order
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,
@ -1663,11 +1662,9 @@ def remove_sessionpresentation(request, session_id, num, name):
return render(request,'meeting/remove_sessionpresentation.html', {'sp': sp })
def set_slide_order(request, session_id, num, name):
# num is redundant, but we're dragging it along an artifact of where we are in the current URL structure
def ajax_add_slides_to_session(request, session_id, num):
session = get_object_or_404(Session,pk=session_id)
if not Document.objects.filter(type_id='slides',name=name).exists():
raise Http404
if not session.can_manage_materials(request.user):
return HttpResponseForbidden("You don't have permission to upload slides for this session.")
if session.is_material_submission_cutoff() and not has_role(request.user, "Secretariat"):
@ -1675,19 +1672,109 @@ def set_slide_order(request, session_id, num, name):
if request.method != 'POST' or not request.POST:
return HttpResponse(json.dumps({ 'success' : False, 'error' : 'No data submitted or not POST' }),content_type='application/json')
order_str = request.POST.get('order', None)
try:
order = int(order_str)
except ValueError:
except (ValueError, TypeError):
return HttpResponse(json.dumps({ 'success' : False, 'error' : 'Supplied order is not valid' }),content_type='application/json')
if order <=0 or order > 32767 :
if order < 1 or order > session.sessionpresentation_set.filter(document__type_id='slides').count() + 1 :
return HttpResponse(json.dumps({ 'success' : False, 'error' : 'Supplied order is not valid' }),content_type='application/json')
sp = session.sessionpresentation_set.get(document__name = name)
sp.order = order
name = request.POST.get('name', None)
doc = Document.objects.filter(name=name).first()
if not doc:
return HttpResponse(json.dumps({ 'success' : False, 'error' : 'Supplied name is not valid' }),content_type='application/json')
if not session.sessionpresentation_set.filter(document=doc).exists():
condition_slide_order(session)
session.sessionpresentation_set.filter(document__type_id='slides', order__gte=order).update(order=F('order')+1)
session.sessionpresentation_set.create(document=doc,rev=doc.rev,order=order)
DocEvent.objects.create(type="added_comment", doc=doc, rev=doc.rev, by=request.user.person, desc="Added to session: %s" % session)
return HttpResponse(json.dumps({'success':True}), content_type='application/json')
def ajax_remove_slides_from_session(request, session_id, num):
session = get_object_or_404(Session,pk=session_id)
if not session.can_manage_materials(request.user):
return HttpResponseForbidden("You don't have permission to upload slides for this session.")
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.")
if request.method != 'POST' or not request.POST:
return HttpResponse(json.dumps({ 'success' : False, 'error' : 'No data submitted or not POST' }),content_type='application/json')
oldIndex_str = request.POST.get('oldIndex', None)
try:
oldIndex = int(oldIndex_str)
except (ValueError, TypeError):
return HttpResponse(json.dumps({ 'success' : False, 'error' : 'Supplied index is not valid' }),content_type='application/json')
if oldIndex < 1 or oldIndex > session.sessionpresentation_set.filter(document__type_id='slides').count() :
return HttpResponse(json.dumps({ 'success' : False, 'error' : 'Supplied index is not valid' }),content_type='application/json')
name = request.POST.get('name', None)
doc = Document.objects.filter(name=name).first()
if not doc:
return HttpResponse(json.dumps({ 'success' : False, 'error' : 'Supplied name is not valid' }),content_type='application/json')
condition_slide_order(session)
affected_presentations = session.sessionpresentation_set.filter(document=doc).first()
if affected_presentations:
if affected_presentations.order == oldIndex:
affected_presentations.delete()
session.sessionpresentation_set.filter(document__type_id='slides', order__gt=oldIndex).update(order=F('order')-1)
DocEvent.objects.create(type="added_comment", doc=doc, rev=doc.rev, by=request.user.person, desc="Removed from session: %s" % session)
return HttpResponse(json.dumps({'success':True}), content_type='application/json')
else:
return HttpResponse(json.dumps({ 'success' : False, 'error' : 'Name does not match index' }),content_type='application/json')
else:
return HttpResponse(json.dumps({ 'success' : False, 'error' : 'SessionPresentation not found' }),content_type='application/json')
def ajax_reorder_slides_in_session(request, session_id, num):
session = get_object_or_404(Session,pk=session_id)
if not session.can_manage_materials(request.user):
return HttpResponseForbidden("You don't have permission to upload slides for this session.")
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.")
if request.method != 'POST' or not request.POST:
return HttpResponse(json.dumps({ 'success' : False, 'error' : 'No data submitted or not POST' }),content_type='application/json')
num_slides_in_session = session.sessionpresentation_set.filter(document__type_id='slides').count()
oldIndex_str = request.POST.get('oldIndex', None)
try:
oldIndex = int(oldIndex_str)
except (ValueError, TypeError):
return HttpResponse(json.dumps({ 'success' : False, 'error' : 'Supplied index is not valid' }),content_type='application/json')
if oldIndex < 1 or oldIndex > num_slides_in_session :
return HttpResponse(json.dumps({ 'success' : False, 'error' : 'Supplied index is not valid' }),content_type='application/json')
newIndex_str = request.POST.get('newIndex', None)
try:
newIndex = int(newIndex_str)
except (ValueError, TypeError):
return HttpResponse(json.dumps({ 'success' : False, 'error' : 'Supplied index is not valid' }),content_type='application/json')
if newIndex < 1 or newIndex > num_slides_in_session :
return HttpResponse(json.dumps({ 'success' : False, 'error' : 'Supplied index is not valid' }),content_type='application/json')
if newIndex == oldIndex:
return HttpResponse(json.dumps({ 'success' : False, 'error' : 'Supplied index is not valid' }),content_type='application/json')
condition_slide_order(session)
sp = session.sessionpresentation_set.get(order=oldIndex)
if oldIndex < newIndex:
session.sessionpresentation_set.filter(order__gt=oldIndex, order__lte=newIndex).update(order=F('order')-1)
else:
session.sessionpresentation_set.filter(order__gte=newIndex, order__lt=oldIndex).update(order=F('order')+1)
sp.order = newIndex
sp.save()
return HttpResponse(json.dumps({'success':True}),content_type='application/json')
return HttpResponse(json.dumps({'success':True}), content_type='application/json')
@role_required('Secretariat')
def make_schedule_official(request, num, owner, name):

View file

@ -61,16 +61,15 @@
{% endblock %}
{# TODO don't rely on secr/js version of jquery-ui #}
{# Sorting based loosely on the original secr upload sorting and on http://www.avtex.com/blog/2015/01/27/drag-and-drop-sorting-of-table-rows-in-priority-order/ #}
{% block js %}
{% if can_manage_materials %}
<script type="text/javascript" src="{% static 'jquery/jquery.min.js' %}"></script>
<script type="text/javascript" src="{% static 'secr/js/jquery-ui-1.11.4.custom.min.js' %}"></script>
<script type="text/javascript" src="{% static 'jquery.cookie/jquery.cookie.js' %}"></script>
<script type="text/javascript" src={% static 'Sortable/Sortable.min.js' %}></script>
<script type="text/javascript">
$.ajaxSetup({
crossDomain: false,
beforeSend: function(xhr, settings) {
@ -80,33 +79,53 @@
}
});
$(document).ready(function() {
var rowWidthHelper = function (e, tr) {
var $originals = tr.children();
var $helper = tr.clone();
$helper.children().each(function(index)
{
$(this).width($originals.eq(index).width())
});
return $helper;
};
$(".slides tbody").sortable({
helper: rowWidthHelper,
stop: function(event,ui) {adjustDatabase(ui.item.parent())}
}).disableSelection();
});
var sortables=[];
var options = {
group: "slides",
animation: 150,
onAdd: function(event) {onAdd(event)},
onRemove: function(event) {onRemove(event)},
onEnd: function(event) {onEnd(event)}
};
function adjustDatabase(tbody) {
tbody.find('tr').each(function() {
count = $(this).parent().children().index($(this)) + 1;
old_order = $(this).attr("data-order");
if ( count != old_order ) {
$(this).attr("data-order", count);
$.post($(this).attr("data-url"),{'order':count});
}
function onAdd(event) {
var old_session = event.from.getAttribute("session");
var new_session = event.to.getAttribute("session");
$.post(event.to.getAttribute("addToSession"), {
'order': event.newIndex + 1,
'name': event.item.getAttribute("name")
});
$(event.item).find("td:eq(1)").find("a").each(function(){
$(this).attr("href", $(this).attr("href").replace(old_session,new_session) );
});
}
function onRemove(event) {
var old_session = event.from.getAttribute("session");
$.post(event.from.getAttribute("removeFromSession"),{
'oldIndex': event.oldIndex + 1,
'name': event.item.getAttribute("name")
});
}
function onEnd(event) {
if (event.to == event.from) {
$.post(event.from.getAttribute("reorderInSession"),{
'oldIndex': event.oldIndex + 1,
'newIndex': event.newIndex + 1
});
}
}
$(document).ready(function() {
$(".slides tbody").each(function() {
sortables.push(Sortable.create(this, options));
});
});
</script>
{% endif %}

View file

@ -66,9 +66,9 @@
<div class="panel-heading" data-toggle="tooltip" title="Drag and drop to reorder slides">Slides</div>
<div class="panel-body">
<table class="table table-condensed table-striped slides" id="slides_{{session.pk}}">
<tbody>
<tbody session="{{session.pk}}" addToSession="{% url 'ietf.meeting.views.ajax_add_slides_to_session' session_id=session.pk num=session.meeting.number %}" removeFromSession="{% url 'ietf.meeting.views.ajax_remove_slides_from_session' session_id=session.pk num=session.meeting.number %}" reorderInSession="{% url 'ietf.meeting.views.ajax_reorder_slides_in_session' session_id=session.pk num=session.meeting.number %}">
{% for pres in session.filtered_slides %}
<tr data-order="{{pres.order}}" data-url="{% url 'ietf.meeting.views.set_slide_order' session_id=session.pk num=session.meeting.number name=pres.document.name %}">
<tr name="{{pres.document.name}}">
{% url 'ietf.doc.views_doc.document_main' name=pres.document.name as url %}
<td>
<a href="{{pres.document.href}}">{{pres.document.title}} </a>