Browse Source
feat: implement account role system with custom models, admin integration, and settings
main
feat: implement account role system with custom models, admin integration, and settings
main
9 changed files with 893 additions and 11 deletions
-
1apps/account/admin/__init__.py
-
142apps/account/admin/role.py
-
24apps/account/admin/user.py
-
387apps/account/management/commands/seed_roles.py
-
88apps/account/migrations/0005_role_user_custom_permissions_user_role.py
-
4apps/account/models/__init__.py
-
188apps/account/models/role.py
-
55apps/account/models/user.py
-
15config/settings/base.py
@ -0,0 +1,142 @@ |
|||||
|
from django.contrib import admin |
||||
|
from django.utils.translation import gettext_lazy as _ |
||||
|
from unfold.admin import ModelAdmin |
||||
|
from unfold.decorators import display |
||||
|
|
||||
|
from apps.account.models.role import Role, PERMISSION_CATEGORIES |
||||
|
from utils.admin import project_admin_site |
||||
|
|
||||
|
|
||||
|
class RoleAdmin(ModelAdmin): |
||||
|
list_display = ('name', 'slug', 'is_default', 'is_system', 'users_count', 'updated_at') |
||||
|
list_filter = ('is_default', 'is_system') |
||||
|
search_fields = ('name', 'slug', 'description') |
||||
|
ordering = ('name',) |
||||
|
readonly_fields = ('created_at', 'updated_at') |
||||
|
|
||||
|
fieldsets = ( |
||||
|
(None, { |
||||
|
"fields": ( |
||||
|
("name", "slug"), |
||||
|
("is_default", "is_system"), |
||||
|
"description", |
||||
|
), |
||||
|
}), |
||||
|
(_("Identity & Profiles"), { |
||||
|
"classes": ["tab"], |
||||
|
"fields": ( |
||||
|
("is_searchable", "can_submit_verification"), |
||||
|
("can_view_full_profiles", "can_manage_institutions"), |
||||
|
), |
||||
|
}), |
||||
|
(_("Chat & Communications"), { |
||||
|
"classes": ["tab"], |
||||
|
"fields": ( |
||||
|
("can_send_direct_messages", "can_create_group_chats"), |
||||
|
("can_start_video_calls", "can_moderate_chat"), |
||||
|
("is_chat_muted",), |
||||
|
), |
||||
|
}), |
||||
|
(_("CMS & Articles"), { |
||||
|
"classes": ["tab"], |
||||
|
"fields": ( |
||||
|
("can_create_posts", "can_publish_posts_directly"), |
||||
|
("can_edit_own_posts", "can_delete_own_posts"), |
||||
|
("can_comment",), |
||||
|
), |
||||
|
}), |
||||
|
(_("LMS & Academics"), { |
||||
|
"classes": ["tab"], |
||||
|
"fields": ( |
||||
|
("can_enroll_courses", "can_create_courses"), |
||||
|
("can_publish_courses", "can_issue_certificates"), |
||||
|
("can_grade_submissions", "has_premium_lms_access"), |
||||
|
), |
||||
|
}), |
||||
|
(_("Meetings"), { |
||||
|
"classes": ["tab"], |
||||
|
"fields": ( |
||||
|
("can_request_meetings", "can_accept_meetings"), |
||||
|
), |
||||
|
}), |
||||
|
(_("Events"), { |
||||
|
"classes": ["tab"], |
||||
|
"fields": ( |
||||
|
("can_register_events", "can_create_events"), |
||||
|
("can_publish_events", "can_checkin_attendees"), |
||||
|
("can_export_attendee_list",), |
||||
|
), |
||||
|
}), |
||||
|
(_("Community Projects"), { |
||||
|
"classes": ["tab"], |
||||
|
"fields": ( |
||||
|
("can_volunteer", "can_create_projects"), |
||||
|
("can_manage_project_tasks", "can_approve_projects"), |
||||
|
), |
||||
|
}), |
||||
|
(_("Donations & Charity"), { |
||||
|
"classes": ["tab"], |
||||
|
"fields": ( |
||||
|
("can_make_donations", "can_create_donation_campaigns"), |
||||
|
("can_view_donation_reports",), |
||||
|
), |
||||
|
}), |
||||
|
(_("Support & Tickets"), { |
||||
|
"classes": ["tab"], |
||||
|
"fields": ( |
||||
|
("can_create_tickets", "can_reply_tickets"), |
||||
|
("can_manage_tickets",), |
||||
|
), |
||||
|
}), |
||||
|
(_("Dynamic Forms"), { |
||||
|
"classes": ["tab"], |
||||
|
"fields": ( |
||||
|
("can_submit_forms", "can_create_forms"), |
||||
|
("can_export_form_data",), |
||||
|
), |
||||
|
}), |
||||
|
(_("Diplomacy & Institutional"), { |
||||
|
"classes": ["tab"], |
||||
|
"fields": ( |
||||
|
("can_view_diplomatic_reports", "can_create_diplomatic_reports"), |
||||
|
), |
||||
|
}), |
||||
|
(_("Administration & Security"), { |
||||
|
"classes": ["tab"], |
||||
|
"fields": ( |
||||
|
("can_access_admin_panel", "can_manage_users"), |
||||
|
("can_ban_users", "can_view_analytics"), |
||||
|
), |
||||
|
}), |
||||
|
(_("Metadata"), { |
||||
|
"classes": ["tab"], |
||||
|
"fields": ( |
||||
|
("created_at", "updated_at"), |
||||
|
), |
||||
|
}), |
||||
|
) |
||||
|
|
||||
|
@display(description=_("Assigned Users")) |
||||
|
def users_count(self, obj): |
||||
|
count = obj.users.count() |
||||
|
return f"{count} users" |
||||
|
|
||||
|
def has_add_permission(self, request): |
||||
|
return bool(request.user and request.user.is_authenticated and (request.user.is_staff or request.user.is_superuser or request.user.can_access_admin_panel())) |
||||
|
|
||||
|
def has_change_permission(self, request, obj=None): |
||||
|
return bool(request.user and request.user.is_authenticated and (request.user.is_staff or request.user.is_superuser or request.user.can_access_admin_panel())) |
||||
|
|
||||
|
def has_delete_permission(self, request, obj=None): |
||||
|
if obj and getattr(obj, 'is_system', False): |
||||
|
return False # Protect core default roles from accidental deletion |
||||
|
return bool(request.user and request.user.is_authenticated and (request.user.is_staff or request.user.is_superuser or request.user.can_access_admin_panel())) |
||||
|
|
||||
|
|
||||
|
# Register in both default and custom admin sites |
||||
|
try: |
||||
|
admin.site.register(Role, RoleAdmin) |
||||
|
except admin.sites.AlreadyRegistered: |
||||
|
pass |
||||
|
|
||||
|
project_admin_site.register(Role, RoleAdmin) |
||||
@ -0,0 +1,387 @@ |
|||||
|
from django.core.management.base import BaseCommand |
||||
|
from django.db import transaction |
||||
|
from apps.account.models.role import Role, ALL_PERMISSION_FIELDS |
||||
|
|
||||
|
|
||||
|
DEFAULT_ROLES = [ |
||||
|
{ |
||||
|
"name": "User", |
||||
|
"slug": "user", |
||||
|
"description": "Standard community member with basic profile, messaging, learning, and participation privileges.", |
||||
|
"is_default": True, |
||||
|
"is_system": True, |
||||
|
"permissions": { |
||||
|
"is_searchable": True, |
||||
|
"can_submit_verification": True, |
||||
|
"can_view_full_profiles": True, |
||||
|
"can_manage_institutions": False, |
||||
|
"can_send_direct_messages": True, |
||||
|
"can_create_group_chats": True, |
||||
|
"can_start_video_calls": False, |
||||
|
"can_moderate_chat": False, |
||||
|
"is_chat_muted": False, |
||||
|
"can_create_posts": True, |
||||
|
"can_publish_posts_directly": False, |
||||
|
"can_edit_own_posts": True, |
||||
|
"can_delete_own_posts": True, |
||||
|
"can_comment": True, |
||||
|
"can_enroll_courses": True, |
||||
|
"can_create_courses": False, |
||||
|
"can_publish_courses": False, |
||||
|
"can_issue_certificates": False, |
||||
|
"can_grade_submissions": False, |
||||
|
"has_premium_lms_access": False, |
||||
|
"can_request_meetings": True, |
||||
|
"can_accept_meetings": False, |
||||
|
"can_register_events": True, |
||||
|
"can_create_events": False, |
||||
|
"can_publish_events": False, |
||||
|
"can_checkin_attendees": False, |
||||
|
"can_export_attendee_list": False, |
||||
|
"can_volunteer": True, |
||||
|
"can_create_projects": False, |
||||
|
"can_manage_project_tasks": False, |
||||
|
"can_approve_projects": False, |
||||
|
"can_make_donations": True, |
||||
|
"can_create_donation_campaigns": False, |
||||
|
"can_view_donation_reports": False, |
||||
|
"can_create_tickets": True, |
||||
|
"can_reply_tickets": True, |
||||
|
"can_manage_tickets": False, |
||||
|
"can_submit_forms": True, |
||||
|
"can_create_forms": False, |
||||
|
"can_export_form_data": False, |
||||
|
"can_view_diplomatic_reports": False, |
||||
|
"can_create_diplomatic_reports": False, |
||||
|
"can_access_admin_panel": False, |
||||
|
"can_manage_users": False, |
||||
|
"can_ban_users": False, |
||||
|
"can_view_analytics": False, |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
"name": "Verified User", |
||||
|
"slug": "verified_user", |
||||
|
"description": "Verified community member with authenticated identity, video calling, and direct content publishing.", |
||||
|
"is_default": False, |
||||
|
"is_system": True, |
||||
|
"permissions": { |
||||
|
"is_searchable": True, |
||||
|
"can_submit_verification": True, |
||||
|
"can_view_full_profiles": True, |
||||
|
"can_manage_institutions": False, |
||||
|
"can_send_direct_messages": True, |
||||
|
"can_create_group_chats": True, |
||||
|
"can_start_video_calls": True, |
||||
|
"can_moderate_chat": False, |
||||
|
"is_chat_muted": False, |
||||
|
"can_create_posts": True, |
||||
|
"can_publish_posts_directly": True, |
||||
|
"can_edit_own_posts": True, |
||||
|
"can_delete_own_posts": True, |
||||
|
"can_comment": True, |
||||
|
"can_enroll_courses": True, |
||||
|
"can_create_courses": False, |
||||
|
"can_publish_courses": False, |
||||
|
"can_issue_certificates": False, |
||||
|
"can_grade_submissions": False, |
||||
|
"has_premium_lms_access": True, |
||||
|
"can_request_meetings": True, |
||||
|
"can_accept_meetings": False, |
||||
|
"can_register_events": True, |
||||
|
"can_create_events": True, |
||||
|
"can_publish_events": False, |
||||
|
"can_checkin_attendees": False, |
||||
|
"can_export_attendee_list": False, |
||||
|
"can_volunteer": True, |
||||
|
"can_create_projects": False, |
||||
|
"can_manage_project_tasks": False, |
||||
|
"can_approve_projects": False, |
||||
|
"can_make_donations": True, |
||||
|
"can_create_donation_campaigns": False, |
||||
|
"can_view_donation_reports": False, |
||||
|
"can_create_tickets": True, |
||||
|
"can_reply_tickets": True, |
||||
|
"can_manage_tickets": False, |
||||
|
"can_submit_forms": True, |
||||
|
"can_create_forms": False, |
||||
|
"can_export_form_data": False, |
||||
|
"can_view_diplomatic_reports": False, |
||||
|
"can_create_diplomatic_reports": False, |
||||
|
"can_access_admin_panel": False, |
||||
|
"can_manage_users": False, |
||||
|
"can_ban_users": False, |
||||
|
"can_view_analytics": False, |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
"name": "Institution Admin", |
||||
|
"slug": "institution_admin", |
||||
|
"description": "Administrator of an Islamic center, university, or institution with management over courses, events, meetings, and campaigns.", |
||||
|
"is_default": False, |
||||
|
"is_system": True, |
||||
|
"permissions": { |
||||
|
"is_searchable": True, |
||||
|
"can_submit_verification": True, |
||||
|
"can_view_full_profiles": True, |
||||
|
"can_manage_institutions": True, |
||||
|
"can_send_direct_messages": True, |
||||
|
"can_create_group_chats": True, |
||||
|
"can_start_video_calls": True, |
||||
|
"can_moderate_chat": False, |
||||
|
"is_chat_muted": False, |
||||
|
"can_create_posts": True, |
||||
|
"can_publish_posts_directly": True, |
||||
|
"can_edit_own_posts": True, |
||||
|
"can_delete_own_posts": True, |
||||
|
"can_comment": True, |
||||
|
"can_enroll_courses": True, |
||||
|
"can_create_courses": True, |
||||
|
"can_publish_courses": True, |
||||
|
"can_issue_certificates": True, |
||||
|
"can_grade_submissions": True, |
||||
|
"has_premium_lms_access": True, |
||||
|
"can_request_meetings": True, |
||||
|
"can_accept_meetings": True, |
||||
|
"can_register_events": True, |
||||
|
"can_create_events": True, |
||||
|
"can_publish_events": True, |
||||
|
"can_checkin_attendees": True, |
||||
|
"can_export_attendee_list": True, |
||||
|
"can_volunteer": True, |
||||
|
"can_create_projects": True, |
||||
|
"can_manage_project_tasks": True, |
||||
|
"can_approve_projects": False, |
||||
|
"can_make_donations": True, |
||||
|
"can_create_donation_campaigns": True, |
||||
|
"can_view_donation_reports": True, |
||||
|
"can_create_tickets": True, |
||||
|
"can_reply_tickets": True, |
||||
|
"can_manage_tickets": False, |
||||
|
"can_submit_forms": True, |
||||
|
"can_create_forms": True, |
||||
|
"can_export_form_data": True, |
||||
|
"can_view_diplomatic_reports": True, |
||||
|
"can_create_diplomatic_reports": False, |
||||
|
"can_access_admin_panel": False, |
||||
|
"can_manage_users": False, |
||||
|
"can_ban_users": False, |
||||
|
"can_view_analytics": False, |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
"name": "Institution Owner", |
||||
|
"slug": "institution_owner", |
||||
|
"description": "Owner / Primary Representative of an institution with full oversight over institutional projects, campaigns, and diplomacy.", |
||||
|
"is_default": False, |
||||
|
"is_system": True, |
||||
|
"permissions": { |
||||
|
"is_searchable": True, |
||||
|
"can_submit_verification": True, |
||||
|
"can_view_full_profiles": True, |
||||
|
"can_manage_institutions": True, |
||||
|
"can_send_direct_messages": True, |
||||
|
"can_create_group_chats": True, |
||||
|
"can_start_video_calls": True, |
||||
|
"can_moderate_chat": False, |
||||
|
"is_chat_muted": False, |
||||
|
"can_create_posts": True, |
||||
|
"can_publish_posts_directly": True, |
||||
|
"can_edit_own_posts": True, |
||||
|
"can_delete_own_posts": True, |
||||
|
"can_comment": True, |
||||
|
"can_enroll_courses": True, |
||||
|
"can_create_courses": True, |
||||
|
"can_publish_courses": True, |
||||
|
"can_issue_certificates": True, |
||||
|
"can_grade_submissions": True, |
||||
|
"has_premium_lms_access": True, |
||||
|
"can_request_meetings": True, |
||||
|
"can_accept_meetings": True, |
||||
|
"can_register_events": True, |
||||
|
"can_create_events": True, |
||||
|
"can_publish_events": True, |
||||
|
"can_checkin_attendees": True, |
||||
|
"can_export_attendee_list": True, |
||||
|
"can_volunteer": True, |
||||
|
"can_create_projects": True, |
||||
|
"can_manage_project_tasks": True, |
||||
|
"can_approve_projects": True, |
||||
|
"can_make_donations": True, |
||||
|
"can_create_donation_campaigns": True, |
||||
|
"can_view_donation_reports": True, |
||||
|
"can_create_tickets": True, |
||||
|
"can_reply_tickets": True, |
||||
|
"can_manage_tickets": False, |
||||
|
"can_submit_forms": True, |
||||
|
"can_create_forms": True, |
||||
|
"can_export_form_data": True, |
||||
|
"can_view_diplomatic_reports": True, |
||||
|
"can_create_diplomatic_reports": True, |
||||
|
"can_access_admin_panel": False, |
||||
|
"can_manage_users": False, |
||||
|
"can_ban_users": False, |
||||
|
"can_view_analytics": False, |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
"name": "Content Moderator", |
||||
|
"slug": "content_moderator", |
||||
|
"description": "Community moderator responsible for chat safety, content review, and ticket management.", |
||||
|
"is_default": False, |
||||
|
"is_system": True, |
||||
|
"permissions": { |
||||
|
"is_searchable": True, |
||||
|
"can_submit_verification": True, |
||||
|
"can_view_full_profiles": True, |
||||
|
"can_manage_institutions": False, |
||||
|
"can_send_direct_messages": True, |
||||
|
"can_create_group_chats": True, |
||||
|
"can_start_video_calls": False, |
||||
|
"can_moderate_chat": True, |
||||
|
"is_chat_muted": False, |
||||
|
"can_create_posts": True, |
||||
|
"can_publish_posts_directly": True, |
||||
|
"can_edit_own_posts": True, |
||||
|
"can_delete_own_posts": True, |
||||
|
"can_comment": True, |
||||
|
"can_enroll_courses": True, |
||||
|
"can_create_courses": False, |
||||
|
"can_publish_courses": False, |
||||
|
"can_issue_certificates": False, |
||||
|
"can_grade_submissions": False, |
||||
|
"has_premium_lms_access": False, |
||||
|
"can_request_meetings": True, |
||||
|
"can_accept_meetings": False, |
||||
|
"can_register_events": True, |
||||
|
"can_create_events": False, |
||||
|
"can_publish_events": False, |
||||
|
"can_checkin_attendees": False, |
||||
|
"can_export_attendee_list": False, |
||||
|
"can_volunteer": True, |
||||
|
"can_create_projects": False, |
||||
|
"can_manage_project_tasks": False, |
||||
|
"can_approve_projects": False, |
||||
|
"can_make_donations": True, |
||||
|
"can_create_donation_campaigns": False, |
||||
|
"can_view_donation_reports": False, |
||||
|
"can_create_tickets": True, |
||||
|
"can_reply_tickets": True, |
||||
|
"can_manage_tickets": True, |
||||
|
"can_submit_forms": True, |
||||
|
"can_create_forms": False, |
||||
|
"can_export_form_data": False, |
||||
|
"can_view_diplomatic_reports": False, |
||||
|
"can_create_diplomatic_reports": False, |
||||
|
"can_access_admin_panel": True, |
||||
|
"can_manage_users": False, |
||||
|
"can_ban_users": False, |
||||
|
"can_view_analytics": False, |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
"name": "Regional Admin", |
||||
|
"slug": "regional_admin", |
||||
|
"description": "Regional supervisor with authority over regional institutions, user accounts, and community analytics.", |
||||
|
"is_default": False, |
||||
|
"is_system": True, |
||||
|
"permissions": { |
||||
|
"is_searchable": True, |
||||
|
"can_submit_verification": True, |
||||
|
"can_view_full_profiles": True, |
||||
|
"can_manage_institutions": True, |
||||
|
"can_send_direct_messages": True, |
||||
|
"can_create_group_chats": True, |
||||
|
"can_start_video_calls": True, |
||||
|
"can_moderate_chat": True, |
||||
|
"is_chat_muted": False, |
||||
|
"can_create_posts": True, |
||||
|
"can_publish_posts_directly": True, |
||||
|
"can_edit_own_posts": True, |
||||
|
"can_delete_own_posts": True, |
||||
|
"can_comment": True, |
||||
|
"can_enroll_courses": True, |
||||
|
"can_create_courses": True, |
||||
|
"can_publish_courses": True, |
||||
|
"can_issue_certificates": True, |
||||
|
"can_grade_submissions": True, |
||||
|
"has_premium_lms_access": True, |
||||
|
"can_request_meetings": True, |
||||
|
"can_accept_meetings": True, |
||||
|
"can_register_events": True, |
||||
|
"can_create_events": True, |
||||
|
"can_publish_events": True, |
||||
|
"can_checkin_attendees": True, |
||||
|
"can_export_attendee_list": True, |
||||
|
"can_volunteer": True, |
||||
|
"can_create_projects": True, |
||||
|
"can_manage_project_tasks": True, |
||||
|
"can_approve_projects": True, |
||||
|
"can_make_donations": True, |
||||
|
"can_create_donation_campaigns": True, |
||||
|
"can_view_donation_reports": True, |
||||
|
"can_create_tickets": True, |
||||
|
"can_reply_tickets": True, |
||||
|
"can_manage_tickets": True, |
||||
|
"can_submit_forms": True, |
||||
|
"can_create_forms": True, |
||||
|
"can_export_form_data": True, |
||||
|
"can_view_diplomatic_reports": True, |
||||
|
"can_create_diplomatic_reports": True, |
||||
|
"can_access_admin_panel": True, |
||||
|
"can_manage_users": True, |
||||
|
"can_ban_users": True, |
||||
|
"can_view_analytics": True, |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
"name": "Super Administrator", |
||||
|
"slug": "super_admin", |
||||
|
"description": "Master administrative role with all permissions, total access control, and full platform authority.", |
||||
|
"is_default": False, |
||||
|
"is_system": True, |
||||
|
"permissions": {perm: (perm != "is_chat_muted") for perm in ALL_PERMISSION_FIELDS} |
||||
|
}, |
||||
|
] |
||||
|
|
||||
|
|
||||
|
class Command(BaseCommand): |
||||
|
help = "Seed the 7 default configurable system roles with their exact permission matrix" |
||||
|
|
||||
|
@transaction.atomic |
||||
|
def handle(self, *args, **options): |
||||
|
self.stdout.write("Seeding default system roles...") |
||||
|
created_count = 0 |
||||
|
updated_count = 0 |
||||
|
|
||||
|
for role_data in DEFAULT_ROLES: |
||||
|
perms = role_data.pop("permissions") |
||||
|
role, created = Role.objects.get_or_create( |
||||
|
slug=role_data["slug"], |
||||
|
defaults={ |
||||
|
"name": role_data["name"], |
||||
|
"description": role_data["description"], |
||||
|
"is_default": role_data["is_default"], |
||||
|
"is_system": role_data["is_system"], |
||||
|
**perms |
||||
|
} |
||||
|
) |
||||
|
|
||||
|
if not created: |
||||
|
# Update role configuration if existing |
||||
|
role.name = role_data["name"] |
||||
|
role.description = role_data["description"] |
||||
|
role.is_default = role_data["is_default"] |
||||
|
role.is_system = role_data["is_system"] |
||||
|
for perm, val in perms.items(): |
||||
|
setattr(role, perm, val) |
||||
|
role.save() |
||||
|
updated_count += 1 |
||||
|
self.stdout.write(self.style.SUCCESS(f" [✓] Updated role: {role.name} ({role.slug})")) |
||||
|
else: |
||||
|
created_count += 1 |
||||
|
self.stdout.write(self.style.SUCCESS(f" [+] Created role: {role.name} ({role.slug})")) |
||||
|
|
||||
|
self.stdout.write(self.style.SUCCESS( |
||||
|
f"Successfully finished seeding roles: {created_count} created, {updated_count} updated." |
||||
|
)) |
||||
@ -0,0 +1,88 @@ |
|||||
|
# Generated by Django 4.2.30 on 2026-10-06 12:37 |
||||
|
|
||||
|
from django.db import migrations, models |
||||
|
import django.db.models.deletion |
||||
|
|
||||
|
|
||||
|
class Migration(migrations.Migration): |
||||
|
|
||||
|
dependencies = [ |
||||
|
('account', '0004_user_address_user_institution_name_user_rank_and_more'), |
||||
|
] |
||||
|
|
||||
|
operations = [ |
||||
|
migrations.CreateModel( |
||||
|
name='Role', |
||||
|
fields=[ |
||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), |
||||
|
('name', models.CharField(max_length=100, unique=True, verbose_name='Role Name')), |
||||
|
('slug', models.SlugField(blank=True, max_length=100, unique=True, verbose_name='Slug / Identifier')), |
||||
|
('description', models.TextField(blank=True, verbose_name='Description')), |
||||
|
('is_default', models.BooleanField(default=False, help_text='Automatically assigned to new registrations if no other role is selected.', verbose_name='Default Role for New Users')), |
||||
|
('is_system', models.BooleanField(default=False, help_text='Marks default system roles. Admins can configure permissions, but deletion is restricted.', verbose_name='System Core Role')), |
||||
|
('is_searchable', models.BooleanField(default=True, verbose_name='Appear in User Searches / Directory')), |
||||
|
('can_submit_verification', models.BooleanField(default=True, verbose_name='Submit Verification Documents')), |
||||
|
('can_view_full_profiles', models.BooleanField(default=True, verbose_name='View Full Profiles')), |
||||
|
('can_manage_institutions', models.BooleanField(default=False, verbose_name='Manage Institutions')), |
||||
|
('can_send_direct_messages', models.BooleanField(default=True, verbose_name='Send Direct Messages')), |
||||
|
('can_create_group_chats', models.BooleanField(default=True, verbose_name='Create Group Chats')), |
||||
|
('can_start_video_calls', models.BooleanField(default=False, verbose_name='Start Video Calls')), |
||||
|
('can_moderate_chat', models.BooleanField(default=False, verbose_name='Moderate Chat Rooms')), |
||||
|
('is_chat_muted', models.BooleanField(default=False, verbose_name='Chat Muted (Kill-switch)')), |
||||
|
('can_create_posts', models.BooleanField(default=True, verbose_name='Create Posts')), |
||||
|
('can_publish_posts_directly', models.BooleanField(default=False, verbose_name='Publish Posts Directly (Bypass Moderation)')), |
||||
|
('can_edit_own_posts', models.BooleanField(default=True, verbose_name='Edit Own Posts')), |
||||
|
('can_delete_own_posts', models.BooleanField(default=True, verbose_name='Delete Own Posts')), |
||||
|
('can_comment', models.BooleanField(default=True, verbose_name='Comment on Posts')), |
||||
|
('can_enroll_courses', models.BooleanField(default=True, verbose_name='Enroll in Courses')), |
||||
|
('can_create_courses', models.BooleanField(default=False, verbose_name='Create Courses')), |
||||
|
('can_publish_courses', models.BooleanField(default=False, verbose_name='Publish Courses Directly')), |
||||
|
('can_issue_certificates', models.BooleanField(default=False, verbose_name='Issue Certificates')), |
||||
|
('can_grade_submissions', models.BooleanField(default=False, verbose_name='Grade Submissions')), |
||||
|
('has_premium_lms_access', models.BooleanField(default=False, verbose_name='Premium LMS Access')), |
||||
|
('can_request_meetings', models.BooleanField(default=True, verbose_name='Send Meeting Requests')), |
||||
|
('can_accept_meetings', models.BooleanField(default=False, verbose_name='Accept / Host Meetings')), |
||||
|
('can_register_events', models.BooleanField(default=True, verbose_name='Register for Events')), |
||||
|
('can_create_events', models.BooleanField(default=False, verbose_name='Create Events')), |
||||
|
('can_publish_events', models.BooleanField(default=False, verbose_name='Publish Events Directly')), |
||||
|
('can_checkin_attendees', models.BooleanField(default=False, verbose_name='Check-in Attendees (Scan Tickets)')), |
||||
|
('can_export_attendee_list', models.BooleanField(default=False, verbose_name='Export Attendee List')), |
||||
|
('can_volunteer', models.BooleanField(default=True, verbose_name='Volunteer for Projects')), |
||||
|
('can_create_projects', models.BooleanField(default=False, verbose_name='Create Projects')), |
||||
|
('can_manage_project_tasks', models.BooleanField(default=False, verbose_name='Manage Project Tasks')), |
||||
|
('can_approve_projects', models.BooleanField(default=False, verbose_name='Approve Projects')), |
||||
|
('can_make_donations', models.BooleanField(default=True, verbose_name='Make Donations')), |
||||
|
('can_create_donation_campaigns', models.BooleanField(default=False, verbose_name='Create Donation Campaigns')), |
||||
|
('can_view_donation_reports', models.BooleanField(default=False, verbose_name='View Donation Reports')), |
||||
|
('can_create_tickets', models.BooleanField(default=True, verbose_name='Create Support Tickets')), |
||||
|
('can_reply_tickets', models.BooleanField(default=True, verbose_name='Reply to Support Tickets')), |
||||
|
('can_manage_tickets', models.BooleanField(default=False, verbose_name='Manage Support Tickets')), |
||||
|
('can_submit_forms', models.BooleanField(default=True, verbose_name='Submit Dynamic Forms')), |
||||
|
('can_create_forms', models.BooleanField(default=False, verbose_name='Create Dynamic Forms')), |
||||
|
('can_export_form_data', models.BooleanField(default=False, verbose_name='Export Form Responses')), |
||||
|
('can_view_diplomatic_reports', models.BooleanField(default=False, verbose_name='View Diplomatic Reports')), |
||||
|
('can_create_diplomatic_reports', models.BooleanField(default=False, verbose_name='Create Diplomatic Reports')), |
||||
|
('can_access_admin_panel', models.BooleanField(default=False, verbose_name='Access Admin Panel')), |
||||
|
('can_manage_users', models.BooleanField(default=False, verbose_name='Manage Users')), |
||||
|
('can_ban_users', models.BooleanField(default=False, verbose_name='Ban / Suspend Users')), |
||||
|
('can_view_analytics', models.BooleanField(default=False, verbose_name='View Analytics Dashboard')), |
||||
|
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), |
||||
|
('updated_at', models.DateTimeField(auto_now=True, verbose_name='Updated At')), |
||||
|
], |
||||
|
options={ |
||||
|
'verbose_name': 'Role', |
||||
|
'verbose_name_plural': 'Roles', |
||||
|
'ordering': ('name',), |
||||
|
}, |
||||
|
), |
||||
|
migrations.AddField( |
||||
|
model_name='user', |
||||
|
name='custom_permissions', |
||||
|
field=models.JSONField(blank=True, default=dict, help_text='Dictionary of permission overrides: {permission_name: True/False}', verbose_name='Custom Permission Overrides'), |
||||
|
), |
||||
|
migrations.AddField( |
||||
|
model_name='user', |
||||
|
name='role', |
||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='users', to='account.role', verbose_name='Assigned Role'), |
||||
|
), |
||||
|
] |
||||
@ -1,4 +1,6 @@ |
|||||
from .user import * |
from .user import * |
||||
from .groups import * |
from .groups import * |
||||
from .notification import * |
from .notification import * |
||||
from .verification import * |
|
||||
|
from .verification import * |
||||
|
from .role import * |
||||
|
|
||||
@ -0,0 +1,188 @@ |
|||||
|
from django.db import models |
||||
|
from django.utils.text import slugify |
||||
|
from django.utils.translation import gettext_lazy as _ |
||||
|
|
||||
|
|
||||
|
PERMISSION_CATEGORIES = { |
||||
|
_("Identity & Profiles"): [ |
||||
|
"is_searchable", |
||||
|
"can_submit_verification", |
||||
|
"can_view_full_profiles", |
||||
|
"can_manage_institutions", |
||||
|
], |
||||
|
_("Chat & Communications"): [ |
||||
|
"can_send_direct_messages", |
||||
|
"can_create_group_chats", |
||||
|
"can_start_video_calls", |
||||
|
"can_moderate_chat", |
||||
|
"is_chat_muted", |
||||
|
], |
||||
|
_("CMS & Articles"): [ |
||||
|
"can_create_posts", |
||||
|
"can_publish_posts_directly", |
||||
|
"can_edit_own_posts", |
||||
|
"can_delete_own_posts", |
||||
|
"can_comment", |
||||
|
], |
||||
|
_("LMS & Academics"): [ |
||||
|
"can_enroll_courses", |
||||
|
"can_create_courses", |
||||
|
"can_publish_courses", |
||||
|
"can_issue_certificates", |
||||
|
"can_grade_submissions", |
||||
|
"has_premium_lms_access", |
||||
|
], |
||||
|
_("Meetings"): [ |
||||
|
"can_request_meetings", |
||||
|
"can_accept_meetings", |
||||
|
], |
||||
|
_("Events"): [ |
||||
|
"can_register_events", |
||||
|
"can_create_events", |
||||
|
"can_publish_events", |
||||
|
"can_checkin_attendees", |
||||
|
"can_export_attendee_list", |
||||
|
], |
||||
|
_("Community Projects"): [ |
||||
|
"can_volunteer", |
||||
|
"can_create_projects", |
||||
|
"can_manage_project_tasks", |
||||
|
"can_approve_projects", |
||||
|
], |
||||
|
_("Donations & Charity"): [ |
||||
|
"can_make_donations", |
||||
|
"can_create_donation_campaigns", |
||||
|
"can_view_donation_reports", |
||||
|
], |
||||
|
_("Support & Tickets"): [ |
||||
|
"can_create_tickets", |
||||
|
"can_reply_tickets", |
||||
|
"can_manage_tickets", |
||||
|
], |
||||
|
_("Dynamic Forms"): [ |
||||
|
"can_submit_forms", |
||||
|
"can_create_forms", |
||||
|
"can_export_form_data", |
||||
|
], |
||||
|
_("Diplomacy & Institutional"): [ |
||||
|
"can_view_diplomatic_reports", |
||||
|
"can_create_diplomatic_reports", |
||||
|
], |
||||
|
_("Administration & Security"): [ |
||||
|
"can_access_admin_panel", |
||||
|
"can_manage_users", |
||||
|
"can_ban_users", |
||||
|
"can_view_analytics", |
||||
|
], |
||||
|
} |
||||
|
|
||||
|
ALL_PERMISSION_FIELDS = [perm for perms in PERMISSION_CATEGORIES.values() for perm in perms] |
||||
|
|
||||
|
|
||||
|
class Role(models.Model): |
||||
|
name = models.CharField(max_length=100, unique=True, verbose_name=_("Role Name")) |
||||
|
slug = models.SlugField(max_length=100, unique=True, blank=True, verbose_name=_("Slug / Identifier")) |
||||
|
description = models.TextField(blank=True, verbose_name=_("Description")) |
||||
|
is_default = models.BooleanField( |
||||
|
default=False, |
||||
|
verbose_name=_("Default Role for New Users"), |
||||
|
help_text=_("Automatically assigned to new registrations if no other role is selected.") |
||||
|
) |
||||
|
is_system = models.BooleanField( |
||||
|
default=False, |
||||
|
verbose_name=_("System Core Role"), |
||||
|
help_text=_("Marks default system roles. Admins can configure permissions, but deletion is restricted.") |
||||
|
) |
||||
|
|
||||
|
# 1. Identity & Profiles |
||||
|
is_searchable = models.BooleanField(default=True, verbose_name=_("Appear in User Searches / Directory")) |
||||
|
can_submit_verification = models.BooleanField(default=True, verbose_name=_("Submit Verification Documents")) |
||||
|
can_view_full_profiles = models.BooleanField(default=True, verbose_name=_("View Full Profiles")) |
||||
|
can_manage_institutions = models.BooleanField(default=False, verbose_name=_("Manage Institutions")) |
||||
|
|
||||
|
# 2. Chat & Communications |
||||
|
can_send_direct_messages = models.BooleanField(default=True, verbose_name=_("Send Direct Messages")) |
||||
|
can_create_group_chats = models.BooleanField(default=True, verbose_name=_("Create Group Chats")) |
||||
|
can_start_video_calls = models.BooleanField(default=False, verbose_name=_("Start Video Calls")) |
||||
|
can_moderate_chat = models.BooleanField(default=False, verbose_name=_("Moderate Chat Rooms")) |
||||
|
is_chat_muted = models.BooleanField(default=False, verbose_name=_("Chat Muted (Kill-switch)")) |
||||
|
|
||||
|
# 3. CMS & Articles |
||||
|
can_create_posts = models.BooleanField(default=True, verbose_name=_("Create Posts")) |
||||
|
can_publish_posts_directly = models.BooleanField(default=False, verbose_name=_("Publish Posts Directly (Bypass Moderation)")) |
||||
|
can_edit_own_posts = models.BooleanField(default=True, verbose_name=_("Edit Own Posts")) |
||||
|
can_delete_own_posts = models.BooleanField(default=True, verbose_name=_("Delete Own Posts")) |
||||
|
can_comment = models.BooleanField(default=True, verbose_name=_("Comment on Posts")) |
||||
|
|
||||
|
# 4. LMS & Academics |
||||
|
can_enroll_courses = models.BooleanField(default=True, verbose_name=_("Enroll in Courses")) |
||||
|
can_create_courses = models.BooleanField(default=False, verbose_name=_("Create Courses")) |
||||
|
can_publish_courses = models.BooleanField(default=False, verbose_name=_("Publish Courses Directly")) |
||||
|
can_issue_certificates = models.BooleanField(default=False, verbose_name=_("Issue Certificates")) |
||||
|
can_grade_submissions = models.BooleanField(default=False, verbose_name=_("Grade Submissions")) |
||||
|
has_premium_lms_access = models.BooleanField(default=False, verbose_name=_("Premium LMS Access")) |
||||
|
|
||||
|
# 5. Meetings |
||||
|
can_request_meetings = models.BooleanField(default=True, verbose_name=_("Send Meeting Requests")) |
||||
|
can_accept_meetings = models.BooleanField(default=False, verbose_name=_("Accept / Host Meetings")) |
||||
|
|
||||
|
# 6. Events |
||||
|
can_register_events = models.BooleanField(default=True, verbose_name=_("Register for Events")) |
||||
|
can_create_events = models.BooleanField(default=False, verbose_name=_("Create Events")) |
||||
|
can_publish_events = models.BooleanField(default=False, verbose_name=_("Publish Events Directly")) |
||||
|
can_checkin_attendees = models.BooleanField(default=False, verbose_name=_("Check-in Attendees (Scan Tickets)")) |
||||
|
can_export_attendee_list = models.BooleanField(default=False, verbose_name=_("Export Attendee List")) |
||||
|
|
||||
|
# 7. Community Projects |
||||
|
can_volunteer = models.BooleanField(default=True, verbose_name=_("Volunteer for Projects")) |
||||
|
can_create_projects = models.BooleanField(default=False, verbose_name=_("Create Projects")) |
||||
|
can_manage_project_tasks = models.BooleanField(default=False, verbose_name=_("Manage Project Tasks")) |
||||
|
can_approve_projects = models.BooleanField(default=False, verbose_name=_("Approve Projects")) |
||||
|
|
||||
|
# 8. Donations & Charity |
||||
|
can_make_donations = models.BooleanField(default=True, verbose_name=_("Make Donations")) |
||||
|
can_create_donation_campaigns = models.BooleanField(default=False, verbose_name=_("Create Donation Campaigns")) |
||||
|
can_view_donation_reports = models.BooleanField(default=False, verbose_name=_("View Donation Reports")) |
||||
|
|
||||
|
# 9. Support & Tickets |
||||
|
can_create_tickets = models.BooleanField(default=True, verbose_name=_("Create Support Tickets")) |
||||
|
can_reply_tickets = models.BooleanField(default=True, verbose_name=_("Reply to Support Tickets")) |
||||
|
can_manage_tickets = models.BooleanField(default=False, verbose_name=_("Manage Support Tickets")) |
||||
|
|
||||
|
# 10. Dynamic Forms |
||||
|
can_submit_forms = models.BooleanField(default=True, verbose_name=_("Submit Dynamic Forms")) |
||||
|
can_create_forms = models.BooleanField(default=False, verbose_name=_("Create Dynamic Forms")) |
||||
|
can_export_form_data = models.BooleanField(default=False, verbose_name=_("Export Form Responses")) |
||||
|
|
||||
|
# 11. Diplomacy & Institutional |
||||
|
can_view_diplomatic_reports = models.BooleanField(default=False, verbose_name=_("View Diplomatic Reports")) |
||||
|
can_create_diplomatic_reports = models.BooleanField(default=False, verbose_name=_("Create Diplomatic Reports")) |
||||
|
|
||||
|
# 12. Administration & Security |
||||
|
can_access_admin_panel = models.BooleanField(default=False, verbose_name=_("Access Admin Panel")) |
||||
|
can_manage_users = models.BooleanField(default=False, verbose_name=_("Manage Users")) |
||||
|
can_ban_users = models.BooleanField(default=False, verbose_name=_("Ban / Suspend Users")) |
||||
|
can_view_analytics = models.BooleanField(default=False, verbose_name=_("View Analytics Dashboard")) |
||||
|
|
||||
|
created_at = models.DateTimeField(auto_now_add=True, verbose_name=_("Created At")) |
||||
|
updated_at = models.DateTimeField(auto_now=True, verbose_name=_("Updated At")) |
||||
|
|
||||
|
class Meta: |
||||
|
ordering = ('name',) |
||||
|
verbose_name = _("Role") |
||||
|
verbose_name_plural = _("Roles") |
||||
|
|
||||
|
def __str__(self): |
||||
|
return self.name |
||||
|
|
||||
|
def save(self, *args, **kwargs): |
||||
|
if not self.slug: |
||||
|
self.slug = slugify(self.name) |
||||
|
if self.is_default: |
||||
|
# Ensure only one default role exists |
||||
|
Role.objects.filter(is_default=True).exclude(pk=self.pk).update(is_default=False) |
||||
|
super().save(*args, **kwargs) |
||||
|
|
||||
|
def get_permissions_dict(self): |
||||
|
"""Returns a dict of all permission flags and their boolean values.""" |
||||
|
return {perm: getattr(self, perm, False) for perm in ALL_PERMISSION_FIELDS} |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue