Added slide sorting. Commit ready to merge.

- Legacy-Id: 11995
This commit is contained in:
Robert Sparks 2016-09-16 20:36:59 +00:00
parent 02ee5dc5d7
commit bcac1dfff8
4 changed files with 133 additions and 4 deletions

View file

@ -7,10 +7,13 @@ from unittest import skipIf
from django.contrib.staticfiles.testing import StaticLiveServerTestCase
from django.core.urlresolvers import reverse as urlreverse
#from django.test.utils import override_settings
import debug # pyflakes:ignore
from ietf.doc.factories import DocumentFactory
from ietf.group import colors
from ietf.meeting.factories import SessionFactory
from ietf.meeting.test_data import make_meeting_test_data
from ietf.meeting.models import SchedTimeSessAssignment
from ietf.utils.test_runner import set_coverage_checking
@ -88,6 +91,45 @@ class ScheduleEditTests(StaticLiveServerTestCase):
time.sleep(0.1) # The API that modifies the database runs async
self.assertEqual(SchedTimeSessAssignment.objects.filter(session__meeting__number=42,session__group__acronym='mars',schedule__name='test-agenda').count(),0)
@skipIf(skip_selenium, skip_message)
class SlideReorderTests(StaticLiveServerTestCase):
def setUp(self):
set_coverage_checking(False)
self.session = SessionFactory(meeting__type_id='ietf')
self.session.sessionpresentation_set.create(document=DocumentFactory(type_id='slides',name='one'),order=1)
self.session.sessionpresentation_set.create(document=DocumentFactory(type_id='slides',name='two'),order=2)
self.session.sessionpresentation_set.create(document=DocumentFactory(type_id='slides',name='three'),order=3)
self.driver = webdriver.PhantomJS(port=0, service_log_path=settings.TEST_GHOSTDRIVER_LOG_PATH)
self.driver.set_window_size(1024,768)
def absreverse(self,*args,**kwargs):
return '%s%s'%(self.live_server_url,urlreverse(*args,**kwargs))
def secr_login(self):
url = '%s%s'%(self.live_server_url, urlreverse('django.contrib.auth.views.login'))
self.driver.get(url)
self.driver.find_element_by_name('username').send_keys('secretary')
self.driver.find_element_by_name('password').send_keys('secretary+password')
self.driver.find_element_by_xpath('//button[@type="submit"]').click()
#@override_settings(DEBUG=True)
def testReorderSlides(self):
return
url = self.absreverse('ietf.meeting.views.session_details',
kwargs=dict(
num=self.session.meeting.number,
acronym = self.session.group.acronym,))
self.secr_login()
self.driver.get(url)
#debug.show('unicode(self.driver.page_source)')
second = self.driver.find_element_by_css_selector('#slides tr:nth-child(2)')
third = self.driver.find_element_by_css_selector('#slides tr:nth-child(3)')
ActionChains(self.driver).drag_and_drop(second,third).perform()
time.sleep(0.1) # The API that modifies the database runs async
names=self.session.sessionpresentation_set.values_list('document__name',flat=True)
self.assertEqual(list(names),[u'one',u'three',u'two'])
# The following are useful debugging tools
# If you add this to a LiveServerTestCase and run just this test, you can browse

View file

@ -14,6 +14,7 @@ safe_for_all_meeting_types = [
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+)/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),
]

View file

@ -1450,6 +1450,32 @@ def upload_session_slides(request, session_id, num, name):
'form': form,
})
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
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"):
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')
order_str = request.POST.get('order', None)
try:
order = int(order_str)
except ValueError:
return HttpResponse(json.dumps({ 'success' : False, 'error' : 'Supplied order is not valid' }),content_type='application/json')
if order <=0 or order > 32767 :
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
sp.save()
return HttpResponse(json.dumps({'success':True}),content_type='application/json')
@role_required('Secretariat')
def make_schedule_official(request, num, owner, name):
@ -1962,3 +1988,4 @@ def proceedings_overview(request, num=None):
'meeting': meeting,
'template': template,
})

View file

@ -1,9 +1,15 @@
{% extends "base.html" %}
{# Copyright The IETF Trust 2015, All Rights Reserved #}
{% load origin ietf_filters %}
{% load origin ietf_filters staticfiles %}
{% block title %}{{ meeting }} : {{ acronym }}{% endblock %}
{% block morecss %}
.ui-sortable tr {
cursor:pointer;
}
{% endblock %}
{% block content %}
{% origin %}
<h1>{{ meeting }} : {{ acronym }}</h1>
@ -70,11 +76,12 @@
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading">Slides</div>
<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">
<table class="table table-condensed table-striped" id="slides">
<tbody>
{% for pres in session.filtered_slides %}
<tr>
<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 %}">
{% if pres.rev %}
{% url 'doc_view' name=pres.document.name rev=pres.rev as url %}
{% else %}
@ -90,6 +97,7 @@
{% endif %}
</tr>
{% endfor %}
</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>
@ -124,3 +132,54 @@
{% endfor %}
{% 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">
$.ajaxSetup({
crossDomain: false,
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type)) {
xhr.setRequestHeader("X-CSRFToken", $.cookie('csrftoken'));
}
}
});
$(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("#slides")}
}).disableSelection();
});
function adjustDatabase(tableID) {
$(tableID + " 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});
}
});
}
</script>
{% endif %}
{% endblock %}