diff --git a/phonebox_plugin/__init__.py b/phonebox_plugin/__init__.py index b10eae7..079e337 100644 --- a/phonebox_plugin/__init__.py +++ b/phonebox_plugin/__init__.py @@ -5,7 +5,7 @@ class PhoneBoxConfig(PluginConfig): name = 'phonebox_plugin' verbose_name = 'PhoneBox Plugin' description = 'Telephone Number Management Plugin for NetBox.' - version = 'v0.0.4-beta.1' + version = 'v0.0.6-beta.1' author = 'Igor Korotchenkov' author_email = 'iDebugAll@gmail.com' base_url = 'phonebox' @@ -16,4 +16,4 @@ class PhoneBoxConfig(PluginConfig): '*': None } -config = PhoneBoxConfig +config = PhoneBoxConfig \ No newline at end of file diff --git a/phonebox_plugin/admin.py b/phonebox_plugin/admin.py index fa24117..dc23d59 100644 --- a/phonebox_plugin/admin.py +++ b/phonebox_plugin/admin.py @@ -4,4 +4,4 @@ @admin.register(Number) class NumPlanAdmin(admin.ModelAdmin): - list_display = ("number", "tenant", "description", "provider", "forward_to") + list_display = ('number', 'fio', 'pbx', 'tenant', 'region', 'site', 'provider') diff --git a/phonebox_plugin/api/__init__.py b/phonebox_plugin/api/__init__.py index 6dc5b51..9303317 100644 --- a/phonebox_plugin/api/__init__.py +++ b/phonebox_plugin/api/__init__.py @@ -1 +1 @@ -"""REST API for PhoneBox Plugin""" +"""REST API for PhoneBox Plugin""" \ No newline at end of file diff --git a/phonebox_plugin/api/nested_serializers.py b/phonebox_plugin/api/nested_serializers.py index f617b48..deca111 100644 --- a/phonebox_plugin/api/nested_serializers.py +++ b/phonebox_plugin/api/nested_serializers.py @@ -13,6 +13,4 @@ class NestedNumberSerializer(WritableNestedSerializer): class Meta: model = models.Number - fields = [ - "id", "label", "number", "tenant", - ] + fields = ["id", "label", "number", "tenant"] \ No newline at end of file diff --git a/phonebox_plugin/api/serializers.py b/phonebox_plugin/api/serializers.py index 84e5afb..9cdeefa 100644 --- a/phonebox_plugin/api/serializers.py +++ b/phonebox_plugin/api/serializers.py @@ -22,4 +22,4 @@ class Meta: model = Number fields = [ "id", "label", "number", "tenant", "region", "forward_to", "description", "provider", "tags", - ] + ] \ No newline at end of file diff --git a/phonebox_plugin/api/urls.py b/phonebox_plugin/api/urls.py index 185535d..6836f20 100644 --- a/phonebox_plugin/api/urls.py +++ b/phonebox_plugin/api/urls.py @@ -1,11 +1,10 @@ from rest_framework.routers import DefaultRouter from . import views - router = DefaultRouter() router.APIRootView = views.PhoneBoxPluginRootView router.register(r'numbers', views.NumberViewSet) app_name = "phonebox_plugin-api" -urlpatterns = router.urls +urlpatterns = router.urls \ No newline at end of file diff --git a/phonebox_plugin/api/views.py b/phonebox_plugin/api/views.py index 457b260..dfa7712 100644 --- a/phonebox_plugin/api/views.py +++ b/phonebox_plugin/api/views.py @@ -24,4 +24,4 @@ def get_view_name(self): class NumberViewSet(ModelViewSet): queryset = Number.objects.prefetch_related('tenant', 'region', 'tags') serializer_class = serializers.NumberSerializer - filterset_class = filters.NumberFilterSet + filterset_class = filters.NumberFilterSet \ No newline at end of file diff --git a/phonebox_plugin/choices.py b/phonebox_plugin/choices.py index f3a4646..f89b736 100644 --- a/phonebox_plugin/choices.py +++ b/phonebox_plugin/choices.py @@ -12,8 +12,26 @@ class VoiceCircuitTypeChoices(ChoiceSet): (ANALOG_VOICE_CIRCUIT, 'Analog Voice Circuit'), ) +class PBXTypeChoices(ChoiceSet): + VOIP_PBX = 'voip_pbx' + DIGITAL_PBX = 'digital_pbx' + ANALOG_PBX = 'analog_pbx' + CHOICES = ( + (VOIP_PBX, 'VoIP PBX'), + (DIGITAL_PBX, 'Digital PBX'), + (ANALOG_PBX, 'Analog PBX'), + ) VOICE_CIRCUIT_ASSIGNMENT_MODELS = Q( Q(app_label='dcim', model='interface') | Q(app_label='virtualization', model='vminterface') ) + +PBX_ASSIGNMENT_MODELS = Q( + Q(app_label='dcim', model='interface') | + Q(app_label='virtualization', model='vminterface') +) + +NUMBER_ASSIGNMENT_MODELS = Q( + Q(app_label='dcim', model='interface') +) \ No newline at end of file diff --git a/phonebox_plugin/filters.py b/phonebox_plugin/filters.py index d87f1eb..a9f1624 100644 --- a/phonebox_plugin/filters.py +++ b/phonebox_plugin/filters.py @@ -1,11 +1,11 @@ import django_filters from django.db.models import Q -from circuits.models import Provider -from dcim.models import Region, Site -from tenancy.models import Tenant -from .models import Number, VoiceCircuit -from packaging import version -from django.conf import settings +from circuits.models import Provider +from dcim.models import Region, Site, Location, Device +from tenancy.models import Tenant +from .models import Number, VoiceCircuit, PBX +from packaging import version +from django.conf import settings NETBOX_CURRENT_VERSION = version.parse(settings.VERSION) @@ -14,7 +14,7 @@ from utilities.filters import TagFilter else: from netbox.filtersets import BaseFilterSet - from extras.filters import TagFilter + from extras.filters import TagFilter class NumberFilterSet(BaseFilterSet): @@ -29,12 +29,12 @@ class NumberFilterSet(BaseFilterSet): to_field_name='number', label='number', ) - tenant = django_filters.ModelMultipleChoiceFilter( - queryset=Tenant.objects.all(), - field_name='tenant__id', - to_field_name='id', - label='Tenant (id)', - ) +# tenant = django_filters.ModelMultipleChoiceFilter( +# queryset=Tenant.objects.all(), +# field_name='tenant__id', +# to_field_name='id', +# label='Tenant (id)', +# ) region = django_filters.ModelMultipleChoiceFilter( queryset=Region.objects.all(), field_name='region__id', @@ -45,13 +45,13 @@ class NumberFilterSet(BaseFilterSet): queryset=Provider.objects.all(), field_name='provider__id', to_field_name='id', - label='Region (id)', + label='Provider (id)', ) forward_to = django_filters.ModelMultipleChoiceFilter( field_name='forward_to', queryset=Number.objects.all(), to_field_name='number', - label='forward_to', + label='Forward_to', ) tag = TagFilter() @@ -77,7 +77,7 @@ class VoiceCircuitFilterSet(BaseFilterSet): field_name='name', queryset=VoiceCircuit.objects.all(), to_field_name='name', - label='name', + label='Name', ) tenant = django_filters.ModelMultipleChoiceFilter( queryset=Tenant.objects.all(), @@ -115,3 +115,34 @@ def search(self, queryset, name, value): return queryset.filter( Q(name__icontains=value) ) + +class PbxTelephonyFilterSet(BaseFilterSet): + + q = django_filters.CharFilter( + method='search', + label='Search', + ) + name = django_filters.ModelMultipleChoiceFilter( + field_name='name', + queryset=PBX.objects.all(), + to_field_name='name', + label='Name', + ) + region = django_filters.ModelMultipleChoiceFilter( + queryset=Region.objects.all(), + field_name='region__id', + to_field_name='id', + label='Region (id)', + ) + tag = TagFilter() + + class Meta(): + model = PBX + fields = ('name',) + + def search(self, queryset, name, value): + if not value.strip(): + return queryset + return queryset.filter( + Q(name__icontains=value) + ) \ No newline at end of file diff --git a/phonebox_plugin/forms.py b/phonebox_plugin/forms.py index b3f1571..40901cd 100644 --- a/phonebox_plugin/forms.py +++ b/phonebox_plugin/forms.py @@ -3,13 +3,14 @@ BootstrapMixin, DynamicModelMultipleChoiceField, DynamicModelChoiceField, TagFilterField, BulkEditForm, CSVModelForm, CSVModelChoiceField ) -from tenancy.models import Tenant +from tenancy.forms import TenancyForm +from tenancy.models import Tenant, TenantGroup from dcim.models import Region, Site, Device, Interface from virtualization.models import VirtualMachine, VMInterface from circuits.models import Provider from extras.models import Tag -from .models import Number, VoiceCircuit -from .choices import VoiceCircuitTypeChoices +from .models import Number, VoiceCircuit, PBX +from .choices import VoiceCircuitTypeChoices, PBXTypeChoices class AddRemoveTagsForm(forms.Form): @@ -47,16 +48,10 @@ class NumberFilterForm(forms.Form): required=False, null_option='None', ) - provider = DynamicModelMultipleChoiceField( - queryset=Provider.objects.all(), - to_field_name='id', - required=False, - null_option='None', - ) tag = TagFilterField(model) -class NumberEditForm(forms.ModelForm): +class NumberEditForm(TenancyForm,forms.ModelForm): number = forms.CharField( required=True, @@ -69,15 +64,39 @@ class NumberEditForm(forms.ModelForm): } ) ) +# tenantgroup = DynamicModelChoiceField( +# queryset=TenantGroup.objects.all(), +# to_field_name='id', +# required=False, +# null_option='None', +# ) + tenant = DynamicModelChoiceField( + queryset=Tenant.objects.all(), + to_field_name='id', + required=False, + null_option='None', + initial_params={ + 'tennantgroup': '$tenantgroup' + } + ) + device = DynamicModelChoiceField( + queryset=Device.objects.all(), + required=False, + initial_params={ + 'interfaces': '$interface' + } + ) tags = DynamicModelMultipleChoiceField( queryset=Tag.objects.all(), required=False ) + fieldsets = ( + ('Tenancy', ('tenant_group', 'tenant')), + ) class Meta: model = Number - fields = ('number', 'tenant', 'region', 'description', 'provider', 'forward_to', 'tags') - + fields = ('number', 'fio', 'pbx', 'tenantgroup', 'tenant', 'provider', 'region', 'site', 'device', 'is_record', 'access_cat', 'forward_to', 'comment') class NumberBulkEditForm(AddRemoveTagsForm, BulkEditForm): @@ -97,13 +116,6 @@ class NumberBulkEditForm(AddRemoveTagsForm, BulkEditForm): required=False, null_option='None', ) - provider = DynamicModelChoiceField( - queryset=Provider.objects.all(), - to_field_name='id', - required=False, - null_option='None', - ) - # Implement plugin API to migrate to DynamicModelChoiceField forward_to = forms.ModelChoiceField( queryset=Number.objects.all(), to_field_name="number", @@ -115,7 +127,7 @@ class NumberBulkEditForm(AddRemoveTagsForm, BulkEditForm): ) class Meta: - nullable_fields = ('region', 'provider', 'forward_to', 'description') + nullable_fields = ('region', 'forward_to') class NumberCSVForm(CSVModelForm): @@ -349,3 +361,179 @@ class Meta: 'description', 'provider', 'provider_circuit_id', 'device', 'virtual_machine', 'interface', ] + +class PBXEditForm(forms.ModelForm): + + name = forms.CharField( + required=True, + ) + device = DynamicModelChoiceField( + queryset=Device.objects.all(), + required=False, + initial_params={ + 'interfaces': '$interface' + } + ) + interface = DynamicModelChoiceField( + queryset=Interface.objects.all(), + required=False, + query_params={ + 'device_id': '$device' + } + ) + virtual_machine = DynamicModelChoiceField( + queryset=VirtualMachine.objects.all(), + required=False, + initial_params={ + 'interfaces': '$vminterface' + } + ) + vminterface = DynamicModelChoiceField( + queryset=VMInterface.objects.all(), + required=False, + label='Interface', + query_params={ + 'virtual_machine_id': '$virtual_machine' + } + ) + tags = DynamicModelMultipleChoiceField( + queryset=Tag.objects.all(), + required=False + ) + + class Media: + js = ('phonebox_plugin/js/edit_PBX.js',) + + class Meta: + model = PBX + fields = ( + 'name', 'is_virtual', 'pbx_type', 'tenant', 'region', 'site', + 'description', 'domain', 'protocol', 'port', 'tags', + 'sip_proxy1', 'sip_proxy2' + ) + def __init__(self, *args, **kwargs): + + # Initialize helper selectors + instance = kwargs.get('instance') + initial = kwargs.get('initial', {}).copy() + if instance: + if type(instance.assigned_object) is Interface: + initial['interface'] = instance.assigned_object + elif type(instance.assigned_object) is VMInterface: + initial['vminterface'] = instance.assigned_object + + kwargs['initial'] = initial + super().__init__(*args, **kwargs) + + def clean(self): + super().clean() + + # Cannot select both a device interface and a VM interface + if self.cleaned_data.get('interface') and self.cleaned_data.get('vminterface'): + raise forms.ValidationError("Cannot select both a device interface and a virtual machine interface") + if not (self.cleaned_data.get('interface') or self.cleaned_data.get('vminterface')): + raise forms.ValidationError("PBX must be attached to a device interface or a VM interface") + self.instance.assigned_object = self.cleaned_data.get('interface') or self.cleaned_data.get('vminterface') + + +class PBXFilterForm(forms.Form): + + model = PBX + q = forms.CharField( + required=False, + label='Search' + ) + tenant = DynamicModelMultipleChoiceField( + queryset=Tenant.objects.all(), + to_field_name='id', + required=False, + null_option='None', + ) + region = DynamicModelMultipleChoiceField( + queryset=Region.objects.all(), + to_field_name='id', + required=False, + null_option='None', + ) + site = DynamicModelMultipleChoiceField( + queryset=Site.objects.all(), + to_field_name='id', + required=False, + null_option='None', + ) + tag = TagFilterField(model) + + +class PBXBulkEditForm(AddRemoveTagsForm, BulkEditForm): + + pk = forms.ModelMultipleChoiceField( + queryset=PBX.objects.all(), + widget=forms.MultipleHiddenInput() + ) + tenant = DynamicModelChoiceField( + queryset=Tenant.objects.all(), + to_field_name='id', + required=False, + null_option='None', + ) + region = DynamicModelChoiceField( + queryset=Region.objects.all(), + to_field_name='id', + required=False, + null_option='None', + ) + description = forms.CharField( + max_length=200, + required=False + ) + + class Meta: + nullable_fields = ('region', 'description') + + +class PBXCSVForm(CSVModelForm): + + tenant = CSVModelChoiceField( + queryset=Tenant.objects.all(), + required=True, + to_field_name='name', + help_text='Assigned tenant' + ) + site = CSVModelChoiceField( + queryset=Site.objects.all(), + to_field_name='name', + required=False, + help_text='Assigned site' + ) + region = CSVModelChoiceField( + queryset=Region.objects.all(), + required=False, + to_field_name='name', + help_text='Assigned region' + ) + device = CSVModelChoiceField( + queryset=Device.objects.all(), + required=False, + to_field_name='name', + help_text='Parent device of assigned interface (if any)' + ) + virtual_machine = CSVModelChoiceField( + queryset=VirtualMachine.objects.all(), + required=False, + to_field_name='name', + help_text='Parent VM of assigned interface (if any)' + ) + interface = CSVModelChoiceField( + queryset=Interface.objects.none(), # Can also refer to VMInterface + required=True, + to_field_name='name', + help_text='Assigned interface' + ) + + class Meta: + model = PBX + fields = [ + 'name', 'pbx_type', 'tenant', 'region', 'site', + 'description', 'protocol', 'port', 'tags', + 'sip_proxy1', 'sip_proxy2' + ] diff --git a/phonebox_plugin/migrations/0001_initial.py b/phonebox_plugin/migrations/0001_initial.py deleted file mode 100644 index e8d7cd9..0000000 --- a/phonebox_plugin/migrations/0001_initial.py +++ /dev/null @@ -1,39 +0,0 @@ -# Generated by Django 3.1.3 on 2021-02-07 17:09 - -import django.core.validators -from django.db import migrations, models -import django.db.models.deletion -import taggit.managers - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - ('dcim', '0122_standardize_name_length'), - ('extras', '0053_rename_webhook_obj_type'), - ('circuits', '0024_standardize_name_length'), - ('tenancy', '0011_standardize_name_length'), - ] - - operations = [ - migrations.CreateModel( - name='Number', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False)), - ('created', models.DateField(auto_now_add=True, null=True)), - ('last_updated', models.DateTimeField(auto_now=True, null=True)), - ('number', models.CharField(max_length=32, validators=[django.core.validators.RegexValidator('^\\+?[0-9A-D\\#\\*]*$', 'Numbers can only contain: leading +, digits 0-9; chars A, B, C, D; # and *')])), - ('description', models.CharField(blank=True, max_length=200)), - ('forward_to', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='forward_to_set', to='phonebox_plugin.number')), - ('provider', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='provider_set', to='circuits.provider')), - ('region', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='region_set', to='dcim.region')), - ('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')), - ('tenant', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='tenancy.tenant')), - ], - options={ - 'unique_together': {('number', 'tenant')}, - }, - ), - ] diff --git a/phonebox_plugin/migrations/0001_pbx.py b/phonebox_plugin/migrations/0001_pbx.py new file mode 100644 index 0000000..7b2c8c6 --- /dev/null +++ b/phonebox_plugin/migrations/0001_pbx.py @@ -0,0 +1,42 @@ +# Generated by Django + +from django.db import migrations, models +import django.db.models.deletion +import taggit.managers + +class Migration(migrations.Migration): + initial = True +# dependencies = [ +# ('dcim', '0122_standardize_name_length'), +# ('extras', '0053_rename_webhook_obj_type'), +# ('circuits', '0024_standardize_name_length'), +# ('tenancy', '0011_standardize_name_length'), +# ] + operations = [ + migrations.CreateModel( + name='PBX', + fields=[ + ('id', models.BigAutoField(primary_key=True, serialize=False)), + ('created', models.DateField(auto_now_add=True, null=True)), + ('last_updated', models.DateTimeField(auto_now=True, null=True)), + ('name', models.CharField(max_length=64)), + ('description', models.CharField(blank=True, null=True, max_length=200)), + ('pbx_type', models.CharField(max_length=50)), + ('is_virtual', models.BooleanField()), + ('domain', models.CharField(blank=True, null=True, max_length=200)), + ('protocol', models.CharField(blank=True, null=True,max_length=50)), + ('port', models.PositiveIntegerField(blank=True, null=True)), + ('sip_proxy1', models.CharField(blank=True, null=True, max_length=200)), + ('sip_proxy2', models.CharField(blank=True, null=True, max_length=200)), + ('assigned_object_id', models.PositiveIntegerField(blank=True, null=True)), + ('assigned_object_type', models.ForeignKey(blank=True, limit_choices_to=models.Q(models.Q(models.Q(('app_label', 'dcim'), ('model', 'interface')), models.Q(('app_label', 'virtualization'), ('model', 'vminterface')), _connector='OR')), null=True, on_delete=django.db.models.deletion.PROTECT, related_name='pbx_assign', to='contenttypes.contenttype')), + ('region', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='pbx_region_set', to='dcim.region')), + ('site', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='pbx_site_set', to='dcim.site')), + ('tenant', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='tenancy.tenant')), + ('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')), + ], + options={ + 'abstract': False, + }, + ), + ] \ No newline at end of file diff --git a/phonebox_plugin/migrations/0002_alter_number_id.py b/phonebox_plugin/migrations/0002_alter_number_id.py deleted file mode 100644 index 104313a..0000000 --- a/phonebox_plugin/migrations/0002_alter_number_id.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('phonebox_plugin', '0001_initial'), - ] - - operations = [ - migrations.AlterField( - model_name='number', - name='id', - field=models.BigAutoField(primary_key=True, serialize=False), - ), - ] diff --git a/phonebox_plugin/migrations/0003_voicecircuit.py b/phonebox_plugin/migrations/0002_voice_circuits.py similarity index 82% rename from phonebox_plugin/migrations/0003_voicecircuit.py rename to phonebox_plugin/migrations/0002_voice_circuits.py index 1779a87..f3615df 100644 --- a/phonebox_plugin/migrations/0003_voicecircuit.py +++ b/phonebox_plugin/migrations/0002_voice_circuits.py @@ -1,16 +1,18 @@ -# Generated by Django 3.2.7 +# Generated by Django from django.db import migrations, models import django.db.models.deletion import taggit.managers - class Migration(migrations.Migration): dependencies = [ - ('phonebox_plugin', '0002_alter_number_id'), + ('phonebox_plugin', '0001_pbx'), + ('dcim', '0122_standardize_name_length'), + ('extras', '0053_rename_webhook_obj_type'), + ('circuits', '0024_standardize_name_length'), + ('tenancy', '0011_standardize_name_length'), ] - operations = [ migrations.CreateModel( name='VoiceCircuit', @@ -20,10 +22,11 @@ class Migration(migrations.Migration): ('id', models.BigAutoField(primary_key=True, serialize=False)), ('name', models.CharField(max_length=64)), ('description', models.CharField(blank=True, max_length=200)), + ('pbx', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='vc_pbx_set', to='phonebox_plugin.pbx')), ('voice_circuit_type', models.CharField(max_length=50)), ('provider_circuit_id', models.CharField(blank=True, max_length=50)), - ('sip_source', models.CharField(blank=True, max_length=255)), - ('sip_target', models.CharField(blank=True, max_length=255)), + ('sip_source', models.CharField(blank=True, max_length=200)), + ('sip_target', models.CharField(blank=True, max_length=200)), ('assigned_object_id', models.PositiveIntegerField(blank=True, null=True)), ('assigned_object_type', models.ForeignKey(blank=True, limit_choices_to=models.Q(models.Q(models.Q(('app_label', 'dcim'), ('model', 'interface')), models.Q(('app_label', 'virtualization'), ('model', 'vminterface')), _connector='OR')), null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='contenttypes.contenttype')), ('provider', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='vc_provider_set', to='circuits.provider')), @@ -36,4 +39,4 @@ class Migration(migrations.Migration): 'abstract': False, }, ), - ] + ] \ No newline at end of file diff --git a/phonebox_plugin/migrations/0003_number.py b/phonebox_plugin/migrations/0003_number.py new file mode 100644 index 0000000..03537ef --- /dev/null +++ b/phonebox_plugin/migrations/0003_number.py @@ -0,0 +1,66 @@ +# Generated by Django + +import django.core.validators +from django.db import migrations, models +import django.db.models.deletion +import taggit.managers + +class Migration(migrations.Migration): + + dependencies = [ + ('phonebox_plugin', '0002_voice_circuits'), + ('dcim', '0122_standardize_name_length'), + ('extras', '0053_rename_webhook_obj_type'), + ('circuits', '0024_standardize_name_length'), + ('tenancy', '0011_standardize_name_length'), + ] + + operations = [ + migrations.CreateModel( + name='Number', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)), + ('created', models.DateField(auto_now_add=True, null=True)), + ('last_updated', models.DateTimeField(auto_now=True, null=True)), + ('number', models.CharField(max_length=32, + validators=[django.core.validators.RegexValidator('^\\+?[0-9A-D\\#\\*]*$', 'Numbers can only contain: leading +, digits 0-9; chars A, B, C, D; # and *')])), + ('fio', models.CharField(blank=True, null=True, max_length=200)), + ('pbx', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, + related_name='num_pbx_set', to='phonebox_plugin.pbx')), + ('device', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, + related_name='num_device_set', to='dcim.device')), + ('region', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, + related_name='num_region_set', to='dcim.region')), + ('site', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, + related_name='num_site_set', to='dcim.site')), + ('tenantgroup', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, + to='tenancy.tenantgroup')), + ('tenant', models.ForeignKey(blank=True, null=True,on_delete=django.db.models.deletion.CASCADE, + to='tenancy.tenant')), + ('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')), + ('forward_to', models.ForeignKey(blank=True, null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='num_forward_to_set', to='phonebox_plugin.number')), + ('provider', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, + related_name='num_provider_set', to='circuits.provider')), + ('is_record', models.BooleanField()), + ('access_cat', models.CharField(blank=True, null=True, max_length=32)), + ('comment', models.CharField(blank=True, null=True, max_length=200)), + ('assigned_object_id', models.PositiveIntegerField(blank=True, null=True)), + ('assigned_object_type', models.ForeignKey(blank=True, + limit_choices_to=models.Q(models.Q(models.Q(('app_label', 'dcim'), ('model', 'interface')),)), + null=True, on_delete=django.db.models.deletion.PROTECT, + related_name='number_assign', + to='contenttypes.contenttype')), + ('description', models.CharField(blank=True, null=True, max_length=200)), + ], + options={ + 'abstract': False, + }, + ), + migrations.AlterField( + model_name='number', + name='id', + field=models.BigAutoField(primary_key=True, serialize=False), + ), + ] \ No newline at end of file diff --git a/phonebox_plugin/models.py b/phonebox_plugin/models.py index 5dfe2c8..5c9e60f 100644 --- a/phonebox_plugin/models.py +++ b/phonebox_plugin/models.py @@ -7,14 +7,13 @@ from django.core.validators import RegexValidator from taggit.managers import TaggableManager from django.urls import reverse -from .choices import VoiceCircuitTypeChoices, VOICE_CIRCUIT_ASSIGNMENT_MODELS +from .choices import VoiceCircuitTypeChoices, VOICE_CIRCUIT_ASSIGNMENT_MODELS, PBXTypeChoices, PBX_ASSIGNMENT_MODELS, NUMBER_ASSIGNMENT_MODELS number_validator = RegexValidator( r"^\+?[0-9A-D\#\*]*$", "Numbers can only contain: leading +, digits 0-9; chars A, B, C, D; # and *" ) - class Number(ChangeLoggedModel): """A Number represents a single telephone number of an arbitrary format. A Number can contain only valid DTMF characters and leading plus sign for E.164 support: @@ -32,39 +31,90 @@ class Number(ChangeLoggedModel): """ number = models.CharField(max_length=32, validators=[number_validator]) + fio = models.CharField(max_length=200, blank=True) + pbx = models.ForeignKey( + to='phonebox_plugin.PBX', + on_delete=models.SET_NULL, + blank=True, + null=True, + related_name = "num_pbx_set" + ) tenant = models.ForeignKey( to='tenancy.Tenant', on_delete=models.CASCADE, - blank=False, - null=False - ) - description = models.CharField(max_length=200, blank=True) - provider = models.ForeignKey( - to="circuits.Provider", - on_delete=models.SET_NULL, blank=True, null=True, - related_name="provider_set" + related_query_name='tennantgroup' + ) + tenantgroup = models.ForeignKey( + to='tenancy.TenantGroup', + on_delete=models.CASCADE, + blank=True, + null=True ) region = models.ForeignKey( to="dcim.Region", on_delete=models.SET_NULL, blank=True, null=True, - related_name="region_set" + related_name="num_region_set" + ) + site = models.ForeignKey( + to="dcim.Site", + on_delete=models.SET_NULL, + blank=True, + null=True, + related_name="num_site_set" + ) + tags = TaggableManager(through=TaggedItem) + device = models.ForeignKey( + to='dcim.Device', + on_delete=models.SET_NULL, + blank=True, + null=True, + related_name = "num_device_set" + ) + is_record = models.BooleanField() + access_cat = models.CharField( + max_length=32, + blank=True, + null=True, ) forward_to = models.ForeignKey( to="self", on_delete=models.SET_NULL, blank=True, null=True, - related_name="forward_to_set" + related_name="num_forward_to_set" ) - tags = TaggableManager(through=TaggedItem) + provider = models.ForeignKey( + to="circuits.Provider", + on_delete=models.SET_NULL, + blank=True, + null=True, + related_name="num_provider_set" + ) + assigned_object_type = models.ForeignKey( + to=ContentType, + limit_choices_to=NUMBER_ASSIGNMENT_MODELS, + on_delete=models.PROTECT, + related_name='number_assign', + blank=True, + null=True + ) + assigned_object_id = models.PositiveIntegerField( + blank=True, + null=True + ) + assigned_object = GenericForeignKey( + ct_field='assigned_object_type', + fk_field='assigned_object_id' + ) + comment = models.CharField(max_length=200, blank=True) objects = RestrictedQuerySet.as_manager() - csv_headers = ['number', 'tenant', 'region', 'description', 'provider', 'forward_to'] + csv_headers = ['number', 'pbx', 'tenant', 'provider', 'region', 'site', 'device', 'is_record', 'access_cat', 'forward_to'] def __str__(self): return str(self.number) @@ -73,8 +123,7 @@ def get_absolute_url(self): return reverse("plugins:phonebox_plugin:number_view", kwargs={"pk": self.pk}) class Meta: - unique_together = ("number", "tenant",) - + ordering = ['tenantgroup', 'tenant'] class VoiceCircuit(ChangeLoggedModel): """A Voice Circuit represents a single circuit of one of the following types: @@ -96,6 +145,13 @@ class VoiceCircuit(ChangeLoggedModel): choices=VoiceCircuitTypeChoices, blank=False ) + pbx = models.ForeignKey( + to='phonebox_plugin.PBX', + on_delete=models.SET_NULL, + blank=True, + null=True, + related_name = "vc_pbx_set" + ) provider = models.ForeignKey( to="circuits.Provider", on_delete=models.SET_NULL, @@ -124,11 +180,11 @@ class VoiceCircuit(ChangeLoggedModel): tags = TaggableManager(through=TaggedItem) sip_source = models.CharField( - max_length=255, + max_length=200, blank=True ) sip_target = models.CharField( - max_length=255, + max_length=200, blank=True ) @@ -158,3 +214,93 @@ def __str__(self): def get_absolute_url(self): return reverse("plugins:phonebox_plugin:voice_circuit_view", kwargs={"pk": self.pk}) + +class PBX(ChangeLoggedModel): + + name = models.CharField( + max_length=64 + ) + description = models.CharField( + max_length=200, + blank=True, + null=True + ) + pbx_type = models.CharField( + max_length=50, + choices=PBXTypeChoices, + blank=False + ) + is_virtual = models.BooleanField( + ) + region = models.ForeignKey( + to="dcim.Region", + on_delete=models.SET_NULL, + blank=True, + null=True, + related_name="pbx_region_set" + ) + site = models.ForeignKey( + to="dcim.Site", + on_delete=models.SET_NULL, + blank=True, + null=True, + related_name="pbx_site_set" + ) + tenant = models.ForeignKey( + to='tenancy.Tenant', + on_delete=models.CASCADE, + blank=True, + null=True + ) + tags = TaggableManager(through=TaggedItem) + domain = models.CharField( + max_length=200, + blank=True, + null=True + ) + protocol = models.CharField( + max_length=50, + blank=True, + null=True + ) + port = models.PositiveIntegerField( + blank=True, + null=True + ) + sip_proxy1 = models.CharField( + max_length=200, + blank=True, + null=True + ) + sip_proxy2 = models.CharField( + max_length=200, + blank=True, + null=True + ) + assigned_object_type = models.ForeignKey( + to=ContentType, + limit_choices_to=PBX_ASSIGNMENT_MODELS, + on_delete=models.PROTECT, + related_name='pbx_assign', + blank=True, + null=True + ) + assigned_object_id = models.PositiveIntegerField( + blank=True, + null=True + ) + assigned_object = GenericForeignKey( + ct_field='assigned_object_type', + fk_field='assigned_object_id' + ) + + objects = RestrictedQuerySet.as_manager() + + csv_headers = ['name', 'pbx_type', 'region', 'site'] + + def __str__(self): + return str(self.name) + + def get_absolute_url(self): + return reverse("plugins:phonebox_plugin:pbx_view", kwargs={"pk": self.pk}) + diff --git a/phonebox_plugin/navigation.py b/phonebox_plugin/navigation.py index d6d1e53..32aa754 100644 --- a/phonebox_plugin/navigation.py +++ b/phonebox_plugin/navigation.py @@ -2,13 +2,18 @@ menu_items = ( PluginMenuItem( - link='plugins:phonebox_plugin:list_view', + link='plugins:phonebox_plugin:pbx_list', + link_text='PBX', + buttons=() + ), + PluginMenuItem( + link='plugins:phonebox_plugin:number_list', link_text='Numbers', buttons=() ), PluginMenuItem( - link='plugins:phonebox_plugin:voice_circuit_list_view', + link='plugins:phonebox_plugin:voice_circuit_list', link_text='Voice Circuits', buttons=() ), -) +) \ No newline at end of file diff --git a/phonebox_plugin/tables.py b/phonebox_plugin/tables.py index ac7d91d..ba78748 100644 --- a/phonebox_plugin/tables.py +++ b/phonebox_plugin/tables.py @@ -1,7 +1,7 @@ import django_tables2 as tables -from .models import Number, VoiceCircuit -from django.conf import settings -from packaging import version +from .models import Number, VoiceCircuit, PBX +from django.conf import settings +from packaging import version NETBOX_CURRENT_VERSION = version.parse(settings.VERSION) @@ -14,34 +14,71 @@ class NumberTable(BaseTable): - pk = ToggleColumn() - number = tables.LinkColumn() - tenant = tables.LinkColumn() - region = tables.LinkColumn() - provider = tables.LinkColumn() + pk = ToggleColumn() + number = tables.LinkColumn() + fio = tables.LinkColumn() + pbx = tables.LinkColumn() + tenant = tables.LinkColumn() + tenantgroup= tables.LinkColumn() + region = tables.LinkColumn() + site = tables.LinkColumn() + provider = tables.LinkColumn() + device = tables.LinkColumn() + is_record = tables.LinkColumn() + access_cat = tables.LinkColumn() forward_to = tables.LinkColumn() + comment = tables.LinkColumn() class Meta(BaseTable.Meta): - model = Number - fields = ('pk', 'number', 'tenant', 'region', 'description', 'provider', 'forward_to') + model = Number + fields = ('pk', 'number', 'fio', 'pbx', 'tenant', 'tenantgroup', 'region', 'site', 'provider', 'device', 'is_record', 'access_cat', 'forward_to') +# fields = ('pk', 'number', 'fio', 'tenant', 'region', 'site', 'provider', 'device', 'is_record', 'access_cat', 'forward_to', 'comment') class VoiceCircuitTable(BaseTable): - pk = ToggleColumn() - name = tables.LinkColumn() + pk = ToggleColumn() + name = tables.LinkColumn() voice_device_or_vm = tables.Column( accessor='assigned_object.parent_object', linkify=True, orderable=False, verbose_name='Device/VM' ) + pbx = tables.LinkColumn() voice_circuit_type = tables.LinkColumn() - tenant = tables.LinkColumn() - region = tables.LinkColumn() - site = tables.LinkColumn() - provider = tables.LinkColumn() + tenant = tables.LinkColumn() + region = tables.LinkColumn() + site = tables.LinkColumn() + provider = tables.LinkColumn() class Meta(BaseTable.Meta): - model = VoiceCircuit + model = VoiceCircuit +# fields = ('pk', 'name', 'pbx', 'voice_device_or_vm', 'voice_circuit_type', 'tenant', 'region', 'site', 'provider') fields = ('pk', 'name', 'voice_device_or_vm', 'voice_circuit_type', 'tenant', 'region', 'site', 'provider') + +class PBXTable(BaseTable): + + pk = ToggleColumn() + name = tables.LinkColumn() + description = tables.LinkColumn() + voice_device_or_vm = tables.Column( + accessor='assigned_object.parent_object', + linkify=True, + orderable=False, + verbose_name='Device/VM' + ) + region = tables.LinkColumn() + site = tables.LinkColumn() + pbx_type = tables.LinkColumn() + is_virtual = tables.LinkColumn() + domain = tables.LinkColumn() + protocol = tables.LinkColumn() + port = tables.LinkColumn() + sip_proxy1 = tables.LinkColumn() + sip_proxy2 = tables.LinkColumn() + + class Meta(BaseTable.Meta): + model = PBX + fields = ('pk', 'name', 'voice_device_or_vm', 'region', 'site', 'pbx_type', 'is_virtual') +# fields = ('pk', 'name', 'region', 'site', 'pbx_type') \ No newline at end of file diff --git a/phonebox_plugin/templates/phonebox_plugin/number.html b/phonebox_plugin/templates/phonebox_plugin/number.html index 321f885..bb030e5 100644 --- a/phonebox_plugin/templates/phonebox_plugin/number.html +++ b/phonebox_plugin/templates/phonebox_plugin/number.html @@ -6,12 +6,12 @@
-
+
@@ -51,6 +51,36 @@

