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 @@
- {% 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 @@
-{% 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 %}
+
+
+
+ PBX
+ {{ object }}
+
+
+
+
+
+ {% if perms.phonebox_plugin.pbx_change %}
+
+ Edit
+
+ {% endif %}
+ {% if perms.phonebox_plugin.pbx_delete %}
+
+ Delete
+
+ {% endif %}
+
+
{% block title %}{{ object }}{% endblock %}
+
+{% endblock %}
+
+{% block content %}
+
+
+
+ {% 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 %}
+
+
+
+ {% include 'phonebox_plugin/tags_panel.html' with tags=object.tags.all url='plugins:phonebox_plugin:pbx_list' %}
+
+
+ {% if object.pbx_type == "voip_pbx" %}
+
+
+
+
+
+
+ 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" %}
+
+ {% endif %}
+ {% if object.pbx_type == "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 %}
+
+
+
+
+
+ Device
+
+
+
+
+ Virtual Machine
+
+
+
+
+
+
+
+ {% 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 %}
+
+
+
+
+ PBX
+
+
+
+
+ Filters
+ {% if filter_form %}{% badge filter_form.changed_data|length %}{% endif %}
+
+
+
+
+
+
+
+ {# 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 @@