1
0
mirror of https://github.com/netbox-community/netbox.git synced 2024-05-10 07:54:54 +00:00

Hide table checkboxes when no bulk actions are enabled

This commit is contained in:
jeremystretch
2022-03-02 16:13:59 -05:00
parent dadd8cb93a
commit 21e3159711
5 changed files with 65 additions and 60 deletions

View File

@ -1,5 +1,6 @@
import logging import logging
import re import re
from collections import defaultdict
from copy import deepcopy from copy import deepcopy
from django.contrib import messages from django.contrib import messages
@ -42,27 +43,34 @@ class ObjectListView(BaseMultiObjectView):
Attributes: Attributes:
filterset: A django-filter FilterSet that is applied to the queryset filterset: A django-filter FilterSet that is applied to the queryset
filterset_form: The form class used to render filter options filterset_form: The form class used to render filter options
actions: Supported actions for the model. Default options are add, import, export, bulk_edit, and bulk_delete actions: Supported actions for the model. When adding custom actions, bulk action names must
be prefixed with `bulk_`. Default actions: add, import, export, bulk_edit, bulk_delete
action_perms: A dictionary mapping supported actions to a set of permissions required for each
""" """
template_name = 'generic/object_list.html' template_name = 'generic/object_list.html'
filterset = None filterset = None
filterset_form = None filterset_form = None
actions = ('add', 'import', 'export', 'bulk_edit', 'bulk_delete') actions = ('add', 'import', 'export', 'bulk_edit', 'bulk_delete')
action_perms = defaultdict(set, **{
'add': {'add'},
'import': {'add'},
'bulk_edit': {'change'},
'bulk_delete': {'delete'},
})
def get_required_permission(self): def get_required_permission(self):
return get_permission_for_model(self.queryset.model, 'view') return get_permission_for_model(self.queryset.model, 'view')
def get_table(self, request, permissions): def get_table(self, request, bulk_actions=True):
""" """
Return the django-tables2 Table instance to be used for rendering the objects list. Return the django-tables2 Table instance to be used for rendering the objects list.
Args: Args:
request: The current request request: The current request
permissions: A dictionary mapping of the view, add, change, and delete permissions to booleans indicating bulk_actions: Show checkboxes for object selection
whether the user has each
""" """
table = self.table(self.queryset, user=request.user) table = self.table(self.queryset, user=request.user)
if 'pk' in table.base_columns and (permissions['change'] or permissions['delete']): if 'pk' in table.base_columns and bulk_actions:
table.columns.show('pk') table.columns.show('pk')
return table return table
@ -135,17 +143,20 @@ class ObjectListView(BaseMultiObjectView):
if self.filterset: if self.filterset:
self.queryset = self.filterset(request.GET, self.queryset).qs self.queryset = self.filterset(request.GET, self.queryset).qs
# Compile a dictionary indicating which permissions are available to the current user for this model # Determine the available actions
permissions = {} actions = []
for action in ('add', 'change', 'delete', 'view'): for action in self.actions:
perm_name = get_permission_for_model(model, action) if request.user.has_perms([
permissions[action] = request.user.has_perm(perm_name) get_permission_for_model(model, name) for name in self.action_perms[action]
]):
actions.append(action)
has_bulk_actions = any([a.startswith('bulk_') for a in actions])
if 'export' in request.GET: if 'export' in request.GET:
# Export the current table view # Export the current table view
if request.GET['export'] == 'table': if request.GET['export'] == 'table':
table = self.get_table(request, permissions) table = self.get_table(request, has_bulk_actions)
columns = [name for name, _ in table.selected_columns] columns = [name for name, _ in table.selected_columns]
return self.export_table(table, columns) return self.export_table(table, columns)
@ -163,11 +174,11 @@ class ObjectListView(BaseMultiObjectView):
# Fall back to default table/YAML export # Fall back to default table/YAML export
else: else:
table = self.get_table(request, permissions) table = self.get_table(request, has_bulk_actions)
return self.export_table(table) return self.export_table(table)
# Render the objects table # Render the objects table
table = self.get_table(request, permissions) table = self.get_table(request, has_bulk_actions)
table.configure(request) table.configure(request)
# If this is an HTMX request, return only the rendered table HTML # If this is an HTMX request, return only the rendered table HTML
@ -179,8 +190,7 @@ class ObjectListView(BaseMultiObjectView):
context = { context = {
'model': model, 'model': model,
'table': table, 'table': table,
'permissions': permissions, 'actions': actions,
'actions': self.actions,
'filter_form': self.filterset_form(request.GET, label_suffix='') if self.filterset_form else None, 'filter_form': self.filterset_form(request.GET, label_suffix='') if self.filterset_form else None,
**self.get_extra_context(request), **self.get_extra_context(request),
} }

View File

@ -73,4 +73,5 @@
</ul> </ul>
</div> </div>
{% endif %} {% endif %}
{{ block.super }}
{% endblock %} {% endblock %}

View File