{% block title %}{{ object }}{% endblock %}

Number
+ + + + + + + + + + + + + + + +
FIO + {% if object.fio %} + {{ object.fio }} + {% else %} + None + {% endif %} +
PBX + {% if object.pbx %} + {{ object.pbx }} + {% else %} + None + {% endif %} +
Access Category + {% if object.access_cat %} + {{ object.access_cat }} + {% else %} + None + {% endif %} +
Tenant @@ -74,6 +104,16 @@

{% block title %}{{ object }}{% endblock %}

{% endif %}
Site + {% if object.site %} + {{ object.site }} + {% else %} + None + {% endif %} +
Provider @@ -100,7 +140,7 @@

{% block title %}{{ object }}{% endblock %}

- {% include 'extras/inc/tags_panel.html' with tags=object.tags.all url='plugins:phonebox_plugin:list_view' %} + {% include 'extras/inc/tags_panel.html' with tags=object.tags.all url='plugins:phonebox_plugin:number_list' %}
diff --git a/phonebox_plugin/templates/phonebox_plugin/number_3.x.html b/phonebox_plugin/templates/phonebox_plugin/number_3.x.html index 46a34c0..cd6db24 100644 --- a/phonebox_plugin/templates/phonebox_plugin/number_3.x.html +++ b/phonebox_plugin/templates/phonebox_plugin/number_3.x.html @@ -5,11 +5,11 @@
- +
@@ -26,12 +26,12 @@

