diff --git a/djadmin2/actions.py b/djadmin2/actions.py new file mode 100644 index 0000000..5937e3a --- /dev/null +++ b/djadmin2/actions.py @@ -0,0 +1,60 @@ +from django.core.exceptions import PermissionDenied +from django.template.response import TemplateResponse +from django.utils.text import capfirst + +from django.contrib import messages + +def get_description(action): + if hasattr(action, 'description'): + return action.description + else: + return capfirst(action.__name__.replace('_', ' ')) + +def delete_selected(request, queryset): + # We check whether the user has permission to delete the objects in the + # queryset. + # + # TODO: This duplicates some of the permission-checking functionality in + # BaseAdmin2. Investigate how to DRY this out. + # + # TODO: Check that user has permission to delete all related obejcts. See + # `get_deleted_objects` in contrib.admin.util for how this is currently + # done. (Hint: I think we can do better.) + + model = queryset.model + opts = model._meta + permission_name = '%s.delete.%s' \ + % (opts.app_label, opts.object_name.lower()) + has_permission = request.user.has_perm(permission_name) + + if len(queryset) == 1: + objects_name = opts.verbose_name + else: + objects_name = opts.verbose_name_plural + + if request.POST.get('confirmed'): + # The user has confirmed that they want to delete the objects. + if has_permission: + queryset.delete() + message = "Successfully deleted %d %s" % \ + (len(queryset), objects_name) + messages.add_message(request, messages.INFO, message) + return None + else: + raise PermissionDenied + else: + # The user has not confirmed that they want to delete the objects, so + # render a template asking for their confirmation. + if has_permission: + template = 'admin2/bootstrap/delete_selected_confirmation.html' + context = { + 'queryset': queryset, + 'objects_name': objects_name + } + return TemplateResponse(request, template, context) + else: + message = "Permission to delete %s denied" % objects_name + messages.add_message(request, messages.INFO, message) + return None + +delete_selected.description = "Delete selected items" diff --git a/djadmin2/constants.py b/djadmin2/constants.py index b6c1973..11021b0 100644 --- a/djadmin2/constants.py +++ b/djadmin2/constants.py @@ -3,7 +3,7 @@ from django.conf import settings MODEL_ADMIN_ATTRS = ( 'list_display', 'list_display_links', 'list_filter', 'admin', 'has_permission', 'has_add_permission', - 'has_edit_permission', 'has_delete_permission', + 'has_edit_permission', 'has_delete_permission', 'get_actions' ) ADMIN2_THEME_DIRECTORY = getattr(settings, "ADMIN2_THEME_DIRECTORY", "admin2/bootstrap") diff --git a/djadmin2/models.py b/djadmin2/models.py index bd8cc84..e787d8c 100644 --- a/djadmin2/models.py +++ b/djadmin2/models.py @@ -16,6 +16,7 @@ import extra_views from djadmin2 import apiviews from djadmin2 import constants from djadmin2 import views +from djadmin2 import actions class BaseAdmin2(object): @@ -125,6 +126,9 @@ class ModelAdmin2(BaseAdmin2): api_list_view = apiviews.ListCreateAPIView api_detail_view = apiviews.RetrieveUpdateDestroyAPIView + # Actions + actions = [actions.delete_selected] + def __init__(self, model, admin, **kwargs): self.model = model self.admin = admin @@ -244,6 +248,19 @@ class ModelAdmin2(BaseAdmin2): def api_urls(self): return self.get_api_urls(), None, None + def get_actions(self): + actions_dict = {} + + for cls in type(self).mro()[::-1]: + class_actions = getattr(cls, 'actions', []) + for action in class_actions: + actions_dict[action.__name__] = { + 'name': action.__name__, + 'description': actions.get_description(action), + 'func': action + } + return actions_dict + class Admin2Inline(extra_views.InlineFormSet): """ diff --git a/djadmin2/static/themes/bootstrap/js/actions.js b/djadmin2/static/themes/bootstrap/js/actions.js new file mode 100644 index 0000000..daf3f0b --- /dev/null +++ b/djadmin2/static/themes/bootstrap/js/actions.js @@ -0,0 +1,26 @@ +$(function() { + var element = $("#model-list"); + var selectAllCheckbox = element.find('.model-select-all'); + var selectCheckboxen = element.find('.model-select'); + var selectedCount = element.find('#selected-count'); + + var updateSelectedCount = function() { + var count = 0; + for (var ix = 0; ix < selectCheckboxen.length; ix++) { + if ($(selectCheckboxen[ix]).prop('checked')) { + count++; + }; + }; + selectAllCheckbox.prop('checked', count == selectCheckboxen.length); + selectedCount.text(count); + }; + + selectAllCheckbox.click(function(e) { + selectCheckboxen.prop('checked', this.checked); + updateSelectedCount(); + }); + + selectCheckboxen.click(function(e) { + updateSelectedCount(); + }); +}); diff --git a/djadmin2/templates/admin2/bootstrap/base.html b/djadmin2/templates/admin2/bootstrap/base.html index 445b587..2c6b179 100644 --- a/djadmin2/templates/admin2/bootstrap/base.html +++ b/djadmin2/templates/admin2/bootstrap/base.html @@ -33,7 +33,6 @@