feat(user): Add user settings panel

!11 closes #28
This commit is contained in:
2024-05-25 10:44:49 +09:30
parent 900412b317
commit ee7977fe4a
9 changed files with 273 additions and 3 deletions

View File

@ -4,10 +4,28 @@ from app.urls import urlpatterns
from django.urls import URLPattern, URLResolver
from settings.models.user_settings import UserSettings
def request(request):
return request.get_full_path()
def user_settings(context) -> int:
""" Provides the settings ID for the current user
Returns:
int: model usersettings Primary Key
"""
if context.user.is_authenticated:
settings = UserSettings.objects.filter(user=context.user)
return settings[0].pk
return None
def nav_items(context) -> list(dict()):
""" Fetch All Project URLs
@ -98,4 +116,5 @@ def common(context):
return {
'nav_items': nav_items(context),
'user_settings': user_settings(context),
}

View File

@ -0,0 +1,18 @@
from django.test import TestCase, Client
import pytest
import unittest
import requests
@pytest.mark.skip(reason="to be written")
def test_context_processor_base_user_settings_if_authenticated_only():
""" Context Processor base to only provide `user_settings` for an authenticated user """
pass
@pytest.mark.skip(reason="to be written")
def test_context_processor_base_user_settings_is_logged_in_user():
""" Context Processor base to only provide `user_settings` for the current logged in user """
pass

View File

@ -21,14 +21,21 @@ from django.views.static import serve
from django.urls import include, path, re_path
from .views import home
from core.views import history
from settings.views import user_settings
urlpatterns = [
path('', home.HomeView.as_view(), name='home'),
path('admin/', admin.site.urls, name='_administration'),
path('account/password_change/', auth_views.PasswordChangeView.as_view(template_name="password_change.html.j2"),
name="change_password"),
path('account/password_change/', auth_views.PasswordChangeView.as_view(template_name="password_change.html.j2"), name="change_password"),
path('account/settings/<int:pk>', user_settings.View.as_view(), name="_settings_user"),
path("account/", include("django.contrib.auth.urls")),
path("organization/", include("access.urls")),
path("itam/", include("itam.urls")),
path("history/<str:model_name>/<int:model_pk>", history.View.as_view(), name='_history'),

View File

@ -0,0 +1,32 @@
# Generated by Django 5.0.6 on 2024-05-24 23:50
import access.fields
import django.db.models.deletion
import django.utils.timezone
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('access', '0002_alter_team_organization'),
('settings', '0001_initial'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='UserSettings',
fields=[
('id', models.AutoField(primary_key=True, serialize=False, unique=True)),
('created', access.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False)),
('modified', access.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False)),
('default_organization', models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='access.organization')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
options={
'abstract': False,
},
),
]

View File

@ -0,0 +1,37 @@
# Generated by Django 5.0.6 on 2024-05-24 23:19
import access.fields
import django.db.models.deletion
import django.utils.timezone
from django.conf import settings
from django.db import migrations, models
from django.contrib.auth.models import User
from settings.models.user_settings import UserSettings
def add_user_settings(apps, schema_editor):
for user in User.objects.all():
if not UserSettings.objects.filter(pk=user.id).exists():
user_setting = UserSettings.objects.create(
user=user
)
user_setting.save()
class Migration(migrations.Migration):
dependencies = [
('access', '0002_alter_team_organization'),
('settings', '0002_usersettings'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.RunPython(add_user_settings),
]

View File

@ -0,0 +1,52 @@
from django.contrib.auth.models import User
from django.db import models
from access.fields import *
from access.models import TenancyObject, Organization
from core.mixin.history_save import SaveHistory
class UserSettingsCommonFields(models.Model):
class Meta:
abstract = True
id = models.AutoField(
primary_key=True,
unique=True,
blank=False
)
slug = None
created = AutoCreatedField()
modified = AutoLastModifiedField()
class UserSettings(UserSettingsCommonFields):
user = models.ForeignKey(
User,
on_delete=models.CASCADE,
blank= False,
)
default_organization = models.ForeignKey(
Organization,
on_delete=models.DO_NOTHING,
blank= True,
default = None,
null = True,
)
def is_owner(self, user: int) -> bool:
if user == self.user:
return True
return False

View File

@ -0,0 +1,50 @@
from django.test import TestCase, Client
import pytest
import unittest
import requests
@pytest.mark.skip(reason="to be written")
def test_user_settings_on_create_of_user_settings_add():
""" On Creation of a user their settings are created """
pass
@pytest.mark.skip(reason="to be written")
def test_user_settings_on_delete_of_user_settings_removed():
""" On Delete of a user their settings are removed """
pass
@pytest.mark.skip(reason="to be written")
def test_user_settings_on_delete_organization_default_organization():
""" On Delete of an organization, users default organization set to null """
pass
@pytest.mark.skip(reason="to be written")
def test_user_settings_on_delete_organization_user_settings_not_deleted():
""" On Delete of an organization, users settings are not deleted """
pass
@pytest.mark.skip(reason="to be written")
def test_user_settings_only_owner_can_view():
""" Only owner can access their settings url """
pass
@pytest.mark.skip(reason="to be written")
def test_user_settings_only_owner_can_edit():
""" Only owner and super admin can change user settings """
pass
@pytest.mark.skip(reason="to be written")
def test_user_settings_super_admin_can_edit():
""" Super admin can change user settings """
pass

View File

@ -0,0 +1,49 @@
from django.core.exceptions import PermissionDenied
from django.urls import reverse
from django.views import generic
from access.mixin import OrganizationPermission
from settings.models.user_settings import UserSettings
class View(generic.UpdateView):
context_object_name = "settings"
fields = [
'default_organization',
]
model = UserSettings
template_name = 'form.html.j2'
def form_valid(self, form):
if self.object.is_owner(self.request.user):
return super().form_valid(form)
raise PermissionDenied()
def get_success_url(self, **kwargs):
return reverse('_settings_user', args=(self.kwargs['pk'],))
def get_context_data(self, **kwargs):
if self.object.is_owner(self.request.user):
context = super().get_context_data(**kwargs)
context['content_title'] = 'User Settings'
return context
raise PermissionDenied()

View File

@ -15,9 +15,10 @@
<body>
<header>
<h1><a href="/" style="text-decoration: none; color: inherit;">{% settings_value "SITE_TITLE" %}</a></h1>
{%if user.is_authenticated %}
<div class="dropdown" style="right: 0px; position: fixed; padding-right: 50px;">
<button class="dropbtn">{% block user_name %}{%if user.username %}{{ user.username }}{% else %}My Account{% endif %}{% endblock %}</button>
<div class="dropdown-content">
@ -28,6 +29,10 @@
<button class="accbtn">Admin Panel</button>
</form>
{% endif %}
<form action="{% url '_settings_user' pk=user_settings %}" method="post">
{% csrf_token %}
<button class="accbtn">Settings</button>
</form>
<form action="{% url 'password_change' %}" method="post">
{% csrf_token %}
<button class="accbtn">Change Password</button>
@ -38,6 +43,7 @@
</form>
</div>
</div>
{% endif %}
</header>