{{ object }}

- {% if perms.phonebox_plugin.change_number %} + {% if perms.phonebox_plugin.number_change %} Edit {% endif %} - {% if perms.phonebox_plugin.delete_number %} + {% if perms.phonebox_plugin.number_delete %} Delete @@ -51,6 +51,36 @@
+ + + + + + + + + + + + + + + +
FIO + {% if object.fio %} + {{ object.fio }} + {% else %} + None + {% endif %} +
PBX + {% if object.pbx %} + {{ object.pbx }} + {% else %} + None + {% endif %} +
Access Category + {% if object.access_cat %} + {{ object.access_cat }} + {% else %} + None + {% endif %} +
Tenant @@ -74,6 +104,16 @@
{% endif %}
Site + {% if object.site %} + {{ object.site }} + {% else %} + None + {% endif %} +
Provider @@ -101,8 +141,8 @@
- {% include 'phonebox_plugin/tags_panel.html' with tags=object.tags.all url='plugins:phonebox_plugin:list_view' %} + {% include 'phonebox_plugin/tags_panel.html' with tags=object.tags.all url='plugins:phonebox_plugin:number_list' %}
-{% endblock content%} +{% endblock content%} \ No newline at end of file diff --git a/phonebox_plugin/templates/phonebox_plugin/add_number_3.x.html b/phonebox_plugin/templates/phonebox_plugin/number_add.html similarity index 60% rename from phonebox_plugin/templates/phonebox_plugin/add_number_3.x.html rename to phonebox_plugin/templates/phonebox_plugin/number_add.html index 4ad5861..f990ff8 100644 --- a/phonebox_plugin/templates/phonebox_plugin/add_number_3.x.html +++ b/phonebox_plugin/templates/phonebox_plugin/number_add.html @@ -7,11 +7,18 @@
Number
{% render_field form.number %} + {% render_field form.fio %} + {% render_field form.pbx %} + {% render_field form.tenantgroup %} {% render_field form.tenant %} {% render_field form.region %} + {% render_field form.site %} {% render_field form.provider %} + {% render_field form.device %} + {% render_field form.is_record %} + {% render_field form.access_cat %} {% render_field form.forward_to %} - {% render_field form.description %} + {% render_field form.comment %} {% render_field form.tags %}
diff --git a/phonebox_plugin/templates/phonebox_plugin/add_number.html b/phonebox_plugin/templates/phonebox_plugin/number_add_3.x.html similarity index 60% rename from phonebox_plugin/templates/phonebox_plugin/add_number.html rename to phonebox_plugin/templates/phonebox_plugin/number_add_3.x.html index 4ad5861..f990ff8 100644 --- a/phonebox_plugin/templates/phonebox_plugin/add_number.html +++ b/phonebox_plugin/templates/phonebox_plugin/number_add_3.x.html @@ -7,11 +7,18 @@
Number
{% render_field form.number %} + {% render_field form.fio %} + {% render_field form.pbx %} + {% render_field form.tenantgroup %} {% render_field form.tenant %} {% render_field form.region %} + {% render_field form.site %} {% render_field form.provider %} + {% render_field form.device %} + {% render_field form.is_record %} + {% render_field form.access_cat %} {% render_field form.forward_to %} - {% render_field form.description %} + {% render_field form.comment %} {% render_field form.tags %}
diff --git a/phonebox_plugin/templates/phonebox_plugin/list_view.html b/phonebox_plugin/templates/phonebox_plugin/number_list.html similarity index 77% rename from phonebox_plugin/templates/phonebox_plugin/list_view.html rename to phonebox_plugin/templates/phonebox_plugin/number_list.html index d5024b8..681ae1b 100644 --- a/phonebox_plugin/templates/phonebox_plugin/list_view.html +++ b/phonebox_plugin/templates/phonebox_plugin/number_list.html @@ -3,11 +3,11 @@ {% block content %}
- {% if perms.phonebox_plugin.add_number %} - {% add_button 'plugins:phonebox_plugin:add_number' %} + {% if perms.phonebox_plugin.number_add %} + {% add_button 'plugins:phonebox_plugin:number_add' %} {% endif %} {% if permissions.add and 'import' in action_buttons %} - {% import_button 'plugins:phonebox_plugin:import_numbers' %} + {% import_button 'plugins:phonebox_plugin:number_import' %} {% endif %}

