diff --git a/netbox/templates/users/objectpermission.html b/netbox/templates/users/objectpermission.html index ecbd52340..25fdcead3 100644 --- a/netbox/templates/users/objectpermission.html +++ b/netbox/templates/users/objectpermission.html @@ -47,7 +47,7 @@ {% checkmark object.can_delete %} {% for action in object.actions %} - {% if action not in 'view,add,change,delete' %} + {% if action not in reserved_actions %} {{ action }} {% checkmark True %} diff --git a/netbox/users/constants.py b/netbox/users/constants.py index 18add80cb..982e69868 100644 --- a/netbox/users/constants.py +++ b/netbox/users/constants.py @@ -10,6 +10,10 @@ OBJECTPERMISSION_OBJECT_TYPES = ( CONSTRAINT_TOKEN_USER = '$user' +# Built-in actions that receive special handling (dedicated checkboxes, model properties) +# and should not be registered as custom model actions. +RESERVED_ACTIONS = ('view', 'add', 'change', 'delete') + # API tokens TOKEN_PREFIX = 'nbt_' # Used for v2 tokens only TOKEN_KEY_LENGTH = 12 diff --git a/netbox/users/forms/model_forms.py b/netbox/users/forms/model_forms.py index 348c8b6da..ddb203e0b 100644 --- a/netbox/users/forms/model_forms.py +++ b/netbox/users/forms/model_forms.py @@ -423,7 +423,7 @@ class ObjectPermissionForm(forms.ModelForm): remaining_actions = list(self.instance.actions) # Check the appropriate CRUD checkboxes - for action in ['view', 'add', 'change', 'delete']: + for action in RESERVED_ACTIONS: if action in remaining_actions: self.fields[f'can_{action}'].initial = True remaining_actions.remove(action) @@ -450,7 +450,7 @@ class ObjectPermissionForm(forms.ModelForm): if isinstance(self.initial['actions'], str): self.initial['actions'] = [self.initial['actions']] if cloned_actions := self.initial['actions']: - for action in ['view', 'add', 'change', 'delete']: + for action in RESERVED_ACTIONS: if action in cloned_actions: self.fields[f'can_{action}'].initial = True self.initial['actions'].remove(action) @@ -479,10 +479,11 @@ class ObjectPermissionForm(forms.ModelForm): 'Action "{action}" is for {model} which is not selected.' ).format(action=action_name, model=model_key) }) - final_actions.append(action_name) + if action_name not in final_actions: + final_actions.append(action_name) # Append any of the selected CRUD checkboxes to the actions list - for action in ['view', 'add', 'change', 'delete']: + for action in RESERVED_ACTIONS: if self.cleaned_data.get(f'can_{action}') and action not in final_actions: final_actions.append(action) diff --git a/netbox/users/views.py b/netbox/users/views.py index 59ebe518f..c47e472eb 100644 --- a/netbox/users/views.py +++ b/netbox/users/views.py @@ -19,6 +19,7 @@ from utilities.query import count_related from utilities.views import GetRelatedModelsMixin, register_model_view from . import filtersets, forms, tables +from .constants import RESERVED_ACTIONS from .models import Group, ObjectPermission, Owner, OwnerGroup, Token, User # @@ -285,6 +286,11 @@ class ObjectPermissionView(generic.ObjectView): ], ) + def get_extra_context(self, request, instance): + return { + 'reserved_actions': RESERVED_ACTIONS, + } + @register_model_view(ObjectPermission, 'add', detail=False) @register_model_view(ObjectPermission, 'edit')