forms.py 4.0 KB

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