Add new app 'help' for user support

This commit adds a new model that enhances the user profile with an
offline support code, a postal address and an email address to allow
users to reset their profile.

The commit adds to Django admin commands 'populate' and
'reset_offline_code' to maintain the help user profiles from the Django
command line.
This commit is contained in:
Jan Dittberner 2023-04-16 12:20:37 +02:00
parent 4b7e311c62
commit f9ea88cd24
14 changed files with 161 additions and 34 deletions

View file

@ -1,6 +1,8 @@
Changelog Changelog
========= =========
* :feature:`-` add support model for offline account reset codes in new help
app
* :support:`-` remove unused PowerDNS support tables from domains app * :support:`-` remove unused PowerDNS support tables from domains app
* :feature:`-` add impersonation support for superusers * :feature:`-` add impersonation support for superusers
* :support:`-` remove django-braces dependency * :support:`-` remove django-braces dependency

View file

@ -232,6 +232,7 @@ LOCAL_APPS = (
"userdbs", "userdbs",
"hostingpackages", "hostingpackages",
"websites", "websites",
"help",
"contact_form", "contact_form",
) )
@ -276,7 +277,7 @@ LOGGING = {
"formatters": { "formatters": {
"verbose": { "verbose": {
"format": "%(levelname)s %(asctime)s %(name)s " "format": "%(levelname)s %(asctime)s %(name)s "
"%(module)s:%(lineno)d %(process)d %(thread)d %(message)s" "%(module)s:%(lineno)d %(process)d %(thread)d %(message)s"
}, },
"simple": {"format": "%(levelname)s %(name)s:%(lineno)d %(message)s"}, "simple": {"format": "%(levelname)s %(name)s:%(lineno)d %(message)s"},
}, },
@ -404,23 +405,8 @@ if GVA_ENVIRONMENT == "local":
dict( dict(
[ [
(key, {"handlers": ["console"], "level": "DEBUG", "propagate": True}) (key, {"handlers": ["console"], "level": "DEBUG", "propagate": True})
for key in [ for key in LOCAL_APPS
"dashboard", ],
"domains",
"fileservertasks",
"gvacommon",
"gvawebcore",
"hostingpackages",
"ldaptasks",
"managemails",
"mysqltasks",
"osusers",
"pgsqltasks",
"taskresults",
"userdbs",
"websites",
]
]
) )
) )
elif GVA_ENVIRONMENT == "test": elif GVA_ENVIRONMENT == "test":
@ -439,22 +425,7 @@ elif GVA_ENVIRONMENT == "test":
dict( dict(
[ [
(key, {"handlers": ["console"], "level": "ERROR", "propagate": True}) (key, {"handlers": ["console"], "level": "ERROR", "propagate": True})
for key in [ for key in LOCAL_APPS
"dashboard",
"domains",
"fileservertasks",
"gvacommon",
"gvawebcore",
"hostingpackages",
"ldaptasks",
"managemails",
"mysqltasks",
"osusers",
"pgsqltasks",
"taskresults",
"userdbs",
"websites",
]
] ]
) )
) )

View file

View file

@ -0,0 +1,22 @@
from django.contrib import admin
from django.contrib.auth import get_user_model
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.utils.translation import ugettext_lazy as _
from help.models import HelpUser
User = get_user_model()
class HelpUserInline(admin.StackedInline):
model = HelpUser
can_delete = False
readonly_fields = ("offline_account_code",)
class UserAdmin(BaseUserAdmin):
inlines = (HelpUserInline,)
admin.site.unregister(User)
admin.site.register(User, UserAdmin)

View file

@ -0,0 +1,8 @@
from django.apps import AppConfig
from django.utils.translation import gettext_lazy as _
class HelpConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "help"
verbose_name = _("User self help")

View file

@ -0,0 +1,17 @@
from django.contrib.auth import get_user_model
from django.core.management import BaseCommand
from help.models import HelpUser
User = get_user_model()
class Command(BaseCommand):
help = "Populate help user information for existing users"
def handle(self, *args, **options):
for user in User.objects.filter(helpuser=None):
help_user = HelpUser.objects.create(user_id=user.id, email_address=user.email)
help_user.generate_offline_account_code()
help_user.save()
self.stdout.write(f"created offline account code {help_user.offline_account_code} for {user}.")

View file

@ -0,0 +1,29 @@
from django.contrib.auth import get_user_model
from django.core.management import BaseCommand, CommandError
from help.models import HelpUser
User = get_user_model()
class Command(BaseCommand):
help = "Reset offline account reset code for existing users"
def add_arguments(self, parser):
parser.add_argument("users", nargs='+', type=str)
def handle(self, *args, **options):
for name in options["users"]:
try:
user = User.objects.get(username=name)
except User.DoesNotExist:
raise CommandError(f'User {name} does not exist')
help_user = user.helpuser
if help_user is None:
help_user = HelpUser.objects.create(email_address=user.email)
help_user.generate_offline_account_code()
help_user.save()
self.stdout.write(f"generated new offline account reset code {help_user.offline_account_code} for {name}")

View file

@ -0,0 +1,55 @@
# Generated by Django 3.2.18 on 2023-04-16 09:32
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name="HelpUser",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"email_address",
models.EmailField(
help_text="Contact email address", max_length=254
),
),
(
"postal_address",
models.TextField(blank=True, help_text="Contact postal address"),
),
(
"offline_account_code",
models.CharField(
default="",
help_text="Offline account reset code",
max_length=36,
),
),
(
"user",
models.OneToOneField(
on_delete=django.db.models.deletion.CASCADE,
to=settings.AUTH_USER_MODEL,
),
),
],
),
]

View file

@ -0,0 +1,17 @@
import uuid
from django.conf import settings
from django.db import models
from django.utils.translation import ugettext_lazy as _
class HelpUser(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
email_address = models.EmailField(help_text=_("Contact email address"))
postal_address = models.TextField(help_text=_("Contact postal address"), blank=True)
offline_account_code = models.CharField(
help_text=_("Offline account reset code"), max_length=36, default=""
)
def generate_offline_account_code(self):
self.offline_account_code = str(uuid.uuid4())

View file

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

View file

@ -0,0 +1,3 @@
from django.shortcuts import render
# Create your views here.