From f3e6e961360c850f2357de93d15324c446a4a1f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Laur=C3=A9line=20Gu=C3=A9rin?= Date: Tue, 24 May 2022 22:14:41 +0200 Subject: [PATCH] manager: open the correct tab after redirect (#65653) --- chrono/manager/static/js/chrono.manager.js | 8 +++ chrono/manager/views.py | 62 ++++++++++++++++--- tests/manager/test_all.py | 2 +- tests/manager/test_meetings_agenda_options.py | 2 +- tests/manager/test_resource.py | 4 +- tests/manager/test_timeperiod.py | 2 +- tests/manager/test_unavailability_calendar.py | 6 +- 7 files changed, 69 insertions(+), 17 deletions(-) diff --git a/chrono/manager/static/js/chrono.manager.js b/chrono/manager/static/js/chrono.manager.js index 97d87a18..717e757c 100644 --- a/chrono/manager/static/js/chrono.manager.js +++ b/chrono/manager/static/js/chrono.manager.js @@ -48,4 +48,12 @@ $(function() { total_form.val(form_num + 1); }) } + + /* focus tab from #open: anchor, to point to open panel */ + if (document.location.hash && document.location.hash.indexOf('#open:') == 0) { + const $tab_button = $('#tab-' + document.location.hash.substring(6) + '[role=tab]'); + if ($tab_button.length) { + $('.pk-tabs')[0].tabs.selectTab($tab_button[0]); + } + } }); diff --git a/chrono/manager/views.py b/chrono/manager/views.py index adaea892..282abd49 100644 --- a/chrono/manager/views.py +++ b/chrono/manager/views.py @@ -1154,6 +1154,8 @@ class ViewableAgendaMixin: class ManagedAgendaMixin(ViewableAgendaMixin): + tab_anchor = None + def check_permissions(self, user): return self.agenda.can_be_managed(user) @@ -1165,7 +1167,10 @@ class ManagedAgendaMixin(ViewableAgendaMixin): return kwargs def get_success_url(self): - return reverse('chrono-manager-agenda-settings', kwargs={'pk': self.agenda.id}) + url = reverse('chrono-manager-agenda-settings', kwargs={'pk': self.agenda.id}) + if self.tab_anchor: + url += '#open:%s' % self.tab_anchor + return url class AgendaEditView(ManagedAgendaMixin, UpdateView): @@ -1186,6 +1191,7 @@ agenda_edit = AgendaEditView.as_view() class AgendaBookingDelaysView(AgendaEditView): form_class = AgendaBookingDelaysForm title = _('Configure booking delays') + tab_anchor = 'delays' agenda_booking_delays = AgendaBookingDelaysView.as_view() @@ -1194,6 +1200,7 @@ agenda_booking_delays = AgendaBookingDelaysView.as_view() class AgendaRolesView(AgendaEditView): form_class = AgendaRolesForm title = _('Configure roles') + tab_anchor = 'permissions' agenda_roles = AgendaRolesView.as_view() @@ -1202,6 +1209,7 @@ agenda_roles = AgendaRolesView.as_view() class AgendaDisplaySettingsView(AgendaEditView): form_class = AgendaDisplaySettingsForm title = _("Configure display options") + tab_anchor = 'display-options' def set_agenda(self, **kwargs): self.agenda = get_object_or_404(Agenda.objects.exclude(kind='virtual'), pk=kwargs.get('pk')) @@ -1216,6 +1224,7 @@ agenda_display_settings = AgendaDisplaySettingsView.as_view() class AgendaBookingCheckSettingsView(AgendaEditView): form_class = AgendaBookingCheckSettingsForm title = _("Configure booking check options") + tab_anchor = 'booking-check-options' def set_agenda(self, **kwargs): self.agenda = get_object_or_404(Agenda, pk=kwargs.get('pk'), kind='events') @@ -1748,6 +1757,7 @@ agenda_open_events_view = AgendaOpenEventsView.as_view() class ManagedAgendaSubobjectMixin: agenda = None + tab_anchor = None def dispatch(self, request, *args, **kwargs): self.agenda = self.get_object().agenda @@ -1761,11 +1771,15 @@ class ManagedAgendaSubobjectMixin: return context def get_success_url(self): - return reverse('chrono-manager-agenda-settings', kwargs={'pk': self.agenda.id}) + url = reverse('chrono-manager-agenda-settings', kwargs={'pk': self.agenda.id}) + if self.tab_anchor: + url += '#open:%s' % self.tab_anchor + return url class ManagedDeskMixin: desk = None + tab_anchor = None def dispatch(self, request, *args, **kwargs): try: @@ -1790,11 +1804,15 @@ class ManagedDeskMixin: return context def get_success_url(self): - return reverse('chrono-manager-agenda-settings', kwargs={'pk': self.desk.agenda.id}) + url = reverse('chrono-manager-agenda-settings', kwargs={'pk': self.desk.agenda.id}) + if self.tab_anchor: + url += '#open:%s' % self.tab_anchor + return url class ManagedTimePeriodMixin: agenda = None + tab_anchor = None def dispatch(self, request, *args, **kwargs): self.time_period = self.get_object() @@ -1812,13 +1830,16 @@ class ManagedTimePeriodMixin: return context def get_success_url(self): - return reverse('chrono-manager-agenda-settings', kwargs={'pk': self.agenda.id}) + url = reverse('chrono-manager-agenda-settings', kwargs={'pk': self.agenda.id}) + if self.tab_anchor: + url += '#open:%s' % self.tab_anchor + return url class ManagedTimePeriodExceptionMixin: - desk = None unavailability_calendar = None + tab_anchor = None def dispatch(self, request, *args, **kwargs): object_ = self.get_object() @@ -1845,12 +1866,15 @@ class ManagedTimePeriodExceptionMixin: def get_success_url(self): if self.desk: - return reverse('chrono-manager-agenda-settings', kwargs={'pk': self.desk.agenda.id}) + url = reverse('chrono-manager-agenda-settings', kwargs={'pk': self.desk.agenda.id}) elif self.unavailability_calendar: - return reverse( + url = reverse( 'chrono-manager-unavailability-calendar-settings', kwargs={'pk': self.unavailability_calendar.pk}, ) + if self.tab_anchor: + url += '#open:%s' % self.tab_anchor + return url class AgendaSettingsRedirectView(RedirectView): @@ -2092,6 +2116,7 @@ class AgendaNotificationsSettingsView(ManagedAgendaMixin, UpdateView): template_name = 'chrono/manager_agenda_notifications_form.html' model = AgendaNotificationsSettings form_class = AgendaNotificationsForm + tab_anchor = 'notifications' def get_object(self): try: @@ -2114,6 +2139,7 @@ class AgendaReminderSettingsView(AgendaEditView): title = _('Reminder Settings') model = AgendaReminderSettings form_class = AgendaReminderForm + tab_anchor = 'reminders' def get_object(self): try: @@ -2548,6 +2574,7 @@ event_checked = EventCheckedView.as_view() class AgendaAddResourceView(ManagedAgendaMixin, FormView): template_name = 'chrono/manager_agenda_resource_form.html' form_class = AgendaResourceForm + tab_anchor = 'resources' def set_agenda(self, **kwargs): self.agenda = get_object_or_404(Agenda, id=kwargs.get('pk'), kind='meetings') @@ -2569,6 +2596,7 @@ class AgendaResourceDeleteView(ManagedAgendaMixin, DeleteView): template_name = 'chrono/manager_confirm_delete.html' model = Resource pk_url_kwarg = 'resource_pk' + tab_anchor = 'resources' def set_agenda(self, **kwargs): self.agenda = get_object_or_404(Agenda, id=kwargs.get('pk'), kind='meetings') @@ -2630,7 +2658,8 @@ class UnavailabilityCalendarToggleView(ManagedDeskMixin, DetailView): break return HttpResponseRedirect( - reverse('chrono-manager-agenda-settings', kwargs={'pk': self.desk.agenda_id}) + '%s#open:time-periods' + % reverse('chrono-manager-agenda-settings', kwargs={'pk': self.desk.agenda_id}) ) @@ -2731,6 +2760,7 @@ def process_time_period_add_form(form, desk=None, agenda=None): class AgendaAddTimePeriodView(ManagedDeskMixin, FormView): template_name = 'chrono/manager_time_period_form.html' form_class = TimePeriodAddForm + tab_anchor = 'time-periods' def get_form_kwargs(self): return super(FormView, self).get_form_kwargs() @@ -2750,6 +2780,7 @@ agenda_add_time_period = AgendaAddTimePeriodView.as_view() class VirtualAgendaAddTimePeriodView(ManagedAgendaMixin, FormView): template_name = 'chrono/manager_time_period_form.html' form_class = TimePeriodAddForm + tab_anchor = 'time-periods' def get_form_kwargs(self): return super(FormView, self).get_form_kwargs() @@ -2766,6 +2797,7 @@ class TimePeriodEditView(ManagedTimePeriodMixin, UpdateView): template_name = 'chrono/manager_time_period_form.html' model = TimePeriod form_class = TimePeriodForm + tab_anchor = 'time-periods' time_period_edit = TimePeriodEditView.as_view() @@ -2774,6 +2806,7 @@ time_period_edit = TimePeriodEditView.as_view() class TimePeriodDeleteView(ManagedTimePeriodMixin, DeleteView): template_name = 'chrono/manager_confirm_delete.html' model = TimePeriod + tab_anchor = 'time-periods' def delete(self, request, *args, **kwargs): time_period = self.get_object() @@ -2803,6 +2836,7 @@ class AgendaAddDesk(ManagedAgendaMixin, CreateView): model = Desk form_class = NewDeskForm agenda = None + tab_anchor = 'desks' def set_agenda(self, **kwargs): self.agenda = get_object_or_404(Agenda, pk=kwargs.get('pk'), kind='meetings') @@ -2815,6 +2849,7 @@ class DeskEditView(ManagedAgendaSubobjectMixin, UpdateView): template_name = 'chrono/manager_desk_form.html' model = Desk form_class = DeskForm + tab_anchor = 'desks' def get_queryset(self): return super().get_queryset().filter(agenda__kind='meetings') @@ -2826,6 +2861,7 @@ desk_edit = DeskEditView.as_view() class DeskDeleteView(ManagedAgendaSubobjectMixin, DeleteView): template_name = 'chrono/manager_confirm_delete.html' model = Desk + tab_anchor = 'desks' def dispatch(self, request, *args, **kwargs): agenda = self.get_object().agenda @@ -2895,6 +2931,7 @@ class AgendaAddTimePeriodExceptionView(ManagedDeskMixin, CreateView): template_name = 'chrono/manager_time_period_exception_form.html' model = TimePeriodException form_class = NewTimePeriodExceptionForm + tab_anchor = 'time-periods' def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) @@ -2925,6 +2962,7 @@ class TimePeriodExceptionEditView(ManagedTimePeriodExceptionMixin, UpdateView): template_name = 'chrono/manager_time_period_exception_form.html' model = TimePeriodException form_class = TimePeriodExceptionForm + tab_anchor = 'time-periods' def form_valid(self, form): result = super().form_valid(form) @@ -2972,6 +3010,7 @@ time_period_exception_extract_list = TimePeriodExceptionExtractListView.as_view( class TimePeriodExceptionDeleteView(ManagedTimePeriodExceptionMixin, DeleteView): template_name = 'chrono/manager_confirm_exception_delete.html' model = TimePeriodException + tab_anchor = 'time-periods' def get_success_url(self): if self.desk: @@ -3018,6 +3057,7 @@ class DeskImportTimePeriodExceptionsView(ManagedAgendaSubobjectMixin, UpdateView model = Desk form_class = DeskExceptionsImportForm template_name = 'chrono/manager_import_exceptions.html' + tab_anchor = 'time-periods' def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) @@ -3079,6 +3119,7 @@ desk_import_time_period_exceptions = DeskImportTimePeriodExceptionsView.as_view( class TimePeriodExceptionSourceDeleteView(ManagedTimePeriodExceptionMixin, DeleteView): template_name = 'chrono/manager_confirm_source_delete.html' model = TimePeriodExceptionSource + tab_anchor = 'time-periods' def delete(self, request, *args, **kwargs): source = self.get_object() @@ -3106,6 +3147,7 @@ class TimePeriodExceptionSourceReplaceView(ManagedTimePeriodExceptionMixin, Upda model = TimePeriodExceptionSource form_class = TimePeriodExceptionSourceReplaceForm template_name = 'chrono/manager_replace_exceptions.html' + tab_anchor = 'time-periods' def get_queryset(self): queryset = super().get_queryset() @@ -3136,6 +3178,7 @@ time_period_exception_source_replace = TimePeriodExceptionSourceReplaceView.as_v class TimePeriodExceptionSourceRefreshView(ManagedTimePeriodExceptionMixin, DetailView): model = TimePeriodExceptionSource + tab_anchor = 'time-periods' def get_queryset(self): queryset = super().get_queryset() @@ -3404,7 +3447,8 @@ class TimePeriodExceptionSourceToggleView(ManagedTimePeriodExceptionMixin, Detai messages.info(self.request, message % {'source': source, 'desk': source.desk}) return HttpResponseRedirect( - reverse('chrono-manager-agenda-settings', kwargs={'pk': source.desk.agenda_id}) + '%s#open:time-periods' + % reverse('chrono-manager-agenda-settings', kwargs={'pk': source.desk.agenda_id}) ) diff --git a/tests/manager/test_all.py b/tests/manager/test_all.py index 5d1ee271..4cc5e8f5 100644 --- a/tests/manager/test_all.py +++ b/tests/manager/test_all.py @@ -2248,7 +2248,7 @@ def test_virtual_agenda_settings_delete_excluded_period(app, admin_user): url = '/manage/timeperiods/%s/delete' % tp.pk resp = resp.click(href=url) resp = resp.form.submit() - assert resp.location.endswith('/manage/agendas/%s/settings' % agenda.id) + assert resp.location.endswith('/manage/agendas/%s/settings#open:time-periods' % agenda.id) assert TimePeriod.objects.count() == 0 diff --git a/tests/manager/test_meetings_agenda_options.py b/tests/manager/test_meetings_agenda_options.py index 4d654385..1d1245ed 100644 --- a/tests/manager/test_meetings_agenda_options.py +++ b/tests/manager/test_meetings_agenda_options.py @@ -378,7 +378,7 @@ def test_meetings_agenda_delete_desk(app, admin_user): resp = resp.click('Desk A') resp = resp.click('Delete') resp = resp.form.submit() - assert resp.location.endswith('/manage/agendas/%s/settings' % agenda.pk) + assert resp.location.endswith('/manage/agendas/%s/settings#open:desks' % agenda.pk) assert Desk.objects.count() == 1 # only one desk diff --git a/tests/manager/test_resource.py b/tests/manager/test_resource.py index 26a78165..feb02f5c 100644 --- a/tests/manager/test_resource.py +++ b/tests/manager/test_resource.py @@ -572,7 +572,7 @@ def test_meetings_agenda_resources(app, admin_user): assert list(resp.context['form'].fields['resource'].queryset) == [resource] resp.form['resource'] = resource.pk resp = resp.form.submit() - assert resp.location.endswith('/manage/agendas/%s/settings' % agenda.pk) + assert resp.location.endswith('/manage/agendas/%s/settings#open:resources' % agenda.pk) assert list(agenda.resources.all()) == [resource] resp = resp.follow() assert '/manage/resource/%s/' % resource.pk in resp.text @@ -582,7 +582,7 @@ def test_meetings_agenda_resources(app, admin_user): resp = app.get('/manage/agendas/%s/resource/%s/delete/' % (agenda.pk, resource.pk)) resp = resp.form.submit() - assert resp.location.endswith('/manage/agendas/%s/settings' % agenda.pk) + assert resp.location.endswith('/manage/agendas/%s/settings#open:resources' % agenda.pk) assert list(agenda.resources.all()) == [] resp = resp.follow() assert '/manage/resource/%s/' % resource.pk not in resp.text diff --git a/tests/manager/test_timeperiod.py b/tests/manager/test_timeperiod.py index 6d1c303e..71853f03 100644 --- a/tests/manager/test_timeperiod.py +++ b/tests/manager/test_timeperiod.py @@ -211,7 +211,7 @@ def test_meetings_agenda_delete_time_period(app, admin_user): resp = resp.click('Wednesday', index=0) resp = resp.click('Delete') resp = resp.form.submit() - assert resp.location.endswith('/manage/agendas/%s/settings' % agenda.id) + assert resp.location.endswith('/manage/agendas/%s/settings#open:time-periods' % agenda.id) assert TimePeriod.objects.count() == 1 diff --git a/tests/manager/test_unavailability_calendar.py b/tests/manager/test_unavailability_calendar.py index 314e0977..567f9fcc 100644 --- a/tests/manager/test_unavailability_calendar.py +++ b/tests/manager/test_unavailability_calendar.py @@ -216,7 +216,7 @@ def test_unavailability_calendar_in_desk(app, admin_user): resp = resp.click( href='/manage/desk/%s/unavailability-calendar/%s/toggle/' % (desk.pk, unavailability_calendar.pk) ) - settings_url = '/manage/agendas/%s/settings' % agenda.pk + settings_url = '/manage/agendas/%s/settings#open:time-periods' % agenda.pk assert resp.location.endswith(settings_url) # exception from calendar displayed on the settings page resp = app.get(settings_url) @@ -233,7 +233,7 @@ def test_unavailability_calendar_in_desk(app, admin_user): resp = resp.click( href='/manage/desk/%s/unavailability-calendar/%s/toggle/' % (desk.pk, unavailability_calendar.pk) ) - assert resp.location.endswith('/manage/agendas/%s/settings' % agenda.pk) + assert resp.location.endswith('/manage/agendas/%s/settings#open:time-periods' % agenda.pk) resp = app.get(exceptions_url) assert 'calendar' in resp.text assert 'enable' in resp.text @@ -255,7 +255,7 @@ def test_unavailability_calendar_in_desk(app, admin_user): resp = resp.click( href='/manage/desk/%s/unavailability-calendar/%s/toggle/' % (desk.pk, unavailability_calendar.pk) ) - assert resp.location.endswith('/manage/agendas/%s/settings' % agenda.pk) + assert resp.location.endswith('/manage/agendas/%s/settings#open:time-periods' % agenda.pk) resp = resp.follow() assert 'One or several bookings overlap with exceptions.' in resp.text