initial commit
This commit is contained in:
commit
ce809235e5
34 changed files with 736 additions and 0 deletions
0
settings/__init__.py
Normal file
0
settings/__init__.py
Normal file
30
settings/admin.py
Normal file
30
settings/admin.py
Normal file
|
@ -0,0 +1,30 @@
|
|||
from django.contrib import admin
|
||||
|
||||
from .models import SIPAccount, SnomPhone, SnomPhoneType, SnomFunctionKey
|
||||
|
||||
|
||||
class SnomFunctionKeyInline(admin.TabularInline):
|
||||
model = SnomFunctionKey
|
||||
|
||||
|
||||
@admin.display(description="SIP Account")
|
||||
def sip_username_ip(obj):
|
||||
return f"{obj.username}@{obj.ip}"
|
||||
|
||||
|
||||
@admin.register(SIPAccount)
|
||||
class SIPAccountAdmin(admin.ModelAdmin):
|
||||
list_display = (sip_username_ip, 'display_name', 'tone_scheme')
|
||||
list_filter = ('ip', 'tone_scheme')
|
||||
|
||||
|
||||
@admin.register(SnomPhone)
|
||||
class SnomPhoneAdmin(admin.ModelAdmin):
|
||||
list_display = ('phone_name', 'mac_address', 'sip_account')
|
||||
|
||||
|
||||
@admin.register(SnomPhoneType)
|
||||
class SnomPhoneTypeAdmin(admin.ModelAdmin):
|
||||
inlines = [
|
||||
SnomFunctionKeyInline
|
||||
]
|
6
settings/apps.py
Normal file
6
settings/apps.py
Normal file
|
@ -0,0 +1,6 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class SettingsConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'settings'
|
37
settings/migrations/0001_initial.py
Normal file
37
settings/migrations/0001_initial.py
Normal file
|
@ -0,0 +1,37 @@
|
|||
# Generated by Django 5.2.3 on 2025-06-15 08:00
|
||||
|
||||
import django.core.validators
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='SIPAccount',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('ip', models.GenericIPAddressField()),
|
||||
('username', models.CharField(max_length=255)),
|
||||
('password', models.CharField(max_length=255)),
|
||||
('display_name', models.CharField(max_length=255)),
|
||||
('tone_scheme', models.CharField(max_length=10)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='SnomPhone',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('mac_address', models.CharField(max_length=12, unique=True, validators=[django.core.validators.RegexValidator('^[0-9A-F]{12}$')])),
|
||||
('phone_name', models.CharField(max_length=255)),
|
||||
('admin_password', models.CharField(max_length=255)),
|
||||
('sip_account', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='settings.sipaccount')),
|
||||
],
|
||||
),
|
||||
]
|
|
@ -0,0 +1,36 @@
|
|||
# Generated by Django 5.2.3 on 2025-06-15 08:16
|
||||
|
||||
import django.core.validators
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('settings', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='sipaccount',
|
||||
options={'ordering': ['display_name'], 'verbose_name': 'SIP Account', 'verbose_name_plural': 'SIP Accounts'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='snomphone',
|
||||
options={'ordering': ['phone_name'], 'verbose_name': 'Snom Phone', 'verbose_name_plural': 'Snom Phones'},
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='snomphone',
|
||||
name='id',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='snomphone',
|
||||
name='timezone',
|
||||
field=models.CharField(default='GBR-0', help_text='https://service.snom.com/display/wiki/timezone', max_length=10),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='snomphone',
|
||||
name='mac_address',
|
||||
field=models.CharField(max_length=12, primary_key=True, serialize=False, validators=[django.core.validators.RegexValidator('^[0-9A-F]{12}$')]),
|
||||
),
|
||||
]
|
35
settings/migrations/0003_snomphonetype_snomfunctionkey.py
Normal file
35
settings/migrations/0003_snomphonetype_snomfunctionkey.py
Normal file
|
@ -0,0 +1,35 @@
|
|||
# Generated by Django 5.2.3 on 2025-06-15 08:43
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('settings', '0002_alter_sipaccount_options_alter_snomphone_options_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='SnomPhoneType',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('model', models.CharField(max_length=255)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='SnomFunctionKey',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('key_id', models.IntegerField()),
|
||||
('label', models.CharField(max_length=255)),
|
||||
('value', models.CharField(max_length=255)),
|
||||
('phone_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='settings.snomphonetype')),
|
||||
],
|
||||
options={
|
||||
'ordering': ['phone_type', 'key_id'],
|
||||
'constraints': [models.UniqueConstraint(fields=('phone_type', 'key_id'), name='phone_type_key_id_unique')],
|
||||
},
|
||||
),
|
||||
]
|
20
settings/migrations/0004_snomphone_phone_type.py
Normal file
20
settings/migrations/0004_snomphone_phone_type.py
Normal file
|
@ -0,0 +1,20 @@
|
|||
# Generated by Django 5.2.3 on 2025-06-15 08:50
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('settings', '0003_snomphonetype_snomfunctionkey'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='snomphone',
|
||||
name='phone_type',
|
||||
field=models.ForeignKey(default=0, on_delete=django.db.models.deletion.PROTECT, to='settings.snomphonetype'),
|
||||
preserve_default=False,
|
||||
),
|
||||
]
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 5.2.3 on 2025-06-15 09:08
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('settings', '0004_snomphone_phone_type'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='snomphonetype',
|
||||
name='phonebook_function_key',
|
||||
field=models.IntegerField(default=-1, help_text='if set to anything >+0, phonebook will be provisioned on this key'),
|
||||
),
|
||||
]
|
0
settings/migrations/__init__.py
Normal file
0
settings/migrations/__init__.py
Normal file
64
settings/models.py
Normal file
64
settings/models.py
Normal file
|
@ -0,0 +1,64 @@
|
|||
from django.db import models
|
||||
from django.core.validators import RegexValidator
|
||||
|
||||
|
||||
class SIPAccount(models.Model):
|
||||
ip = models.GenericIPAddressField()
|
||||
username = models.CharField(max_length=255)
|
||||
password = models.CharField(max_length=255)
|
||||
display_name = models.CharField(max_length=255)
|
||||
|
||||
tone_scheme = models.CharField(max_length=10)
|
||||
|
||||
def __str__(self):
|
||||
return f'{self.username}@{self.ip}'
|
||||
|
||||
class Meta:
|
||||
verbose_name = "SIP Account"
|
||||
verbose_name_plural = "SIP Accounts"
|
||||
ordering = ['display_name']
|
||||
|
||||
|
||||
class SnomPhoneType(models.Model):
|
||||
model = models.CharField(max_length=255)
|
||||
phonebook_function_key = models.IntegerField(default=-1, help_text='if set to anything >+0, phonebook will be provisioned on this key')
|
||||
|
||||
def __str__(self):
|
||||
return self.model
|
||||
|
||||
|
||||
class SnomFunctionKey(models.Model):
|
||||
phone_type = models.ForeignKey(SnomPhoneType, on_delete=models.CASCADE)
|
||||
key_id = models.IntegerField()
|
||||
|
||||
label = models.CharField(max_length=255)
|
||||
value = models.CharField(max_length=255)
|
||||
|
||||
def __str__(self):
|
||||
return f'{self.phone_type.model}:{self.key_id}'
|
||||
|
||||
class Meta:
|
||||
ordering = ['phone_type', 'key_id']
|
||||
|
||||
constraints = [
|
||||
models.UniqueConstraint(fields=['phone_type', 'key_id'], name='phone_type_key_id_unique')
|
||||
]
|
||||
|
||||
|
||||
class SnomPhone(models.Model):
|
||||
mac_address = models.CharField(max_length=12, validators=[RegexValidator('^[0-9A-F]{12}$')], primary_key=True)
|
||||
phone_name = models.CharField(max_length=255)
|
||||
admin_password = models.CharField(max_length=255)
|
||||
timezone = models.CharField(max_length=10, help_text="https://service.snom.com/display/wiki/timezone", default="GBR-0")
|
||||
|
||||
sip_account = models.ForeignKey(SIPAccount, on_delete=models.PROTECT)
|
||||
phone_type = models.ForeignKey(SnomPhoneType, on_delete=models.PROTECT)
|
||||
|
||||
def __str__(self):
|
||||
return self.phone_name
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Snom Phone"
|
||||
verbose_name_plural = "Snom Phones"
|
||||
ordering = ['phone_name']
|
||||
|
35
settings/templates/settings/settings.xml
Normal file
35
settings/templates/settings/settings.xml
Normal file
|
@ -0,0 +1,35 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<settings>
|
||||
<phone-settings>
|
||||
<update_policy perm="R">auto_update</update_policy>
|
||||
<phone_name perm="R">{{ settings.phone_name }}</phone_name>
|
||||
<language perm="">English</language>
|
||||
<web_language perm="">English</web_language>
|
||||
<date_us_format perm="R">off</date_us_format>
|
||||
<dialnumber_us_format perm="R">off</dialnumber_us_format>
|
||||
<timezone>{{ settings.timezone }}</timezone>
|
||||
<time_server perm="R">pool.ntp.org</time_server>
|
||||
<tone_scheme perm="R">{{ settings.sip_account.tone_scheme }}</tone_scheme>
|
||||
|
||||
<user_name idx="1" perm="R">{{ settings.sip_account.username }}</user_name>
|
||||
<user_pass idx="1" perm="R">{{ settings.sip_account.password }}</user_pass>
|
||||
<user_host idx="1" perm="R">{{ settings.sip_account.ip }}</user_host>
|
||||
<user_realname idx="1" perm="R">{{ settings.sip_account.display_name }}</user_realname>
|
||||
<user_active idx="1" perm="R">on</user_active>
|
||||
|
||||
<functionKeys e="2">
|
||||
{% for key in function_keys.all %}
|
||||
{% if settings.phone_type.phonebook_function_key != key.key_id %}
|
||||
<fkey idx="{{ key.key_id }}" context="active" label="{{ key.label}}" lp="on" default_text="$name" perm="R">{{ key.value }}</fkey>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% if settings.phone_type.phonebook_function_key >= 0 %}
|
||||
<fkey idx="{{ settings.phone_type.phonebook_function_key }}" context="active" label="Phonebook" lp="on" default_text="$name" perm="R">url {{ phonebook_url }}</fkey>
|
||||
{% endif %}
|
||||
</functionKeys>
|
||||
|
||||
<admin_mode_password perm="R">{{ settings.admin_password }}</admin_mode_password>
|
||||
<http_user perm="R">snom</http_user>
|
||||
<http_pass perm="R">{{ settings.admin_password }}</http_pass>
|
||||
</phone-settings>
|
||||
</settings>
|
3
settings/tests.py
Normal file
3
settings/tests.py
Normal file
|
@ -0,0 +1,3 @@
|
|||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
7
settings/urls.py
Normal file
7
settings/urls.py
Normal file
|
@ -0,0 +1,7 @@
|
|||
from django.urls import path
|
||||
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
path("settings-<str:mac_address>.xml", views.mac_settings, name="mac_settings"),
|
||||
]
|
17
settings/views.py
Normal file
17
settings/views.py
Normal file
|
@ -0,0 +1,17 @@
|
|||
from django.shortcuts import get_object_or_404, render
|
||||
from settings.models import SnomPhone, SnomFunctionKey
|
||||
from django.urls import reverse
|
||||
|
||||
|
||||
def mac_settings(request, mac_address):
|
||||
settings = get_object_or_404(SnomPhone, mac_address=mac_address)
|
||||
function_keys = SnomFunctionKey.objects.filter(phone_type=settings.phone_type)
|
||||
|
||||
phonebook_url = request.build_absolute_uri(reverse("phonebook_index"))
|
||||
|
||||
context = {
|
||||
"settings": settings,
|
||||
"function_keys": function_keys,
|
||||
"phonebook_url": phonebook_url,
|
||||
}
|
||||
return render(request, "settings/settings.xml", context, content_type="text/xml")
|
Loading…
Add table
Add a link
Reference in a new issue