mirror of
https://github.com/netbox-community/netbox.git
synced 2026-03-21 20:18:38 -06:00
Add ObjectTypeSplitMultiSelectWidget and RegisteredActionsWidget
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
from .actions import *
|
||||
from .apiselect import *
|
||||
from .datetime import *
|
||||
from .misc import *
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
from django import forms
|
||||
|
||||
__all__ = (
|
||||
'RegisteredActionsWidget',
|
||||
)
|
||||
|
||||
|
||||
class RegisteredActionsWidget(forms.CheckboxSelectMultiple):
|
||||
"""
|
||||
Widget rendering checkboxes for registered model actions.
|
||||
Groups actions by model with data attributes for JS show/hide.
|
||||
"""
|
||||
template_name = 'widgets/registered_actions.html'
|
||||
|
||||
def __init__(self, *args, model_actions=None, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.model_actions = model_actions or {}
|
||||
|
||||
def get_context(self, name, value, attrs):
|
||||
context = super().get_context(name, value, attrs)
|
||||
context['widget']['model_actions'] = self.model_actions
|
||||
context['widget']['value'] = value or []
|
||||
return context
|
||||
@@ -9,6 +9,7 @@ __all__ = (
|
||||
'ClearableSelect',
|
||||
'ColorSelect',
|
||||
'HTMXSelect',
|
||||
'ObjectTypeSplitMultiSelectWidget',
|
||||
'SelectWithPK',
|
||||
'SplitMultiSelectWidget',
|
||||
)
|
||||
@@ -180,3 +181,60 @@ class SplitMultiSelectWidget(forms.MultiWidget):
|
||||
def value_from_datadict(self, data, files, name):
|
||||
# Return only the choices from the SelectedOptions widget
|
||||
return super().value_from_datadict(data, files, name)[1]
|
||||
|
||||
|
||||
#
|
||||
# ObjectType-specific widgets for ObjectPermissionForm
|
||||
#
|
||||
|
||||
class ObjectTypeSelectMultiple(SelectMultipleBase):
|
||||
"""
|
||||
SelectMultiple that adds data-model-key attribute to options for JS targeting.
|
||||
"""
|
||||
pk_to_model_key = None
|
||||
|
||||
def create_option(self, name, value, label, selected, index, subindex=None, attrs=None):
|
||||
option = super().create_option(name, value, label, selected, index, subindex, attrs)
|
||||
if self.pk_to_model_key:
|
||||
model_key = self.pk_to_model_key.get(value) or self.pk_to_model_key.get(str(value))
|
||||
if model_key:
|
||||
option['attrs']['data-model-key'] = model_key
|
||||
return option
|
||||
|
||||
|
||||
class ObjectTypeAvailableOptions(ObjectTypeSelectMultiple):
|
||||
include_selected = False
|
||||
|
||||
def get_context(self, name, value, attrs):
|
||||
context = super().get_context(name, value, attrs)
|
||||
context['widget']['attrs']['required'] = False
|
||||
return context
|
||||
|
||||
|
||||
class ObjectTypeSelectedOptions(ObjectTypeSelectMultiple):
|
||||
include_selected = True
|
||||
|
||||
|
||||
class ObjectTypeSplitMultiSelectWidget(SplitMultiSelectWidget):
|
||||
"""
|
||||
SplitMultiSelectWidget that adds data-model-key attributes to options.
|
||||
Used by ObjectPermissionForm to enable JS show/hide of custom actions.
|
||||
"""
|
||||
|
||||
def __init__(self, choices, attrs=None, ordering=False):
|
||||
widgets = [
|
||||
ObjectTypeAvailableOptions(
|
||||
attrs={'size': 8},
|
||||
choices=choices
|
||||
),
|
||||
ObjectTypeSelectedOptions(
|
||||
attrs={'size': 8, 'class': 'select-all'},
|
||||
choices=choices
|
||||
),
|
||||
]
|
||||
forms.MultiWidget.__init__(self, widgets, attrs)
|
||||
self.ordering = ordering
|
||||
|
||||
def set_model_key_map(self, pk_to_model_key):
|
||||
for widget in self.widgets:
|
||||
widget.pk_to_model_key = pk_to_model_key
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
{% load i18n %}
|
||||
<div class="registered-actions-container" id="id_registered_actions_container">
|
||||
{% for model_key, actions in widget.model_actions.items %}
|
||||
<div class="model-actions card mb-2" data-model="{{ model_key }}" style="display: none;">
|
||||
<div class="card-header py-2">
|
||||
<strong>{{ model_key }}</strong>
|
||||
</div>
|
||||
<div class="card-body py-2">
|
||||
{% for action in actions %}
|
||||
<div class="form-check">
|
||||
<input type="checkbox"
|
||||
class="form-check-input"
|
||||
name="{{ widget.name }}"
|
||||
value="{{ model_key }}.{{ action.name }}"
|
||||
id="id_{{ widget.name }}_{{ forloop.parentloop.counter }}_{{ forloop.counter }}"
|
||||
{% if model_key|add:"."|add:action.name in widget.value %}checked{% endif %}>
|
||||
<label class="form-check-label" for="id_{{ widget.name }}_{{ forloop.parentloop.counter }}_{{ forloop.counter }}">
|
||||
{{ action.name }}
|
||||
{% if action.help_text %}
|
||||
<small class="text-muted ms-1">{{ action.help_text }}</small>
|
||||
{% endif %}
|
||||
</label>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% empty %}
|
||||
<p class="text-muted" id="no-custom-actions-message">
|
||||
{% trans "No custom actions registered." %}
|
||||
</p>
|
||||
{% endfor %}
|
||||
</div>
|
||||
Reference in New Issue
Block a user