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:
parent
77eaa3be6a
commit
c463de0ada
4 changed files with 103 additions and 5 deletions
|
@ -6,3 +6,4 @@ import DataTable from 'datatables.net-dt';
|
|||
|
||||
let mediaTable = new DataTable('#mediaTable');
|
||||
let scenesTable = new DataTable('#scenesTable');
|
||||
let permissionsTable = new DataTable('#permissionsTable');
|
|
@ -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),
|
||||
),
|
||||
]
|
|
@ -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):
|
||||
|
|
|
@ -1,5 +1,13 @@
|
|||
{% extends "users/base.html" %} {% block content %}
|
||||
<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 %}
|
||||
|
|
Loading…
Reference in a new issue