|
| 1 | +"""EntityMember admin configuration.""" |
| 2 | + |
| 3 | +from django.contrib import admin |
| 4 | +from django.contrib.contenttypes.models import ContentType |
| 5 | +from django.db.models import Q |
| 6 | +from django.urls import reverse |
| 7 | +from django.utils.html import format_html |
| 8 | + |
| 9 | +from apps.owasp.models.chapter import Chapter |
| 10 | +from apps.owasp.models.committee import Committee |
| 11 | +from apps.owasp.models.entity_member import EntityMember |
| 12 | +from apps.owasp.models.project import Project |
| 13 | + |
| 14 | + |
| 15 | +class EntityMemberAdmin(admin.ModelAdmin): |
| 16 | + """Admin for EntityMember records (generic link to any OWASP entity).""" |
| 17 | + |
| 18 | + actions = ("approve_members",) |
| 19 | + autocomplete_fields = ("member",) |
| 20 | + fields = ( |
| 21 | + "entity_type", |
| 22 | + "entity_id", |
| 23 | + "member", |
| 24 | + "role", |
| 25 | + "order", |
| 26 | + "is_active", |
| 27 | + "is_reviewed", |
| 28 | + "description", |
| 29 | + ) |
| 30 | + list_display = ( |
| 31 | + "member", |
| 32 | + "entity", |
| 33 | + "owasp_url", |
| 34 | + "role", |
| 35 | + "is_active", |
| 36 | + "is_reviewed", |
| 37 | + "order", |
| 38 | + ) |
| 39 | + list_filter = ( |
| 40 | + "role", |
| 41 | + "is_active", |
| 42 | + "is_reviewed", |
| 43 | + ) |
| 44 | + raw_id_fields = ("member",) |
| 45 | + search_fields = ( |
| 46 | + "member__login", |
| 47 | + "member__name", |
| 48 | + "description", |
| 49 | + ) |
| 50 | + ordering = ("member__name", "order") |
| 51 | + |
| 52 | + @admin.action(description="Approve selected members") |
| 53 | + def approve_members(self, request, queryset): |
| 54 | + """Approve selected members.""" |
| 55 | + self.message_user( |
| 56 | + request, |
| 57 | + f"Successfully approved {queryset.update(is_active=True, is_reviewed=True)} members.", |
| 58 | + ) |
| 59 | + |
| 60 | + @admin.display(description="Entity", ordering="entity_type") |
| 61 | + def entity(self, obj): |
| 62 | + """Return entity link.""" |
| 63 | + return ( |
| 64 | + format_html( |
| 65 | + '<a href="{}" target="_blank">{}</a>', |
| 66 | + reverse( |
| 67 | + f"admin:{obj.entity_type.app_label}_{obj.entity_type.model}_change", |
| 68 | + args=[obj.entity_id], |
| 69 | + ), |
| 70 | + obj.entity, |
| 71 | + ) |
| 72 | + if obj.entity |
| 73 | + else "-" |
| 74 | + ) |
| 75 | + |
| 76 | + @admin.display(description="OWASP URL", ordering="entity_type") |
| 77 | + def owasp_url(self, obj): |
| 78 | + """Return entity OWASP site URL.""" |
| 79 | + return ( |
| 80 | + format_html('<a href="{}" target="_blank">↗️</a>', obj.entity.owasp_url) |
| 81 | + if obj.entity |
| 82 | + else "-" |
| 83 | + ) |
| 84 | + |
| 85 | + def get_search_results(self, request, queryset, search_term): |
| 86 | + """Get search results from entity name or key.""" |
| 87 | + queryset, use_distinct = super().get_search_results(request, queryset, search_term) |
| 88 | + |
| 89 | + if search_term: |
| 90 | + project_ids = Project.objects.filter( |
| 91 | + Q(name__icontains=search_term) | Q(key__icontains=search_term) |
| 92 | + ).values_list("pk", flat=True) |
| 93 | + |
| 94 | + chapter_ids = Chapter.objects.filter( |
| 95 | + Q(name__icontains=search_term) | Q(key__icontains=search_term) |
| 96 | + ).values_list("pk", flat=True) |
| 97 | + |
| 98 | + committee_ids = Committee.objects.filter( |
| 99 | + Q(name__icontains=search_term) | Q(key__icontains=search_term) |
| 100 | + ).values_list("pk", flat=True) |
| 101 | + |
| 102 | + project_ct = ContentType.objects.get_for_model(Project) |
| 103 | + chapter_ct = ContentType.objects.get_for_model(Chapter) |
| 104 | + committee_ct = ContentType.objects.get_for_model(Committee) |
| 105 | + |
| 106 | + entity_match_query = ( |
| 107 | + Q(entity_type=project_ct, entity_id__in=list(project_ids)) |
| 108 | + | Q(entity_type=chapter_ct, entity_id__in=list(chapter_ids)) |
| 109 | + | Q(entity_type=committee_ct, entity_id__in=list(committee_ids)) |
| 110 | + ) |
| 111 | + |
| 112 | + queryset |= self.model.objects.filter(entity_match_query) |
| 113 | + use_distinct = True |
| 114 | + |
| 115 | + return queryset, use_distinct |
| 116 | + |
| 117 | + |
| 118 | +admin.site.register(EntityMember, EntityMemberAdmin) |
0 commit comments