forms.py 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. from django import forms
  2. from django.conf import settings
  3. from django.contrib.auth.forms import AuthenticationForm, PasswordChangeForm as DjangoPasswordChangeForm
  4. from django.contrib.postgres.forms import SimpleArrayField
  5. from django.utils.html import mark_safe
  6. from ipam.formfields import IPNetworkFormField
  7. from netbox.preferences import PREFERENCES
  8. from utilities.forms import BootstrapMixin, DateTimePicker, StaticSelect
  9. from utilities.utils import flatten_dict
  10. from .models import Token, UserConfig
  11. class LoginForm(BootstrapMixin, AuthenticationForm):
  12. pass
  13. class PasswordChangeForm(BootstrapMixin, DjangoPasswordChangeForm):
  14. pass
  15. class UserConfigFormMetaclass(forms.models.ModelFormMetaclass):
  16. def __new__(mcs, name, bases, attrs):
  17. # Emulate a declared field for each supported user preference
  18. preference_fields = {}
  19. for field_name, preference in PREFERENCES.items():
  20. description = f'{preference.description}<br />' if preference.description else ''
  21. help_text = f'{description}<code>{field_name}</code>'
  22. field_kwargs = {
  23. 'label': preference.label,
  24. 'choices': preference.choices,
  25. 'help_text': mark_safe(help_text),
  26. 'coerce': preference.coerce,
  27. 'required': False,
  28. 'widget': StaticSelect,
  29. }
  30. preference_fields[field_name] = forms.TypedChoiceField(**field_kwargs)
  31. attrs.update(preference_fields)
  32. return super().__new__(mcs, name, bases, attrs)
  33. class UserConfigForm(BootstrapMixin, forms.ModelForm, metaclass=UserConfigFormMetaclass):
  34. fieldsets = (
  35. ('User Interface', (
  36. 'pagination.per_page',
  37. 'pagination.placement',
  38. 'ui.colormode',
  39. )),
  40. ('Miscellaneous', (
  41. 'data_format',
  42. )),
  43. )
  44. # List of clearable preferences
  45. pk = forms.MultipleChoiceField(
  46. choices=[],
  47. required=False
  48. )
  49. class Meta:
  50. model = UserConfig
  51. fields = ()
  52. def __init__(self, *args, instance=None, **kwargs):
  53. # Get initial data from UserConfig instance
  54. initial_data = flatten_dict(instance.data)
  55. kwargs['initial'] = initial_data
  56. super().__init__(*args, instance=instance, **kwargs)
  57. # Compile clearable preference choices
  58. self.fields['pk'].choices = (
  59. (f'tables.{table_name}', '') for table_name in instance.data.get('tables', [])
  60. )
  61. def save(self, *args, **kwargs):
  62. # Set UserConfig data
  63. for pref_name, value in self.cleaned_data.items():
  64. if pref_name == 'pk':
  65. continue
  66. self.instance.set(pref_name, value, commit=False)
  67. # Clear selected preferences
  68. for preference in self.cleaned_data['pk']:
  69. self.instance.clear(preference)
  70. return super().save(*args, **kwargs)
  71. @property
  72. def plugin_fields(self):
  73. return [
  74. name for name in self.fields.keys() if name.startswith('plugins.')
  75. ]
  76. class TokenForm(BootstrapMixin, forms.ModelForm):
  77. key = forms.CharField(
  78. required=False,
  79. help_text="If no key is provided, one will be generated automatically."
  80. )
  81. allowed_ips = SimpleArrayField(
  82. base_field=IPNetworkFormField(),
  83. required=False,
  84. label='Allowed IPs',
  85. help_text='Allowed IPv4/IPv6 networks from where the token can be used. Leave blank for no restrictions. '
  86. 'Example: <code>10.1.1.0/24,192.168.10.16/32,2001:db8:1::/64</code>',
  87. )
  88. class Meta:
  89. model = Token
  90. fields = [
  91. 'key', 'write_enabled', 'expires', 'description', 'allowed_ips',
  92. ]
  93. widgets = {
  94. 'expires': DateTimePicker(),
  95. }
  96. def __init__(self, *args, **kwargs):
  97. super().__init__(*args, **kwargs)
  98. # Omit the key field if token retrieval is not permitted
  99. if self.instance.pk and not settings.ALLOW_TOKEN_RETRIEVAL:
  100. del self.fields['key']