@ -13,9 +13,6 @@ Blocks:
Context: Context:
model: The model class being listed model: The model class being listed
table: The table class used for rendering the list of objects table: The table class used for rendering the list of objects
permissions: A mapping of add/change/delete permissions to boolean indicating
whether the current user possesses each of them. Controls the display of
add/edit/delete buttons.
actions: A list of buttons to display. This template checks for add, import, actions: A list of buttons to display. This template checks for add, import,
export, bulk_edit, and bulk_delete. export, bulk_edit, and bulk_delete.
filter_form: The bound filterset form for filtering the objects list (optional) filter_form: The bound filterset form for filtering the objects list (optional)
@ -28,10 +25,10 @@ Context:
<div class="controls"> <div class="controls">
<div class="control-group"> <div class="control-group">
{% block extra_controls %}{% endblock %} {% block extra_controls %}{% endblock %}
{% if permissions.add and 'add' in actions %} {% if 'add' in actions %}
{% add_button model|validated_viewname:"add" %} {% add_button model|validated_viewname:"add" %}
{% endif %} {% endif %}
{% if permissions.add and 'import' in actions %} {% if 'import' in actions %}
{% import_button model|validated_viewname:"import" %} {% import_button model|validated_viewname:"import" %}
{% endif %} {% endif %}
{% if 'export' in actions %} {% if 'export' in actions %}
@ -72,19 +69,18 @@ Context:
{# "Select all" form #} {# "Select all" form #}
{% if table.paginator.num_pages > 1 %} {% if table.paginator.num_pages > 1 %}
{% with bulk_edit_url=model|validated_viewname:"bulk_edit" bulk_delete_url=model|validated_viewname:"bulk_delete" %}
<div id="select-all-box" class="d-none card noprint"> <div id="select-all-box" class="d-none card noprint">
<form method="post" class="form col-md-12"> <form method="post" class="form col-md-12">
{% csrf_token %} {% csrf_token %}
<div class="card-body"> <div class="card-body">
<div class="float-end"> <div class="float-end">
{% if bulk_edit_url and permissions.change %} {% if 'bulk_edit' in actions %}
<button type="submit" name="_edit" formaction="{% url bulk_edit_url %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="btn btn-warning btn-sm" disabled> <button type="submit" name="_edit" formaction="{% url model|viewname:"bulk_edit" %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="btn btn-warning btn-sm" disabled>
<span class="mdi mdi-pencil" aria-hidden="true"></span> Edit All <span class="mdi mdi-pencil" aria-hidden="true"></span> Edit All
</button> </button>
{% endif %} {% endif %}
{% if bulk_delete_url and permissions.delete %} {% if 'bulk_delete' in actions %}
<button type="submit" name="_delete" formaction="{% url bulk_delete_url %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="btn btn-danger btn-sm" disabled> <button type="submit" name="_delete" formaction="{% url model|viewname:"bulk_delete" %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="btn btn-danger btn-sm" disabled>
<span class="mdi mdi-trash-can-outline" aria-hidden="true"></span> Delete All <span class="mdi mdi-trash-can-outline" aria-hidden="true"></span> Delete All
</button> </button>
{% endif %} {% endif %}
@ -98,7 +94,6 @@ Context:
</div> </div>
</form> </form>
</div> </div>
{% endwith %}
{% endif %} {% endif %}
{# Object table controls #} {# Object table controls #}
@ -118,17 +113,18 @@ Context:
{# Form buttons #} {# Form buttons #}
<div class="noprint bulk-buttons"> <div class="noprint bulk-buttons">
<div class="bulk-button-group"> <div class="bulk-button-group">
{% block bulk_buttons %}{% endblock %} {% block bulk_buttons %}
{% if 'bulk_edit' in actions and permissions.change %} {% if 'bulk_edit' in actions %}
<button type="submit" name="_edit" formaction="{% url model|viewname:"bulk_edit" %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="btn btn-warning btn-sm"> <button type="submit" name="_edit" formaction="{% url model|viewname:"bulk_edit" %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="btn btn-warning btn-sm">
<i class="mdi mdi-pencil" aria-hidden="true"></i> Edit Selected <i class="mdi mdi-pencil" aria-hidden="true"></i> Edit Selected
</button> </button>
{% endif %} {% endif %}
{% if 'bulk_delete' in actions and permissions.delete %} {% if 'bulk_delete' in actions %}
<button type="submit" name="_delete" formaction="{% url model|viewname:"bulk_delete" %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="btn btn-danger btn-sm"> <button type="submit" name="_delete" formaction="{% url model|viewname:"bulk_delete" %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="btn btn-danger btn-sm">
<i class="mdi mdi-trash-can-outline" aria-hidden="true"></i> Delete Selected <i class="mdi mdi-trash-can-outline" aria-hidden="true"></i> Delete Selected
</button> </button>
{% endif %} {% endif %}
{% endblock %}
</div> </div>
</div> </div>

View File

@ -17,4 +17,5 @@
</ul> </ul>
</div> </div>
{% endif %} {% endif %}
{{ block.super }}
{% endblock %} {% endblock %}

View File

@ -9,9 +9,6 @@ def get_permission_for_model(model, action):
:param model: A model or instance :param model: A model or instance
:param action: View, add, change, or delete (string) :param action: View, add, change, or delete (string)
""" """
if action not in ('view', 'add', 'change', 'delete'):
raise ValueError(f"Unsupported action: {action}")
return '{}.{}_{}'.format( return '{}.{}_{}'.format(
model._meta.app_label, model._meta.app_label,
action, action,