import json from django import forms from django.contrib.auth.models import Group from django.utils.html import escape from django.utils.safestring import mark_safe from django.utils.translation import get_language, get_language_bidi, gettext as _ from apps.account.models.role import Role, PERMISSION_CATEGORIES, ALL_PERMISSION_FIELDS from apps.profiles.models import Region CATEGORY_ICONS = { "Identity & Profiles": "badge", "Chat & Communications": "forum", "CMS & Articles": "article", "LMS & Academics": "school", "Meetings": "video_camera_front", "Events": "event", "Community Projects": "task_alt", "Donations & Charity": "volunteer_activism", "Support & Tickets": "confirmation_number", "Dynamic Forms": "dynamic_form", "Diplomacy & Institutional": "public", "Administration & Security": "admin_panel_settings", } CATEGORY_TITLES_FA = { "Identity & Profiles": "هویت و پروفایلها", "Chat & Communications": "پیامرسان و ارتباطات", "CMS & Articles": "مدیریت محتوا و مقالات", "LMS & Academics": "آموزش و امور علمی", "Meetings": "جلسات و کنفرانسها", "Events": "رویدادها و همایشها", "Community Projects": "پروژههای مشارکتی", "Donations & Charity": "امور خیریه و هدایا", "Support & Tickets": "پشتیبانی و تیکتها", "Dynamic Forms": "فرمهای پویا و نظرسنجی", "Diplomacy & Institutional": "روابط نهادی و بینالملل", "Administration & Security": "مدیریت و امنیت سیستم", } class UserAccessManagementWidget(forms.Widget): """ State-of-the-art Unfold Admin Access Management Widget. Supports seamless bilingual localization (English / Persian) with automatic LTR/RTL direction. """ def __init__(self, attrs=None): super().__init__(attrs) self.instance = None def render(self, name, value, attrs=None, renderer=None): instance = getattr(self, "instance", None) current_lang = (get_language() or "en").lower() is_fa = current_lang.startswith("fa") is_bidi = get_language_bidi() layout_dir = "rtl" if is_bidi else "ltr" t = { # Overview Card "overview_title": "خلاصه سطح دسترسی و اختیارات کاربر" if is_fa else "Role & Permissions Overview", "overview_sub": "تنظیمات یکپارچه نقش سازمانی، دسترسیهای اختصاصی، نهاد وابسته و مناطق جغرافیایی" if is_fa else "Unified management of user role, custom permissions, affiliated institution, and managed regions", "btn_add_new_perm": "+ افزودن دسترسی جدید" if is_fa else "+ Add Permission", "btn_open_modal": "تنظیم نقش و دسترسیها (مودال کامل)" if is_fa else "Configure Role & Access", "base_role_label": "نقش سازمانی / Base Role" if is_fa else "Base Role", "btn_change_role": "تغییر نقش" if is_fa else "Change Role", "unassigned_role": "بدون نقش سازمانی" if is_fa else "Unassigned / No Role", "all_perms_match": "تمامی اختیارات منطبق بر نقش پایه" if is_fa else "All permissions match base role", "custom_perms_suffix": "دسترسی اختصاصی فراتر از نقش" if is_fa else "custom permissions beyond role", "revoked_perms_suffix": "اختیار سلبشده" if is_fa else "permissions revoked", "additional_perms_label": "دسترسیهای فراتر از نقش / Additional" if is_fa else "Additional Permissions", "btn_add_perm": "افزودن دسترسی" if is_fa else "Add Permission", "no_custom_perms": "فاقد مجوز اختصاصی (دسترسی استاندارد)" if is_fa else "No custom permissions (Standard role access)", "more_suffix": "دیگر" if is_fa else "more", "add_perm_hint": "روی دکمه «افزودن دسترسی» کلیک کنید تا اختیارات اختصاصی اضافه شود." if is_fa else "Click 'Add Permission' to grant additional custom permissions.", "institution_label": "نهاد وابسته / Institution" if is_fa else "Affiliated Institution", "no_institution": "فاقد نهاد وابسته (شخصی/مستقل)" if is_fa else "No affiliated institution (Independent/Personal)", "institution_hint": "اختیارات اداری دروننهادی در این مرکز اعمال میشود." if is_fa else "Administrative authority within this institution applies here.", "regions_label": "قلمرو جغرافیایی / Managed Regions" if is_fa else "Geographic Jurisdictions (Managed Regions)", "regions_unlimited": "دسترسی عمومی / نامحدود" if is_fa else "Global / Unlimited access", "regions_hint": "برای مدیران منطقهای به استانها یا کشورهای خاص محدود میشود." if is_fa else "For regional administrators, authority is restricted to assigned regions.", "loading": "در حال بارگذاری..." if is_fa else "Loading...", # Modal Dialog "modal_title": "پیکربندی جامع دسترسی و اختیارات" if is_fa else "Configure Role & Permissions", "modal_sub": "تخصیص نقش سازمانی، مدیریت اختیارات اختصاصی، تعیین نهاد و حوزههای منطقهای" if is_fa else "Assign organizational role, configure custom permissions, set affiliated institution and geographic regions", "tab_roles": "نقشها و نوع کاربر" if is_fa else "Roles & User Type", "tab_permissions": "مجوزها و اختیارات" if is_fa else "Permissions & Matrix", "tab_institutions": "دسترسی به نهادها" if is_fa else "Institution Access", "tab_regions": "دسترسی به مناطق" if is_fa else "Region Access", # Tab 1 "select_role_title": "انتخاب نقش پایه سازمانی (Assigned Role)" if is_fa else "Select Base Organizational Role", "select_role_sub": "نقش پایه شامل دستهای از اختیارات پیشفرض تاییدشده است" if is_fa else "Base role includes a bundle of verified default permissions", "default_perms_label": "مجوزهای پیشفرض موجود در نقش انتخابی:" if is_fa else "Default permissions granted by selected role:", "active_perms_count": "مجوز فعال" if is_fa else "active permissions", "no_role_selected": "هیچ نقشی انتخاب نشده است." if is_fa else "No role selected.", "role_hint": "برای اعطای مجوزهای بیشتر به کاربر (فراتر از این نقش) یا سلب برخی اختیارات، به تب «مجوزها و اختیارات» مراجعه کنید." if is_fa else "To grant additional permissions (beyond this role) or revoke specific ones, navigate to the 'Permissions & Matrix' tab.", "default_role_desc": "نقش سازمانی با مجوزهای پیشفرض" if is_fa else "Organizational role with default permissions", "default_perms_chip": "مجوز پیشفرض" if is_fa else "default permissions", "user_type_label": "نوع کاربری (User Type)" if is_fa else "User Type", "groups_label": "گروههای سیستمی (Django Groups)" if is_fa else "Django Groups", # User types "ut_superadmin": "Superadmin (مدیر ارشد سامانه)" if is_fa else "Superadmin (System Administrator)", "ut_regional_admin": "Regional Admin (مدیر منطقهای)" if is_fa else "Regional Admin", "ut_institution_admin": "Institution Admin (مدیر نهاد)" if is_fa else "Institution Admin", "ut_editor": "Editor (ویرایشگر محتوا)" if is_fa else "Editor (Content Editor)", "ut_staff": "Staff (کارشناس پشتیبانی و امور اداری)" if is_fa else "Staff (Support & Operations)", "ut_client": "Client (کاربر عمومی سامانه)" if is_fa else "Client (Standard User)", # Tab 2 "matrix_title": "ماتریس دسترسیها و شخصیسازی مجوزها" if is_fa else "Permissions Matrix & Customization", "matrix_sub": "مجوزهای نقش پایه بهصورت خودکار ارثبری میشوند. میتوانید مجوز جدید اضافه کنید یا اختیاری را سلب فرمایید." if is_fa else "Permissions from the base role are automatically inherited. You can grant custom permissions or revoke existing ones.", "search_placeholder": "جستجو در مجوزها..." if is_fa else "Search permissions...", "reset_btn": "پاکسازی" if is_fa else "Reset Overrides", "add_perm_panel_title": "افزودن دسترسی جدید به کاربر (Add New Permission)" if is_fa else "Add New Permission to User", "add_perm_panel_sub": "یک مجوز سیستمی را انتخاب یا شناسه اختصاصی وارد کنید" if is_fa else "Select a system permission or enter a custom key", "select_system_perm_default": "-- انتخاب از تمام مجوزهای سیستم --" if is_fa else "-- Select from all system permissions --", "in_role_tag": " (در نقش موجود است)" if is_fa else " (in role)", "active_custom_tag": " (فعال اختصاصی)" if is_fa else " (active custom)", "btn_grant": "اعطای مجوز" if is_fa else "Grant Permission", "custom_key_placeholder": "یا وارد کردن شناسه مجوز دلخواه (مثال: can_export_reports)..." if is_fa else "Or enter custom permission key (e.g. can_export_reports)...", "btn_add_key": "افزودن کلید" if is_fa else "Add Key", "custom_flags_title": "مجوزهای سفارشی افزودهشده (Custom Granted Flags)" if is_fa else "Custom Granted Flags", "delete_perm_title": "حذف این مجوز" if is_fa else "Delete this permission", "status_custom_allow": "✓ مجوز اختصاصی (Allow)" if is_fa else "✓ Custom Granted (Allow)", "status_custom_deny": "✕ سلب اختیار (Deny)" if is_fa else "✕ Revoked (Deny)", "status_inherited_active": "● ارثبری: فعال از نقش" if is_fa else "● Inherited: Active from role", "status_inherited_inactive": "○ ارثبری: غیرفعال" if is_fa else "○ Inherited: Inactive", "status_custom_flag_allow": "✓ مجوز فعال سفارشی" if is_fa else "✓ Custom active permission", "status_custom_flag_deny": "✕ سلب اختیار سفارشی" if is_fa else "✕ Custom revoked permission", "btn_inherit": "ارثبری" if is_fa else "Inherit", "btn_allow": "مجوز" if is_fa else "Allow", "btn_deny": "سلب" if is_fa else "Deny", # Tab 3 "inst_title": "نهاد وابسته به کاربر (Affiliated Institution)" if is_fa else "Affiliated Institution", "inst_sub": "نام مرکز، موسسه، دانشگاه یا حوزه علمیهای که کاربر به نمایندگی از آن فعالیت میکند." if is_fa else "The organization, center, university, or seminary represented by this user.", "inst_input_label": "عنوان رسمی نهاد" if is_fa else "Official Institution Name", "inst_input_placeholder": "مثال: جامعة المصطفی العالمیة / دانشگاه بینالمللی اهلبیت (ع)" if is_fa else "e.g., Al-Mustafa International University", "inst_guidelines_title": "راهنمای اختیارات دروننهادی:" if is_fa else "Internal Institution Guidelines:", "inst_guideline_1": "کاربران دارای نوع کاربری Institution Admin بهطور خودکار مدیر صفحه و پروژههای این نهاد خواهند بود." if is_fa else "Users with Institution Admin user type automatically manage this institution's profile and projects.", "inst_guideline_2": "برای عضویت چندگانه در نهادها، میتوانید از بخش پایین صفحه (Institution Memberships Inline) استفاده کنید." if is_fa else "For multiple institution memberships, use the Institution Memberships inline below.", # Tab 4 "regions_title": "تخصیص حوزههای صلاحیت جغرافیایی (Managed Regions)" if is_fa else "Assign Geographic Jurisdictions (Managed Regions)", "regions_sub": "اختیارات نظارتی مدیران منطقهای صرفاً بر روی نهادها، کاربران و رویدادهای واقع در مناطق انتخابشده اعمال میگردد." if is_fa else "Oversight authority of regional admins applies only to institutions, users, and events within selected regions.", "regions_selected_count": "منطقه انتخابشده" if is_fa else "regions selected", # Footer "footer_role": "نقش:" if is_fa else "Role:", "footer_no_role": "بدون نقش" if is_fa else "No Role", "footer_extra": "دسترسی اختصاصی" if is_fa else "custom permissions", "footer_regions": "حوزه منطقهای" if is_fa else "geographic regions", "btn_cancel": "انصراف" if is_fa else "Cancel", "btn_apply": "ثبت و اعمال تغییرات" if is_fa else "Save & Apply Changes", } if isinstance(value, str): try: overrides = json.loads(value) if value.strip() else {} except Exception: overrides = {} elif isinstance(value, dict): overrides = value else: overrides = {} # 1. Fetch Roles Data roles_data = {} roles_list = [] for r in Role.objects.all().order_by("name"): r_dict = { "id": str(r.id), "name": r.name, "slug": r.slug or "", "description": r.description or "", "is_default": r.is_default, "is_system": r.is_system, "permissions": r.get_permissions_dict(), } roles_data[str(r.id)] = r_dict roles_list.append(r_dict) # 2. Fetch Categories & Permissions Data categories_data = [] all_perms_map = {} for cat_label, field_names in PERMISSION_CATEGORIES.items(): cat_name_str = str(cat_label) icon = CATEGORY_ICONS.get(cat_name_str, "admin_panel_settings") perms_list = [] for field_name in field_names: try: f = Role._meta.get_field(field_name) vname = str(f.verbose_name) help_txt = str(f.help_text) if hasattr(f, "help_text") else "" except Exception: vname = field_name.replace("_", " ").title() help_txt = "" p_item = { "name": field_name, "label": vname, "help": help_txt, "category": cat_name_str, } perms_list.append(p_item) all_perms_map[field_name] = p_item display_title = CATEGORY_TITLES_FA.get(cat_name_str, cat_name_str) if is_fa else cat_name_str categories_data.append({ "title": display_title, "icon": icon, "perms": perms_list, }) # 3. Fetch Regions Data regions_data = [] try: for reg in Region.objects.filter(is_active=True).order_by("name"): regions_data.append({ "id": str(reg.id), "name": reg.name, "code": getattr(reg, "code", "") or "", }) except Exception: pass # 4. Fetch Groups Data groups_data = [] try: for grp in Group.objects.all().order_by("name"): groups_data.append({ "id": str(grp.id), "name": grp.name, }) except Exception: pass # 5. Extract Current User State current_role_id = str(instance.role_id) if instance and instance.role_id else "" current_user_type = instance.user_type if instance and hasattr(instance, "user_type") else "client" current_institution = instance.institution_name or "" if instance and hasattr(instance, "institution_name") else "" current_groups = [str(g.id) for g in instance.groups.all()] if instance and instance.pk else [] current_regions = [str(r.id) for r in instance.managed_regions.all()] if instance and instance.pk else [] current_role = roles_data.get(current_role_id) current_role_display = current_role["name"] if current_role else t["unassigned_role"] current_inst_display = current_institution if current_institution else t["no_institution"] initial_state = { "roleId": current_role_id, "userType": current_user_type, "institution": current_institution, "groups": current_groups, "regions": current_regions, "overrides": overrides, } roles_json_str = json.dumps(roles_data) categories_json_str = json.dumps(categories_data) regions_json_str = json.dumps(regions_data) groups_json_str = json.dumps(groups_data) initial_json_str = json.dumps(initial_state) perms_map_json_str = json.dumps(all_perms_map) t_json_str = json.dumps(t) # ─── Render HTML Structure ────────────────────────────────────────── html = [ f'''
{t["overview_sub"]}
{t["modal_sub"]}
{t["role_hint"]}