{% block title %}Numbers{% endblock %}

diff --git a/phonebox_plugin/templates/phonebox_plugin/list_view_3.x.html b/phonebox_plugin/templates/phonebox_plugin/number_list_3.x.html similarity index 84% rename from phonebox_plugin/templates/phonebox_plugin/list_view_3.x.html rename to phonebox_plugin/templates/phonebox_plugin/number_list_3.x.html index b05ecec..b81c2a8 100644 --- a/phonebox_plugin/templates/phonebox_plugin/list_view_3.x.html +++ b/phonebox_plugin/templates/phonebox_plugin/number_list_3.x.html @@ -8,13 +8,13 @@ {% block controls %}
- {% if perms.phonebox_plugin.add_number %} - + {% if perms.phonebox_plugin.number_add %} + Add {% endif %} - {% if perms.phonebox_plugin.add_number %} - + {% if perms.phonebox_plugin.number_add %} + Import {% endif %} diff --git a/phonebox_plugin/templates/phonebox_plugin/obj_table.html b/phonebox_plugin/templates/phonebox_plugin/obj_table.html index 842a90a..9718bc2 100644 --- a/phonebox_plugin/templates/phonebox_plugin/obj_table.html +++ b/phonebox_plugin/templates/phonebox_plugin/obj_table.html @@ -59,4 +59,4 @@ {% endif %} -{% include 'inc/paginator.html' with paginator=table.paginator page=table.page %} +{% include 'inc/paginator.html' with paginator=table.paginator page=table.page %} \ No newline at end of file diff --git a/phonebox_plugin/templates/phonebox_plugin/pbx.html b/phonebox_plugin/templates/phonebox_plugin/pbx.html new file mode 100644 index 0000000..a3cb761 --- /dev/null +++ b/phonebox_plugin/templates/phonebox_plugin/pbx.html @@ -0,0 +1,165 @@ +{% extends 'base.html' %} +{% load buttons %} +{% load static %} + +{% block header %} +
+
+ +
+
+ +
+ + + + +
+ +
+
+
+ {% if perms.phonebox_plugin.pbx_change %} + + Edit + + {% endif %} + {% if perms.phonebox_plugin.pbx_delete %} + + Delete + + {% endif %} +
+

{% block title %}{{ object }}{% endblock %}

+ +{% endblock %} + +{% block content %} +
+
+
+
+ PBX +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Type{{ object.pbx_type }}
Assignment + {% if object.assigned_object %} + {{ object.assigned_object.parent_object }} / + {{ object.assigned_object }} + {% else %} + + {% endif %} +
Tenant + {% if object.tenant %} + {% if object.tenant.group %} + {{ object.tenant.group }} / + {% endif %} + {{ object.tenant }} + {% else %} + None + {% endif %} +
Site + {% if object.site %} + {{ object.site }} + {% else %} + None + {% endif %} +
Region + {% if object.region %} + {{ object.region }} + {% else %} + None + {% endif %} +
Provider + {% if object.provider %} + {{ object.provider }} + {% else %} + None + {% endif %} +
Provider Circuit ID{{ object.provider_ciruit_id }}
Description{{ object.description }}
+
+ {% include 'extras/inc/tags_panel.html' with tags=object.tags.all url='plugins:phonebox_plugin:voice_circuit_list' %} +
+
+ +{% if object.voice_circuit_type == "sip_trunk" %} +
+
+
+ SIP Trunk Details +
+ + + + + + + + + +
Source{{ object.sip_source }}
Target{{ object.sip_target }}
+
+
+ {% endif %} + {% if object.voice_circuit_type == "digital_voice_circuit" %} +
+
+
+ Digital Voice Circuit Details +
+
+
+ {% endif %} + {% if object.voice_circuit_type == "analog_voice_circuit" %} +
+
+
+ Analog Voice Circuit Details +
+
+
+ {% endif %} +
+ +{% endblock %} diff --git a/phonebox_plugin/templates/phonebox_plugin/pbx_3.x.html b/phonebox_plugin/templates/phonebox_plugin/pbx_3.x.html new file mode 100644 index 0000000..046d747 --- /dev/null +++ b/phonebox_plugin/templates/phonebox_plugin/pbx_3.x.html @@ -0,0 +1,156 @@ +{% extends 'base/layout.html' %} +{% load static %} + +{% block header %} +
+
+ +
+
+
+
+ + + + +
+
+
+
+
+
+

{{ object }}

+
+
+ {% if perms.phonebox_plugin.pbx_change %} + + Edit + + {% endif %} + {% if perms.phonebox_plugin.pbx_delete %} + + Delete + + {% endif %} +
+
+ +{% endblock %} + + +{% block content %} +
+
+
+
+ General Information +
+
+ + + + + + + + + + + + + + + + + + + + + +
Type{{ object.pbx_type }}
Assignment + {% if object.assigned_object %} + {{ object.assigned_object.parent_object }} / + {{ object.assigned_object }} + {% else %} + + {% endif %} +
Region + {% if object.region %} + {{ object.region }} + {% else %} + None + {% endif %} +
Site + {% if object.forward_to %} + {{ object.site }} + {% else %} + None + {% endif %} +
Description{{ object.description }}
+
+
+ {% include 'phonebox_plugin/tags_panel.html' with tags=object.tags.all url='plugins:phonebox_plugin:pbx_list' %} +
+ + {% if object.pbx_type == "voip_pbx" %} +
+
+
+ SIP Settings +
+
+ + + + + + + + + + + + + + + + + + + + + +
Domain{{ object.domain }}
Protocol{{ object.protocol }}
Port{{ object.port }}
SIP Proxy 1{{ object.sip_proxy1 }}
SIP Proxy 2{{ object.sip_proxy2 }}
+
+
+
+ {% endif %} + {% if object.pbx_type == "digital_pbx" %} +
+
+
+ Digital PBX +
+
+
+
+
+ {% endif %} + {% if object.pbx_type == "analog_pbx" %} +
+
+
+ Analog PBX +
+
+
+
+
+ {% endif %} +
+ +{% endblock content %} \ No newline at end of file diff --git a/phonebox_plugin/templates/phonebox_plugin/pbx_add_3.x.html b/phonebox_plugin/templates/phonebox_plugin/pbx_add_3.x.html new file mode 100644 index 0000000..c43e33c --- /dev/null +++ b/phonebox_plugin/templates/phonebox_plugin/pbx_add_3.x.html @@ -0,0 +1,112 @@ +{% extends 'generic/object_edit.html' %} +{% load static %} +{% load form_helpers %} + +{% block head %} + {{ block.super }} + +{% endblock %} + +{% block form %} + +
+
PBX
+
+ {% render_field form.name %} + {% render_field form.pbx_type %} + {% render_field form.is_virtual %} + {% render_field form.tenant %} + {% render_field form.site %} + {% render_field form.region %} + {% render_field form.description %} + {% render_field form.tags %} +
+
+
+
Interface Assignment
+
+ {% with vm_tab_active=form.initial.vminterface %} +
+
+ +
+
+
+
+ {% render_field form.device %} + {% render_field form.interface %} +
+
+ {% render_field form.virtual_machine %} + {% render_field form.vminterface %} +
+
+ {% endwith %} +
+ +
+
+
SIP Settings
+
+
+ {% render_field form.domain %} + {% render_field form.protocol %} + {% render_field form.port %} + {% render_field form.sip_proxy1 %} + {% render_field form.sip_proxy2 %} +
+
+ + +
+ +{% endblock %} \ No newline at end of file diff --git a/phonebox_plugin/templates/phonebox_plugin/pbx_list.html b/phonebox_plugin/templates/phonebox_plugin/pbx_list.html new file mode 100644 index 0000000..d16d66d --- /dev/null +++ b/phonebox_plugin/templates/phonebox_plugin/pbx_list.html @@ -0,0 +1,22 @@ +{% extends 'base.html' %} +{% load buttons %} + +{% block content %} +
+ {% if perms.phonebox_plugin.pbx_add %} + {% add_button 'plugins:phonebox_plugin:pbx_add' %} + {% endif %} + {% if permissions.add and 'import' in action_buttons %} + {% import_button 'plugins:phonebox_plugin:pbx_import' %} + {% endif %} +
+

{% block title %}PBX{% endblock %}

