initial commit
This commit is contained in:
commit
ce809235e5
34 changed files with 736 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
db.sqlite3
|
2
README.md
Normal file
2
README.md
Normal file
|
@ -0,0 +1,2 @@
|
|||
https://service.snom.com/display/wiki/DHCP+options#DHCPoptions-Auto-provisioningoptions
|
||||
|
23
manage.py
Executable file
23
manage.py
Executable file
|
@ -0,0 +1,23 @@
|
|||
#!/usr/bin/env python
|
||||
"""Django's command-line utility for administrative tasks."""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
def main():
|
||||
"""Run administrative tasks."""
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "snom.settings")
|
||||
try:
|
||||
from django.core.management import execute_from_command_line
|
||||
except ImportError as exc:
|
||||
raise ImportError(
|
||||
"Couldn't import Django. Are you sure it's installed and "
|
||||
"available on your PYTHONPATH environment variable? Did you "
|
||||
"forget to activate a virtual environment?"
|
||||
) from exc
|
||||
execute_from_command_line(sys.argv)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
0
phonebook/__init__.py
Normal file
0
phonebook/__init__.py
Normal file
27
phonebook/admin.py
Normal file
27
phonebook/admin.py
Normal file
|
@ -0,0 +1,27 @@
|
|||
from django.contrib import admin
|
||||
|
||||
from .models import PhonebookEntry, PhonebookNumber
|
||||
|
||||
|
||||
class PhonebookNumberInline(admin.TabularInline):
|
||||
model = PhonebookNumber
|
||||
|
||||
|
||||
@admin.display(description="Name")
|
||||
def first_and_last_name(obj):
|
||||
return f"{obj.first_name} {obj.last_name}"
|
||||
|
||||
|
||||
@admin.register(PhonebookEntry)
|
||||
class PhonebookEntryAdmin(admin.ModelAdmin):
|
||||
list_display = (first_and_last_name, "enabled", "favourite", "vip", "blocked")
|
||||
list_filter = ("first_name", "last_name", "enabled", "favourite", "vip", "blocked")
|
||||
|
||||
inlines = [
|
||||
PhonebookNumberInline,
|
||||
]
|
||||
|
||||
|
||||
@admin.register(PhonebookNumber)
|
||||
class PhonebookNumberAdmin(admin.ModelAdmin):
|
||||
pass
|
6
phonebook/apps.py
Normal file
6
phonebook/apps.py
Normal file
|
@ -0,0 +1,6 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class PhonebookConfig(AppConfig):
|
||||
default_auto_field = "django.db.models.BigAutoField"
|
||||
name = "phonebook"
|
71
phonebook/migrations/0001_initial.py
Normal file
71
phonebook/migrations/0001_initial.py
Normal file
|
@ -0,0 +1,71 @@
|
|||
# Generated by Django 5.2.3 on 2025-06-15 06:48
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
initial = True
|
||||
|
||||
dependencies = []
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="PhonebookEntry",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("first_name", models.CharField(max_length=255)),
|
||||
("last_name", models.CharField(max_length=255)),
|
||||
("enabled", models.BooleanField(default=True)),
|
||||
("blocked", models.BooleanField(default=False)),
|
||||
("favourite", models.BooleanField(default=False)),
|
||||
("vip", models.BooleanField(default=False)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="PhonebookNumber",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("number", models.IntegerField()),
|
||||
(
|
||||
"type",
|
||||
models.CharField(
|
||||
choices=[
|
||||
("SIP", "SIP"),
|
||||
("MOBILE", "Mobile"),
|
||||
("FIXED", "Fixed"),
|
||||
("HOME", "Home"),
|
||||
("BUSINESS", "Business"),
|
||||
("EXTENSION", "Extension"),
|
||||
],
|
||||
default="SIP",
|
||||
max_length=20,
|
||||
),
|
||||
),
|
||||
(
|
||||
"contact",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="numbers",
|
||||
to="phonebook.phonebookentry",
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
]
|
|
@ -0,0 +1,31 @@
|
|||
# Generated by Django 5.2.3 on 2025-06-15 06:53
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("phonebook", "0001_initial"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name="phonebookentry",
|
||||
options={
|
||||
"ordering": ["first_name", "last_name"],
|
||||
"verbose_name_plural": "Phonebook entries",
|
||||
},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name="phonebooknumber",
|
||||
options={
|
||||
"ordering": ["number"],
|
||||
"verbose_name_plural": "Phonebook numbers",
|
||||
},
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="phonebooknumber",
|
||||
name="number",
|
||||
field=models.CharField(max_length=255),
|
||||
),
|
||||
]
|
|
@ -0,0 +1,27 @@
|
|||
# Generated by Django 5.2.3 on 2025-06-15 08:00
|
||||
|
||||
import django.core.validators
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('phonebook', '0002_alter_phonebookentry_options_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='phonebookentry',
|
||||
options={'ordering': ['first_name', 'last_name'], 'verbose_name_plural': 'Phonebook Entries'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='phonebooknumber',
|
||||
options={'ordering': ['number'], 'verbose_name_plural': 'Phonebook Numbers'},
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='phonebooknumber',
|
||||
name='number',
|
||||
field=models.CharField(max_length=255, validators=[django.core.validators.RegexValidator(message='Phone number must be entered in the format: +999999999', regex='^\\+[0-9]+$')]),
|
||||
),
|
||||
]
|
0
phonebook/migrations/__init__.py
Normal file
0
phonebook/migrations/__init__.py
Normal file
55
phonebook/models.py
Normal file
55
phonebook/models.py
Normal file
|
@ -0,0 +1,55 @@
|
|||
from django.db import models
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.core.validators import RegexValidator
|
||||
|
||||
|
||||
# Create your models here.
|
||||
class PhonebookEntry(models.Model):
|
||||
first_name = models.CharField(max_length=255)
|
||||
last_name = models.CharField(max_length=255)
|
||||
|
||||
enabled = models.BooleanField(default=True)
|
||||
blocked = models.BooleanField(default=False)
|
||||
favourite = models.BooleanField(default=False)
|
||||
vip = models.BooleanField(default=False)
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.first_name} {self.last_name}"
|
||||
|
||||
class Meta:
|
||||
verbose_name_plural = _("Phonebook Entries")
|
||||
ordering = ["first_name", "last_name"]
|
||||
|
||||
|
||||
class PhonebookNumber(models.Model):
|
||||
class ContactTypes(models.TextChoices):
|
||||
SIP = "SIP", _("SIP")
|
||||
MOBILE = "MOBILE", _("Mobile")
|
||||
FIXED = "FIXED", _("Fixed")
|
||||
HOME = "HOME", _("Home")
|
||||
BUSINESS = "BUSINESS", _("Business")
|
||||
EXTENSION = "EXTENSION", _("Extension")
|
||||
|
||||
number = models.CharField(
|
||||
max_length=255,
|
||||
validators=[
|
||||
RegexValidator(
|
||||
regex="^\+[0-9]+$",
|
||||
message="Phone number must be entered in the format: +999999999",
|
||||
)
|
||||
],
|
||||
)
|
||||
type = models.CharField(
|
||||
max_length=20, choices=ContactTypes, default=ContactTypes.SIP
|
||||
)
|
||||
|
||||
contact = models.ForeignKey(
|
||||
PhonebookEntry, on_delete=models.CASCADE, related_name="numbers"
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.number} ({self.contact.first_name} {self.contact.last_name}, {self.type.title()})"
|
||||
|
||||
class Meta:
|
||||
verbose_name_plural = _("Phonebook Numbers")
|
||||
ordering = ["number"]
|
15
phonebook/templates/phonebook/index.xml
Normal file
15
phonebook/templates/phonebook/index.xml
Normal file
|
@ -0,0 +1,15 @@
|
|||
<tbook e='2' version='2.0'>
|
||||
{% for entry in entries %}
|
||||
<contact fav="{{ entry.favourite | lower }}"
|
||||
vip="{{ entry.vip | lower }}"
|
||||
blocked="{{ entry.blocked | lower }}">
|
||||
<first_name>{{ entry.first_name }}</first_name>
|
||||
<last_name>{{ entry.last_name }}</last_name>
|
||||
<numbers>
|
||||
{% for number in entry.numbers.all %}
|
||||
<number no="{{ number.number }}" type="{{ number.type | lower }}" outgoing_id="0"/>
|
||||
{% endfor %}
|
||||
</numbers>
|
||||
</contact>
|
||||
{% endfor %}
|
||||
</tbook>
|
3
phonebook/tests.py
Normal file
3
phonebook/tests.py
Normal file
|
@ -0,0 +1,3 @@
|
|||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
7
phonebook/urls.py
Normal file
7
phonebook/urls.py
Normal file
|
@ -0,0 +1,7 @@
|
|||
from django.urls import path
|
||||
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
path("", views.index, name="phonebook_index"),
|
||||
]
|
8
phonebook/views.py
Normal file
8
phonebook/views.py
Normal file
|
@ -0,0 +1,8 @@
|
|||
from django.shortcuts import render
|
||||
from phonebook.models import PhonebookEntry
|
||||
|
||||
|
||||
def index(request):
|
||||
entries = PhonebookEntry.objects.all().prefetch_related("numbers")
|
||||
context = {"entries": entries}
|
||||
return render(request, "phonebook/index.xml", context, content_type="text/xml")
|
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")
|
0
snom/__init__.py
Normal file
0
snom/__init__.py
Normal file
16
snom/asgi.py
Normal file
16
snom/asgi.py
Normal file
|
@ -0,0 +1,16 @@
|
|||
"""
|
||||
ASGI config for snom project.
|
||||
|
||||
It exposes the ASGI callable as a module-level variable named ``application``.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/5.2/howto/deployment/asgi/
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from django.core.asgi import get_asgi_application
|
||||
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "snom.settings")
|
||||
|
||||
application = get_asgi_application()
|
112
snom/settings.py
Normal file
112
snom/settings.py
Normal file
|
@ -0,0 +1,112 @@
|
|||
from pathlib import Path
|
||||
|
||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||
|
||||
|
||||
# Quick-start development settings - unsuitable for production
|
||||
# See https://docs.djangoproject.com/en/5.2/howto/deployment/checklist/
|
||||
|
||||
# SECURITY WARNING: keep the secret key used in production secret!
|
||||
SECRET_KEY = "django-insecure-rhrs+9%s+#cc)xkqto*(%sv+d2*!@_q=(u2ss+y)w9g5-yp(fc"
|
||||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = True
|
||||
|
||||
ALLOWED_HOSTS = []
|
||||
|
||||
|
||||
# Application definition
|
||||
|
||||
INSTALLED_APPS = [
|
||||
"phonebook.apps.PhonebookConfig",
|
||||
"settings.apps.SettingsConfig",
|
||||
"django.contrib.admin",
|
||||
"django.contrib.auth",
|
||||
"django.contrib.contenttypes",
|
||||
"django.contrib.sessions",
|
||||
"django.contrib.messages",
|
||||
"django.contrib.staticfiles",
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
"django.middleware.security.SecurityMiddleware",
|
||||
"django.contrib.sessions.middleware.SessionMiddleware",
|
||||
"django.middleware.common.CommonMiddleware",
|
||||
"django.middleware.csrf.CsrfViewMiddleware",
|
||||
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
||||
"django.contrib.messages.middleware.MessageMiddleware",
|
||||
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
||||
]
|
||||
|
||||
ROOT_URLCONF = "snom.urls"
|
||||
|
||||
TEMPLATES = [
|
||||
{
|
||||
"BACKEND": "django.template.backends.django.DjangoTemplates",
|
||||
"DIRS": [],
|
||||
"APP_DIRS": True,
|
||||
"OPTIONS": {
|
||||
"context_processors": [
|
||||
"django.template.context_processors.request",
|
||||
"django.contrib.auth.context_processors.auth",
|
||||
"django.contrib.messages.context_processors.messages",
|
||||
],
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
WSGI_APPLICATION = "snom.wsgi.application"
|
||||
|
||||
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/5.2/ref/settings/#databases
|
||||
|
||||
DATABASES = {
|
||||
"default": {
|
||||
"ENGINE": "django.db.backends.sqlite3",
|
||||
"NAME": BASE_DIR / "db.sqlite3",
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Password validation
|
||||
# https://docs.djangoproject.com/en/5.2/ref/settings/#auth-password-validators
|
||||
|
||||
AUTH_PASSWORD_VALIDATORS = [
|
||||
{
|
||||
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
|
||||
},
|
||||
{
|
||||
"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
|
||||
},
|
||||
{
|
||||
"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
|
||||
},
|
||||
{
|
||||
"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/5.2/topics/i18n/
|
||||
|
||||
LANGUAGE_CODE = "en-us"
|
||||
|
||||
TIME_ZONE = "UTC"
|
||||
|
||||
USE_I18N = True
|
||||
|
||||
USE_TZ = True
|
||||
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/5.2/howto/static-files/
|
||||
|
||||
STATIC_URL = "static/"
|
||||
|
||||
# Default primary key field type
|
||||
# https://docs.djangoproject.com/en/5.2/ref/settings/#default-auto-field
|
||||
|
||||
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
|
8
snom/urls.py
Normal file
8
snom/urls.py
Normal file
|
@ -0,0 +1,8 @@
|
|||
from django.contrib import admin
|
||||
from django.urls import include, path
|
||||
|
||||
urlpatterns = [
|
||||
path("phonebook/", include("phonebook.urls")),
|
||||
path("snom/", include("settings.urls")),
|
||||
path("admin/", admin.site.urls),
|
||||
]
|
16
snom/wsgi.py
Normal file
16
snom/wsgi.py
Normal file
|
@ -0,0 +1,16 @@
|
|||
"""
|
||||
WSGI config for snom project.
|
||||
|
||||
It exposes the WSGI callable as a module-level variable named ``application``.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/5.2/howto/deployment/wsgi/
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from django.core.wsgi import get_wsgi_application
|
||||
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "snom.settings")
|
||||
|
||||
application = get_wsgi_application()
|
Loading…
Add table
Add a link
Reference in a new issue