diff --git a/ietf/meeting/tests_views.py b/ietf/meeting/tests_views.py index b7f68da504b..00e8902eb7e 100644 --- a/ietf/meeting/tests_views.py +++ b/ietf/meeting/tests_views.py @@ -3898,6 +3898,69 @@ def test_slot_to_the_right(self): self.assertTrue(mars_slot.slot_to_the_right) self.assertTrue(mars_scheduled.slot_to_the_right) + def test_updateview(self): + """The updateview action should set visible timeslot types in the session""" + meeting = MeetingFactory(type_id='ietf') + url = urlreverse('ietf.meeting.views.edit_meeting_schedule', kwargs={'num': meeting.number}) + types_to_enable = ['regular', 'reg', 'other'] + r = self.client.post( + url, + { + 'action': 'updateview', + 'enabled_timeslot_types[]': types_to_enable, + }, + ) + self.assertEqual(r.status_code, 200) + session_data = self.client.session + self.assertIn('edit_meeting_schedule', session_data) + self.assertCountEqual( + session_data['edit_meeting_schedule']['enabled_timeslot_types'], + types_to_enable, + 'Should set types requested', + ) + + r = self.client.post( + url, + { + 'action': 'updateview', + 'enabled_timeslot_types[]': types_to_enable + ['faketype'], + }, + ) + self.assertEqual(r.status_code, 200) + session_data = self.client.session + self.assertIn('edit_meeting_schedule', session_data) + self.assertCountEqual( + session_data['edit_meeting_schedule']['enabled_timeslot_types'], + types_to_enable, + 'Should ignore unknown types', + ) + + def test_persistent_enabled_timeslot_types(self): + meeting = MeetingFactory(type_id='ietf') + TimeSlotFactory(meeting=meeting, type_id='other') + TimeSlotFactory(meeting=meeting, type_id='reg') + + # test default behavior (only 'regular' enabled) + r = self.client.get(urlreverse('ietf.meeting.views.edit_meeting_schedule', kwargs={'num': meeting.number})) + self.assertEqual(r.status_code, 200) + q = PyQuery(r.content) + self.assertEqual(len(q('#timeslot-type-toggles-modal input[value="regular"][checked]')), 1) + self.assertEqual(len(q('#timeslot-type-toggles-modal input[value="other"]:not([checked])')), 1) + self.assertEqual(len(q('#timeslot-type-toggles-modal input[value="reg"]:not([checked])')), 1) + + # test with 'regular' and 'other' enabled via session store + client_session = self.client.session # must store as var, new session is created on access + client_session['edit_meeting_schedule'] = { + 'enabled_timeslot_types': ['regular', 'other'] + } + client_session.save() + r = self.client.get(urlreverse('ietf.meeting.views.edit_meeting_schedule', kwargs={'num': meeting.number})) + self.assertEqual(r.status_code, 200) + q = PyQuery(r.content) + self.assertEqual(len(q('#timeslot-type-toggles-modal input[value="regular"][checked]')), 1) + self.assertEqual(len(q('#timeslot-type-toggles-modal input[value="other"][checked]')), 1) + self.assertEqual(len(q('#timeslot-type-toggles-modal input[value="reg"]:not([checked])')), 1) + class SessionDetailsTests(TestCase): diff --git a/ietf/meeting/views.py b/ietf/meeting/views.py index 8484fbc5cdc..35145d359e7 100644 --- a/ietf/meeting/views.py +++ b/ietf/meeting/views.py @@ -728,10 +728,18 @@ def _json_response(success, status=None, **extra_data): return JsonResponse(data, status=status) if request.method == 'POST': - if not can_edit: - permission_denied(request, "Can't edit this schedule.") - action = request.POST.get('action') + if action == 'updateview': + # allow updateview action even if can_edit is false, it affects only the user's session + sess_data = request.session.setdefault('edit_meeting_schedule', {}) + enabled_types = [ts_type.slug for ts_type in TimeSlotTypeName.objects.filter( + used=True, + slug__in=request.POST.getlist('enabled_timeslot_types[]', []) + )] + sess_data['enabled_timeslot_types'] = enabled_types + return _json_response(True) + elif not can_edit: + permission_denied(request, "Can't edit this schedule.") # Handle ajax requests. Most of these return JSON responses with at least a 'success' key. # For the swapdays and swaptimeslots actions, the response is either a redirect to the @@ -949,6 +957,16 @@ def cubehelix(i, total, hue=1.2, start_angle=0.5): key=lambda tstype: tstype.name, ) + # extract view configuration from session store + session_data = request.session.get('edit_meeting_schedule', None) + if session_data is None: + enabled_timeslot_types = ['regular'] + else: + enabled_timeslot_types = [ + ts_type.slug for ts_type in timeslot_types + if ts_type.slug in session_data.get('enabled_timeslot_types', []) + ] + return render(request, "meeting/edit_meeting_schedule.html", { 'meeting': meeting, 'schedule': schedule, @@ -963,6 +981,7 @@ def cubehelix(i, total, hue=1.2, start_angle=0.5): 'timeslot_types': timeslot_types, 'hide_menu': True, 'lock_time': lock_time, + 'enabled_timeslot_types': enabled_timeslot_types, }) diff --git a/ietf/static/js/edit-meeting-schedule.js b/ietf/static/js/edit-meeting-schedule.js index 0adc0133099..925c3252e32 100644 --- a/ietf/static/js/edit-meeting-schedule.js +++ b/ietf/static/js/edit-meeting-schedule.js @@ -19,6 +19,17 @@ $(function () { alert("Error: " + errorText); } + function ajaxCall(action, data) { + const ajaxData = { action: action }; + Object.assign(ajaxData, data); + return jQuery.ajax({ + url: window.location.href, + method: "post", + timeout: 5 * 1000, + data: ajaxData, + }); + } + /** * Time to treat as current time for computing whether to lock timeslots * @returns {*} Moment object equal to lockSeconds in the future @@ -437,26 +448,20 @@ $(function () { } if (dropParent.hasClass("unassigned-sessions")) { - jQuery.ajax({ - url: window.location.href, - method: "post", - timeout: 5 * 1000, - data: { - action: "unassign", - session: sessionElement.id.slice("session".length) - } - }).fail(failHandler).done(done); + ajaxCall( + "unassign", + { session: sessionElement.id.slice('session'.length) } + ).fail(failHandler) + .done(done); } else { - jQuery.ajax({ - url: window.location.href, - method: "post", - data: { - action: "assign", + ajaxCall( + "assign", + { session: sessionElement.id.slice("session".length), timeslot: dropParent.attr("id").slice("timeslot".length) - }, - timeout: 5 * 1000 - }).fail(failHandler).done(done); + } + ).fail(failHandler) + .done(done); } }); @@ -759,16 +764,20 @@ $(function () { // Toggling timeslot types function updateTimeSlotTypeToggling() { - let checked = []; - timeSlotTypeInputs.filter(":checked").each(function () { - checked.push("[data-type=" + this.value + "]"); - }); + const checkedTypes = jQuery.map(timeSlotTypeInputs.filter(":checked"), elt => elt.value); + const checkedSelectors = checkedTypes.map(t => '[data-type="' + t + '"]').join(","); - sessions.filter(checked.join(",")).removeClass('hidden-timeslot-type'); - sessions.not(checked.join(",")).addClass('hidden-timeslot-type'); - timeslots.filter(checked.join(",")).removeClass('hidden-timeslot-type'); - timeslots.not(checked.join(",")).addClass('hidden-timeslot-type'); + sessions.filter(checkedSelectors).removeClass('hidden-timeslot-type'); + sessions.not(checkedSelectors).addClass('hidden-timeslot-type'); + timeslots.filter(checkedSelectors).removeClass('hidden-timeslot-type'); + timeslots.not(checkedSelectors).addClass('hidden-timeslot-type'); updateGridVisibility(); + return checkedTypes; + } + + function updateTimeSlotTypeTogglingAndSave() { + const checkedTypes = updateTimeSlotTypeToggling(); + ajaxCall('updateview', {enabled_timeslot_types: checkedTypes}); } // Toggling session purposes @@ -783,7 +792,7 @@ $(function () { } if (timeSlotTypeInputs.length > 0) { - timeSlotTypeInputs.on("change", updateTimeSlotTypeToggling); + timeSlotTypeInputs.on("change", updateTimeSlotTypeTogglingAndSave); updateTimeSlotTypeToggling(); schedEditor.find('#timeslot-type-toggles-modal .timeslot-type-toggles .select-all') .get(0) @@ -791,7 +800,7 @@ $(function () { 'click', function() { timeSlotTypeInputs.prop('checked', true); - updateTimeSlotTypeToggling(); + updateTimeSlotTypeTogglingAndSave(); }); schedEditor.find('#timeslot-type-toggles-modal .timeslot-type-toggles .clear-all') .get(0) @@ -799,7 +808,7 @@ $(function () { 'click', function() { timeSlotTypeInputs.prop('checked', false); - updateTimeSlotTypeToggling(); + updateTimeSlotTypeTogglingAndSave(); }); } diff --git a/ietf/templates/meeting/edit_meeting_schedule.html b/ietf/templates/meeting/edit_meeting_schedule.html index a2c2dbb1282..93bc5c51bca 100644 --- a/ietf/templates/meeting/edit_meeting_schedule.html +++ b/ietf/templates/meeting/edit_meeting_schedule.html @@ -312,7 +312,7 @@