+
+
+ {% include 'utilities/obj_table.html' with bulk_delete_url="plugins:phonebox_plugin:pbx_bulk_delete" bulk_edit_url="plugins:phonebox_plugin:pbx_bulk_edit" %} +
+
+ {% include 'inc/search_panel.html' %} +
+
+{% endblock %} \ No newline at end of file diff --git a/phonebox_plugin/templates/phonebox_plugin/pbx_list_3.x.html b/phonebox_plugin/templates/phonebox_plugin/pbx_list_3.x.html new file mode 100644 index 0000000..6b00bdf --- /dev/null +++ b/phonebox_plugin/templates/phonebox_plugin/pbx_list_3.x.html @@ -0,0 +1,62 @@ +{% extends 'base/layout.html' %} +{% load buttons %} +{% load static %} +{% load plugins %} +{% load helpers %} + + +{% block controls %} + +
+ {% if perms.phonebox_plugin.pbx_add %} + + Add + + {% endif %} + {% if perms.phonebox_plugin.pbx_import %} + + Import + + {% endif %} +
+ +{% endblock %} + + +{% block content %} + + + +
+
+ + {# Applied filters #} + {% if filter_form %} + {% applied_filters filter_form request.GET %} + {% endif %} + +

{% block title %}PBX{% endblock %}

+
+
+ {% include 'phonebox_plugin/obj_table.html' with bulk_delete_url="plugins:phonebox_plugin:pbx_bulk_delete" bulk_edit_url="plugins:phonebox_plugin:pbx_bulk_edit" %} +
+
+
+
+ {% include 'inc/filter_list.html' %} +
+
+ +{% endblock %} \ No newline at end of file diff --git a/phonebox_plugin/templates/phonebox_plugin/tags_panel.html b/phonebox_plugin/templates/phonebox_plugin/tags_panel.html index e67098c..4e53ad2 100644 --- a/phonebox_plugin/templates/phonebox_plugin/tags_panel.html +++ b/phonebox_plugin/templates/phonebox_plugin/tags_panel.html @@ -8,4 +8,4 @@
No tags assigned {% endfor %} - + \ No newline at end of file diff --git a/phonebox_plugin/templates/phonebox_plugin/voice_circuit.html b/phonebox_plugin/templates/phonebox_plugin/voice_circuit.html index 83a2d33..80ecef2 100644 --- a/phonebox_plugin/templates/phonebox_plugin/voice_circuit.html +++ b/phonebox_plugin/templates/phonebox_plugin/voice_circuit.html @@ -6,12 +6,12 @@
-
+
@@ -119,7 +119,7 @@

{% block title %}{{ object }}{% endblock %}

- {% include 'extras/inc/tags_panel.html' with tags=object.tags.all url='plugins:phonebox_plugin:list_view' %} + {% include 'extras/inc/tags_panel.html' with tags=object.tags.all url='plugins:phonebox_plugin:voice_circuit_list' %}
diff --git a/phonebox_plugin/templates/phonebox_plugin/voice_circuit_3.x.html b/phonebox_plugin/templates/phonebox_plugin/voice_circuit_3.x.html index 0ade239..487848d 100644 --- a/phonebox_plugin/templates/phonebox_plugin/voice_circuit_3.x.html +++ b/phonebox_plugin/templates/phonebox_plugin/voice_circuit_3.x.html @@ -5,11 +5,11 @@
- +
@@ -120,7 +120,7 @@
- {% include 'phonebox_plugin/tags_panel.html' with tags=object.tags.all url='plugins:phonebox_plugin:voice_circuit_list_view' %} + {% include 'phonebox_plugin/tags_panel.html' with tags=object.tags.all url='plugins:phonebox_plugin:voice_circuit_list' %}
{% if object.voice_circuit_type == "sip_trunk" %}
@@ -167,4 +167,4 @@
{% endif %}
-{% endblock content %} +{% endblock content %} \ No newline at end of file diff --git a/phonebox_plugin/templates/phonebox_plugin/add_voice_circuit.html b/phonebox_plugin/templates/phonebox_plugin/voice_circuit_add.html similarity index 99% rename from phonebox_plugin/templates/phonebox_plugin/add_voice_circuit.html rename to phonebox_plugin/templates/phonebox_plugin/voice_circuit_add.html index c5a83e5..692daa2 100644 --- a/phonebox_plugin/templates/phonebox_plugin/add_voice_circuit.html +++ b/phonebox_plugin/templates/phonebox_plugin/voice_circuit_add.html @@ -2,7 +2,6 @@ {% load static %} {% load form_helpers %} - {% block header %} {{ block.super }} {% endblock %} - {% block form %}
@@ -36,6 +34,7 @@
{% render_field form.name %} {% render_field form.voice_circuit_type %} + {% render_field form.pbx %} {% render_field form.tenant %} {% render_field form.site %} {% render_field form.region %} diff --git a/phonebox_plugin/templates/phonebox_plugin/add_voice_circuit_3.x.html b/phonebox_plugin/templates/phonebox_plugin/voice_circuit_add_3.x.html similarity index 96% rename from phonebox_plugin/templates/phonebox_plugin/add_voice_circuit_3.x.html rename to phonebox_plugin/templates/phonebox_plugin/voice_circuit_add_3.x.html index c4762f5..dbba87e 100644 --- a/phonebox_plugin/templates/phonebox_plugin/add_voice_circuit_3.x.html +++ b/phonebox_plugin/templates/phonebox_plugin/voice_circuit_add_3.x.html @@ -34,6 +34,7 @@
{% render_field form.name %} {% render_field form.voice_circuit_type %} + {% render_field form.pbx %} {% render_field form.tenant %} {% render_field form.site %} {% render_field form.region %} @@ -91,9 +92,10 @@
Interface Assignment
{% endwith %}
-
-
- SIP Trunk Details + +
+
+
SIP Trunk Details
{% render_field form.sip_source %} diff --git a/phonebox_plugin/templates/phonebox_plugin/voice_circuit_list_view.html b/phonebox_plugin/templates/phonebox_plugin/voice_circuit_list.html similarity index 75% rename from phonebox_plugin/templates/phonebox_plugin/voice_circuit_list_view.html rename to phonebox_plugin/templates/phonebox_plugin/voice_circuit_list.html index 29a55d9..d70fb97 100644 --- a/phonebox_plugin/templates/phonebox_plugin/voice_circuit_list_view.html +++ b/phonebox_plugin/templates/phonebox_plugin/voice_circuit_list.html @@ -3,11 +3,11 @@ {% block content %}
- {% if perms.phonebox_plugin.add_voicecircuit %} - {% add_button 'plugins:phonebox_plugin:add_voice_circuit' %} + {% if perms.phonebox_plugin.voice_circuit_add %} + {% add_button 'plugins:phonebox_plugin:voice_circuit_add' %} {% endif %} {% if permissions.add and 'import' in action_buttons %} - {% import_button 'plugins:phonebox_plugin:import_voice_circuits' %} + {% import_button 'plugins:phonebox_plugin:voice_circuits_import' %} {% endif %}

{% block title %}Voice Circuits{% endblock %}

diff --git a/phonebox_plugin/templates/phonebox_plugin/voice_circuit_list_view_3.x.html b/phonebox_plugin/templates/phonebox_plugin/voice_circuit_list_3.x.html similarity index 91% rename from phonebox_plugin/templates/phonebox_plugin/voice_circuit_list_view_3.x.html rename to phonebox_plugin/templates/phonebox_plugin/voice_circuit_list_3.x.html index dc93dc7..d8d35ad 100644 --- a/phonebox_plugin/templates/phonebox_plugin/voice_circuit_list_view_3.x.html +++ b/phonebox_plugin/templates/phonebox_plugin/voice_circuit_list_3.x.html @@ -9,12 +9,12 @@
{% if perms.phonebox_plugin.add_voice_circuit %} - + Add {% endif %} {% if perms.phonebox_plugin.add_voice_circuit %} - + Import {% endif %} diff --git a/phonebox_plugin/urls.py b/phonebox_plugin/urls.py index 5b8f1df..d7640cb 100644 --- a/phonebox_plugin/urls.py +++ b/phonebox_plugin/urls.py @@ -1,22 +1,51 @@ +"""phonebox_plugin URL Configuration + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/4.0/topics/http/urls/ +Examples: +Function views + 1. Add an import: from my_app import views + 2. Add a URL to urlpatterns: path('', views.home, name='home') +Class-based views + 1. Add an import: from other_app.views import Home + 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') +Including another URLconf + 1. Import the include() function: from django.urls import include, path + 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) +""" +from django.contrib import admin from django.urls import path from . import views - urlpatterns = [ - path("", views.NumberListView.as_view(), name="list_view"), +## Numbers + path("", views.NumberListView.as_view(), name="number_list"), + path("number/add", views.NumberEditView.as_view(), name="number_add"), path("number//", views.NumberView.as_view(), name="number_view"), - path("add_number/", views.NumberEditView.as_view(), name="add_number"), - path('import_numbers/', views.NumberBulkImportView.as_view(), name='import_numbers'), - path("/edit/", views.NumberEditView.as_view(), name="number_edit"), + path("number//edit/", views.NumberEditView.as_view(), name="number_edit"), + path("number//delete/", views.NumberDeleteView.as_view(), name="number_delete"), + path("number/list", views.NumberListView.as_view(), name="number_list"), + path('number/import', views.NumberBulkImportView.as_view(), name='number_import'), path("number_bulk_edit/", views.NumberBulkEditView.as_view(), name="number_bulk_edit"), - path("/delete/", views.NumberDeleteView.as_view(), name="number_delete"), path("number_bulk_delete/", views.NumberBulkDeleteView.as_view(), name="number_bulk_delete"), + +## Voice_circuits + path("voice_circuit/add", views.VoiceCircuitEditView.as_view(), name="voice_circuit_add"), path("voice_circuit//", views.VoiceCircuitView.as_view(), name="voice_circuit_view"), - path("voice_circuit_list_view/", views.VoiceCircuitListView.as_view(), name="voice_circuit_list_view"), - path("add_voice_circuit/", views.VoiceCircuitEditView.as_view(), name="add_voice_circuit"), - path('import_voice_circuits/', views.VoiceCircuitBulkImportView.as_view(), name='import_voice_circuits'), path("voice_circuit//edit/", views.VoiceCircuitEditView.as_view(), name="voice_circuit_edit"), - path("voice_circuit_bulk_edit/", views.VoiceCircuitBulkEditView.as_view(), name="voice_circuit_bulk_edit"), path("voice_circuit//delete/", views.VoiceCircuitDeleteView.as_view(), name="voice_circuit_delete"), + path("voice_circuit/list", views.VoiceCircuitListView.as_view(), name="voice_circuit_list"), + path('voice_circuit/import', views.VoiceCircuitBulkImportView.as_view(), name='voice_circuit_import'), + path("voice_circuit_bulk_edit/", views.VoiceCircuitBulkEditView.as_view(), name="voice_circuit_bulk_edit"), path("voice_circuit_bulk_delete/", views.VoiceCircuitBulkDeleteView.as_view(), name="voice_circuit_bulk_delete"), -] + +## PBX + path("pbx/add", views.PBXEditView.as_view(), name="pbx_add"), + path("pbx//", views.PBXView.as_view(), name="pbx_view"), + path("pbx//edit/", views.PBXEditView.as_view(), name="pbx_edit"), + path("pbx//delete/", views.PBXDeleteView.as_view(), name="pbx_delete"), + path("pbx/list", views.PBXListView.as_view(), name="pbx_list"), + path('pbx/import', views.PBXBulkImportView.as_view(), name='pbx_import'), + path("pbx_bulk_edit/", views.PBXBulkEditView.as_view(), name="pbx_bulk_edit"), + path("pbx_bulk_delete/", views.PBXBulkDeleteView.as_view(), name="pbx_bulk_delete"), +] \ No newline at end of file diff --git a/phonebox_plugin/views.py b/phonebox_plugin/views.py index c64c3d3..4dd5b86 100644 --- a/phonebox_plugin/views.py +++ b/phonebox_plugin/views.py @@ -1,27 +1,26 @@ #!./venv/bin/python from netbox.views import generic -from .models import Number, VoiceCircuit -from . import filters -from . import forms -from . import tables - -from django.conf import settings -from packaging import version +from .models import Number, VoiceCircuit, PBX +from . import filters +from . import forms +from . import tables +from django.conf import settings +from packaging import version NETBOX_CURRENT_VERSION = version.parse(settings.VERSION) - +# NUMBERS class NumberListView(generic.ObjectListView): queryset = Number.objects.all() filterset = filters.NumberFilterSet filterset_form = forms.NumberFilterForm table = tables.NumberTable if NETBOX_CURRENT_VERSION >= version.parse("3.0"): - template_name = "phonebox_plugin/list_view_3.x.html" + template_name = "phonebox_plugin/number_list_3.x.html" else: - template_name = "phonebox_plugin/list_view.html" + template_name = "phonebox_plugin/number_list.html" class NumberView(generic.ObjectView): @@ -41,9 +40,9 @@ class NumberEditView(generic.ObjectEditView): model_form = forms.NumberEditForm if NETBOX_CURRENT_VERSION >= version.parse("3.0"): - template_name = "phonebox_plugin/add_number_3.x.html" + template_name = "phonebox_plugin/number_add_3.x.html" else: - template_name = "phonebox_plugin/add_number.html" + template_name = "phonebox_plugin/number_add.html" class NumberBulkEditView(generic.BulkEditView): @@ -55,14 +54,14 @@ class NumberBulkEditView(generic.BulkEditView): class NumberDeleteView(generic.ObjectDeleteView): queryset = Number.objects.all() - default_return_url = "plugins:phonebox_plugin:list_view" + default_return_url = "plugins:phonebox_plugin:number_list" class NumberBulkDeleteView(generic.BulkDeleteView): queryset = Number.objects.filter() filterset = filters.NumberFilterSet table = tables.NumberTable - default_return_url = "plugins:phonebox_plugin:list_view" + default_return_url = "plugins:phonebox_plugin:number_list" class NumberBulkImportView(generic.BulkImportView): @@ -70,16 +69,16 @@ class NumberBulkImportView(generic.BulkImportView): model_form = forms.NumberCSVForm table = tables.NumberTable - +# VOICE CIRCUITS class VoiceCircuitListView(generic.ObjectListView): queryset = VoiceCircuit.objects.all() filterset = filters.VoiceCircuitFilterSet filterset_form = forms.VoiceCircuitFilterForm table = tables.VoiceCircuitTable if NETBOX_CURRENT_VERSION >= version.parse("3.0"): - template_name = "phonebox_plugin/voice_circuit_list_view_3.x.html" + template_name = "phonebox_plugin/voice_circuit_list_3.x.html" else: - template_name = "phonebox_plugin/voice_circuit_list_view.html" + template_name = "phonebox_plugin/voice_circuit_list.html" class VoiceCircuitView(generic.ObjectView): @@ -92,16 +91,16 @@ class VoiceCircuitView(generic.ObjectView): class VoiceCircuitEditView(generic.ObjectEditView): queryset = VoiceCircuit.objects.all() - + if NETBOX_CURRENT_VERSION >= version.parse("3.2"): form = forms.VoiceCircuitEditForm else: model_form = forms.VoiceCircuitEditForm if NETBOX_CURRENT_VERSION >= version.parse("3.0"): - template_name = "phonebox_plugin/add_voice_circuit_3.x.html" + template_name = "phonebox_plugin/voice_circuit_add_3.x.html" else: - template_name = "phonebox_plugin/add_voice_circuit.html" + template_name = "phonebox_plugin/voice_circuit_add.html" class VoiceCircuitBulkEditView(generic.BulkEditView): @@ -113,17 +112,75 @@ class VoiceCircuitBulkEditView(generic.BulkEditView): class VoiceCircuitDeleteView(generic.ObjectDeleteView): queryset = VoiceCircuit.objects.all() - default_return_url = "plugins:phonebox_plugin:voice_circuit_list_view" + default_return_url = "plugins:phonebox_plugin:voice_circuit_list" class VoiceCircuitBulkDeleteView(generic.BulkDeleteView): queryset = VoiceCircuit.objects.filter() filterset = filters.VoiceCircuitFilterSet table = tables.VoiceCircuitTable - default_return_url = "plugins:phonebox_plugin:voice_circuit_list_view" + default_return_url = "plugins:phonebox_plugin:voice_circuit_list" class VoiceCircuitBulkImportView(generic.BulkImportView): queryset = VoiceCircuit.objects.all() model_form = forms.VoiceCircuitCSVForm table = tables.VoiceCircuitTable + +# PBX'S +class PBXListView(generic.ObjectListView): + queryset = PBX.objects.all() + filterset = filters.PbxTelephonyFilterSet + filterset_form = forms.PBXFilterForm + table = tables.PBXTable + if NETBOX_CURRENT_VERSION >= version.parse("3.0"): + template_name = "phonebox_plugin/pbx_list_3.x.html" + else: + template_name = "phonebox_plugin/pbx_list.html" + + +class PBXView(generic.ObjectView): + queryset = PBX.objects.prefetch_related('tenant') + if NETBOX_CURRENT_VERSION >= version.parse("3.0"): + template_name = "phonebox_plugin/pbx_3.x.html" + else: + template_name = "phonebox_plugin/pbx.html" + + +class PBXEditView(generic.ObjectEditView): + queryset = PBX.objects.all() + + if NETBOX_CURRENT_VERSION >= version.parse("3.2"): + form = forms.PBXEditForm + else: + model_form = forms.PBXEditForm + + if NETBOX_CURRENT_VERSION >= version.parse("3.0"): + template_name = "phonebox_plugin/pbx_add_3.x.html" + else: + template_name = "phonebox_plugin/pbx_add.html" + + +class PBXBulkEditView(generic.BulkEditView): + queryset = PBX.objects.prefetch_related('tenant') + filterset = filters.PbxTelephonyFilterSet + table = tables.PBXTable + form = forms.PBXBulkEditForm + + +class PBXDeleteView(generic.ObjectDeleteView): + queryset = PBX.objects.all() + default_return_url = "plugins:phonebox_plugin:pbx_list" + + +class PBXBulkDeleteView(generic.BulkDeleteView): + queryset = PBX.objects.filter() + filterset = filters.PbxTelephonyFilterSet + table = tables.PBXTable + default_return_url = "plugins:phonebox_plugin:pbx_list" + + +class PBXBulkImportView(generic.BulkImportView): + queryset = PBX.objects.all() + model_form = forms.PBXCSVForm + table = tables.PBXTable \ No newline at end of file diff --git a/setup.py b/setup.py index 3157953..c16f0f1 100644 --- a/setup.py +++ b/setup.py @@ -7,9 +7,9 @@ setup( name='phonebox_plugin', - version='v0.0.4-beta.1', + version='v0.0.6-beta.1', url='https://github.com/iDebugAll/phonebox-plugin.git', - download_url='https://github.com/iDebugAll/phonebox-plugin/archive/v0.0.4-beta.1.tar.gz', + download_url='https://github.com/iDebugAll/phonebox-plugin/archive/v0.0.6-beta.1.tar.gz', description='A phone numbers management plugin for NetBox.', long_description=long_description, long_description_content_type='text/markdown',