From 13ec46b145b3ac688ab112e8a4e5b2676de72a3b Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Tue, 5 Feb 2019 10:49:54 -0600 Subject: [PATCH] Add generic support for "enable/disable selection" of grid records --- tailbone/templates/master/index.mako | 57 ++++++++++++++++++-- tailbone/views/master.py | 81 +++++++++++++++++++++++----- 2 files changed, 120 insertions(+), 18 deletions(-) diff --git a/tailbone/templates/master/index.mako b/tailbone/templates/master/index.mako index 460aa9d0..85252961 100644 --- a/tailbone/templates/master/index.mako +++ b/tailbone/templates/master/index.mako @@ -57,6 +57,38 @@ % endif + % if master.supports_set_enabled_toggle and request.has_perm('{}.enable_disable_set'.format(permission_prefix)): + $('form[name="enable-set"] button').click(function() { + var form = $(this).parents('form'); + var uuids = $('.grid').gridcore('selected_uuids'); + if (! uuids.length) { + alert("You must first select one or more objects to enable."); + return false; + } + if (! confirm("Are you sure you wish to ENABLE the " + uuids.length + " selected objects?")) { + return false; + } + form.find('[name="uuids"]').val(uuids.toString()); + disable_button(this); + form.submit(); + }); + + $('form[name="disable-set"] button').click(function() { + var form = $(this).parents('form'); + var uuids = $('.grid').gridcore('selected_uuids'); + if (! uuids.length) { + alert("You must first select one or more objects to disable."); + return false; + } + if (! confirm("Are you sure you wish to DISABLE the " + uuids.length + " selected objects?")) { + return false; + } + form.find('[name="uuids"]').val(uuids.toString()); + disable_button(this); + form.submit(); + }); + % endif + % if master.set_deletable and request.has_perm('{}.delete_set'.format(permission_prefix)): $('form[name="delete-set"] button').click(function() { var form = $(this).parents('form'); @@ -65,7 +97,7 @@ alert("You must first select one or more objects to delete."); return false; } - if (! confirm("Are you sure you wish to delete the " + uuids.length + " selected objects?")) { + if (! confirm("Are you sure you wish to DELETE the " + uuids.length + " selected objects?")) { return false; } form.find('[name="uuids"]').val(uuids.toString()); @@ -104,11 +136,18 @@ ${h.end_form()} % endif - ## delete search results - % if master.bulk_deletable and request.has_perm('{}.bulk_delete'.format(permission_prefix)): - ${h.form(url('{}.bulk_delete'.format(route_prefix)), name='bulk-delete')} + ## enable / disable selected objects + % if master.supports_set_enabled_toggle and request.has_perm('{}.enable_disable_set'.format(permission_prefix)): + ${h.form(url('{}.enable_set'.format(route_prefix)), name='enable-set')} ${h.csrf_token(request)} - + ${h.hidden('uuids')} + + ${h.end_form()} + + ${h.form(url('{}.disable_set'.format(route_prefix)), name='disable-set')} + ${h.csrf_token(request)} + ${h.hidden('uuids')} + ${h.end_form()} % endif @@ -121,6 +160,14 @@ ${h.end_form()} % endif + ## delete search results + % if master.bulk_deletable and request.has_perm('{}.bulk_delete'.format(permission_prefix)): + ${h.form(url('{}.bulk_delete'.format(route_prefix)), name='bulk-delete')} + ${h.csrf_token(request)} + + ${h.end_form()} + % endif + ${grid.render_complete(tools=capture(self.grid_tools).strip(), context_menu=capture(self.context_menu_items).strip())|n} diff --git a/tailbone/views/master.py b/tailbone/views/master.py index d16a2e73..5eca8b63 100644 --- a/tailbone/views/master.py +++ b/tailbone/views/master.py @@ -83,6 +83,7 @@ class MasterView(View): deletable = True bulk_deletable = False set_deletable = False + supports_set_enabled_toggle = False populatable = False mergeable = False downloadable = False @@ -332,6 +333,8 @@ class MasterView(View): checkboxes = self.checkboxes if not checkboxes and self.mergeable and self.request.has_perm('{}.merge'.format(permission_prefix)): checkboxes = True + if not checkboxes and self.supports_set_enabled_toggle and self.request.has_perm('{}.enable_disable_set'.format(permission_prefix)): + checkboxes = True if not checkboxes and self.set_deletable and self.request.has_perm('{}.delete_set'.format(permission_prefix)): checkboxes = True @@ -1630,9 +1633,9 @@ class MasterView(View): progress.session['success_url'] = self.get_index_url() progress.session.save() - def delete_set(self): + def obtain_set(self): """ - View which can delete a specific set of records. + Obtain the effective "set" (selection) of records from POST data. """ # TODO: should have a cleaner way to parse object uuids? uuids = self.request.POST.get('uuids') @@ -1640,14 +1643,53 @@ class MasterView(View): uuids = uuids.split(',') # TODO: probably need to allow override of fetcher callable fetcher = lambda uuid: self.Session.query(self.model_class).get(uuid) - deleted = 0 + objects = [] for uuid in uuids: obj = fetcher(uuid) if obj: - self.delete_instance(obj) - deleted += 1 + objects.append(obj) + return objects + + def enable_set(self): + """ + View which can turn ON the 'enabled' flag for a specific set of records. + """ + objects = self.obtain_set() + if objects: + enabled = 0 + for obj in objects: + if not obj.enabled: + obj.enabled = True + enabled += 1 model_title_plural = self.get_model_title_plural() - self.request.session.flash("Deleted {} {}".format(deleted, model_title_plural)) + self.request.session.flash("Enabled {} {}".format(enabled, model_title_plural)) + return self.redirect(self.get_index_url()) + + def disable_set(self): + """ + View which can turn OFF the 'enabled' flag for a specific set of records. + """ + objects = self.obtain_set() + if objects: + disabled = 0 + for obj in objects: + if obj.enabled: + obj.enabled = False + disabled += 1 + model_title_plural = self.get_model_title_plural() + self.request.session.flash("Disabled {} {}".format(disabled, model_title_plural)) + return self.redirect(self.get_index_url()) + + def delete_set(self): + """ + View which can delete a specific set of records. + """ + objects = self.obtain_set() + if objects: + for obj in objects: + self.delete_instance(obj) + model_title_plural = self.get_model_title_plural() + self.request.session.flash("Deleted {} {}".format(len(objects), model_title_plural)) return self.redirect(self.get_index_url()) def oneoff_import(self, importer, host_object=None): @@ -3079,14 +3121,18 @@ class MasterView(View): config.add_view(cls, attr='populate', route_name='{}.populate'.format(route_prefix), permission='{}.create'.format(permission_prefix)) - # bulk delete - if cls.bulk_deletable: - config.add_route('{}.bulk_delete'.format(route_prefix), '{}/bulk-delete'.format(url_prefix), + # enable/disable set + if cls.supports_set_enabled_toggle: + config.add_tailbone_permission(permission_prefix, '{}.enable_disable_set'.format(permission_prefix), + "Enable / disable set (selection) of {}".format(model_title_plural)) + config.add_route('{}.enable_set'.format(route_prefix), '{}/enable-set'.format(url_prefix), request_method='POST') - config.add_view(cls, attr='bulk_delete', route_name='{}.bulk_delete'.format(route_prefix), - permission='{}.bulk_delete'.format(permission_prefix)) - config.add_tailbone_permission(permission_prefix, '{}.bulk_delete'.format(permission_prefix), - "Bulk delete {}".format(model_title_plural)) + config.add_view(cls, attr='enable_set', route_name='{}.enable_set'.format(route_prefix), + permission='{}.enable_disable_set'.format(permission_prefix)) + config.add_route('{}.disable_set'.format(route_prefix), '{}/disable-set'.format(url_prefix), + request_method='POST') + config.add_view(cls, attr='disable_set', route_name='{}.disable_set'.format(route_prefix), + permission='{}.enable_disable_set'.format(permission_prefix)) # delete set if cls.set_deletable: @@ -3097,6 +3143,15 @@ class MasterView(View): config.add_view(cls, attr='delete_set', route_name='{}.delete_set'.format(route_prefix), permission='{}.delete_set'.format(permission_prefix)) + # bulk delete + if cls.bulk_deletable: + config.add_route('{}.bulk_delete'.format(route_prefix), '{}/bulk-delete'.format(url_prefix), + request_method='POST') + config.add_view(cls, attr='bulk_delete', route_name='{}.bulk_delete'.format(route_prefix), + permission='{}.bulk_delete'.format(permission_prefix)) + config.add_tailbone_permission(permission_prefix, '{}.bulk_delete'.format(permission_prefix), + "Bulk delete {}".format(model_title_plural)) + # merge if cls.mergeable: config.add_route('{}.merge'.format(route_prefix), '{}/merge'.format(url_prefix))