feat: add user permissions management

Implemented user permissions management functionality across the system,
enhancing control over category access and operations. This includes a
new DataTable in the user area for permissions, updated model relations
with explicit related names for better query readability, and a
migration to enforce these changes at the database level. Additionally,
the UI now features options for ending category membership, deleting
categories, inviting users, and transferring ownership based on user
roles and permissions. This comprehensive update aims at providing more
granular access control and operational flexibility for category owners
and system administrators.
This commit is contained in:
Kumi 2024-03-16 11:07:58 +01:00
parent 77eaa3be6a
commit c463de0ada
Signed by: kumi
GPG key ID: ECBCC9082395383F
4 changed files with 103 additions and 5 deletions

View file

@ -5,4 +5,5 @@ import { Tab } from 'bootstrap';
import DataTable from 'datatables.net-dt';
let mediaTable = new DataTable('#mediaTable');
let scenesTable = new DataTable('#scenesTable');
let scenesTable = new DataTable('#scenesTable');
let permissionsTable = new DataTable('#permissionsTable');

View file

@ -0,0 +1,26 @@
# Generated by Django 5.0.3 on 2024-03-16 09:54
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('tours', '0015_alter_originalmedia_category_alter_scene_category'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.AlterField(
model_name='categorypermission',
name='category',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='permissions', to='tours.category'),
),
migrations.AlterField(
model_name='categorypermission',
name='user',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='category_permissions', to=settings.AUTH_USER_MODEL),
),
]

View file

@ -49,8 +49,14 @@ class Category(models.Model):
class CategoryPermission(models.Model):
category = models.ForeignKey(Category, on_delete=models.CASCADE)
user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)
category = models.ForeignKey(Category, related_name="permissions", on_delete=models.CASCADE)
user = models.ForeignKey(get_user_model(), related_name="category_permissions", on_delete=models.CASCADE)
# TODO: Permission levels
@property
def role(self) -> str:
return "Content Editor"
class Element(PolymorphicModel):

View file

@ -1,5 +1,13 @@
{% extends "users/base.html" %} {% block content %}
<h4>{{ category.title }}</h4>
<div class="d-flex justify-content-between align-items-center">
<h4>{{ category.title }}</h4>
{% if category in request.user.category_memberships %}
<button type="button" class="btn btn-danger">End category membership</button>
{% endif %}
{% if request.user.is_superuser or request.user == category.owner %}
<button type="button" class="btn btn-danger">Delete category</button>
{% endif %}
</div>
<!-- Nav tabs -->
<ul class="nav nav-tabs" id="myTab" role="tablist">
@ -31,6 +39,22 @@
Uploaded Media
</button>
</li>
{% if request.user == category.owner %}
<li class="nav-item" role="presentation">
<button
class="nav-link"
id="permissions-tab"
data-bs-toggle="tab"
data-bs-target="#permissions"
type="button"
role="tab"
aria-controls="permissions"
aria-selected="false"
>
User Permissions
</button>
</li>
{% endif %}
</ul>
<!-- Tab content -->
@ -100,13 +124,54 @@
</td>
<td>{{ media.title }}</td>
<td>
<!-- Actions like Edit/Delete can go here -->
<!-- TODO: Actions like Edit/Delete will go here -->
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% if request.user.is_superuser or request.user == category.owner %}
<div
class="tab-pane fade"
id="permissions"
role="tabpanel"
aria-labelledby="permissions-tab"
>
<div class="d-flex justify-content-between align-items-center">
<h5>User permissions</h5>
<div>
<button type="button" class="btn btn-primary">Invite user</button>
<button type="button" class="btn btn-primary">Transfer ownership</button>
</div>
</div>
<table id="permissionsTable" class="display">
<thead>
<tr>
<th>User</th>
<th>Role</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<tr>
<td>{{ category.owner }}</td>
<td>Owner</td>
<td></td>
</tr>
{% for permission in category.permissions.all %}
<tr>
<td>{{ permission.user }}</td>
<td>{{ permission.role }}</td>
<td>
<!-- TODO: Actions like Edit/Delete will go here -->
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endif %}
</div>
{% endblock %}