Team managers/coaches now only see their own teams, can't create teams, and can view (but not edit) positions -- admins keep full rights. Locations and Opponents move from read-only stubs to full CRUD, gated to admins and management-position staff, with a country dropdown (django-countries) instead of free text. Also: the team list shows player/staff counts, and deleting a news item's main photo promotes another one instead of leaving the item without one. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R1gj3J1QPfP38XWpnpbFpy
135 lines
5.4 KiB
Python
135 lines
5.4 KiB
Python
"""Whether the signed-in user is a club ADMIN, for the management nav to hide
|
|
admin-only sections (seasons, positions, roles, shop, forms) from plain staff.
|
|
|
|
The underlying views are gated regardless (``ClubAdminRequiredMixin``) -- this is
|
|
purely so the nav doesn't show a link a coach or manager can't actually follow.
|
|
Same reasoning for ``news_permissions`` below, gating just the "New news item"
|
|
action rather than the whole section (``NewsAuthorRequiredMixin``/``can_add_news``).
|
|
"""
|
|
|
|
from club.services.access import can_add_news, has_management_access, is_club_admin, is_coach_manager
|
|
|
|
#: Every management URL name, mapped to the nav item it should light up --
|
|
#: management/templates/management/_nav_items.html compares against this.
|
|
#: One dict here beats threading `nav=` through every view in views.py, and
|
|
#: unlike that, a new page can't silently be forgotten (it just renders with
|
|
#: no active item until added below, rather than needing every view touched).
|
|
_NAV_SECTIONS = {
|
|
"home": "home",
|
|
"member_list": "member_list",
|
|
"member_create": "member_list",
|
|
"member_import_template": "member_list",
|
|
"member_import": "member_list",
|
|
"member_import_confirm": "member_list",
|
|
"member_detail": "member_list",
|
|
"member_update": "member_list",
|
|
"member_delete": "member_list",
|
|
"member_attach_family": "member_list",
|
|
"member_grant_login": "member_list",
|
|
"member_detach_family": "member_list",
|
|
"family_create": "member_list",
|
|
"family_detail": "member_list",
|
|
"family_add_child": "member_list",
|
|
"family_add_parent": "member_list",
|
|
"family_membership_role_update": "member_list",
|
|
"membership_list": "membership_list",
|
|
"membership_mark_paid": "membership_list",
|
|
"membership_export_pdf": "membership_list",
|
|
"membership_mark_fully_paid": "membership_list",
|
|
"membership_record_payment": "membership_list",
|
|
"position_list": "position_list",
|
|
"position_create": "position_list",
|
|
"position_update": "position_list",
|
|
"role_list": "role_list",
|
|
"role_create": "role_list",
|
|
"role_revoke": "role_list",
|
|
"team_list": "team_list",
|
|
"team_create": "team_list",
|
|
"team_update": "team_list",
|
|
"team_delete": "team_list",
|
|
"team_detail": "team_list",
|
|
"team_roster_add": "team_list",
|
|
"team_roster_update": "team_list",
|
|
"team_roster_remove": "team_list",
|
|
"team_staff_add": "team_list",
|
|
"team_staff_update": "team_list",
|
|
"team_staff_remove": "team_list",
|
|
"news_list": "news_list",
|
|
"news_create": "news_list",
|
|
"news_detail": "news_list",
|
|
"news_update": "news_list",
|
|
"news_delete": "news_list",
|
|
"news_publish": "news_list",
|
|
"news_unpublish": "news_list",
|
|
"news_photo_upload": "news_list",
|
|
"news_photo_set_main": "news_list",
|
|
"news_photo_delete": "news_list",
|
|
"event_list": "event_list",
|
|
"event_series_list": "event_series_list",
|
|
"location_list": "location_list",
|
|
"location_create": "location_list",
|
|
"location_update": "location_list",
|
|
"location_delete": "location_list",
|
|
"opponent_list": "opponent_list",
|
|
"opponent_create": "opponent_list",
|
|
"opponent_update": "opponent_list",
|
|
"opponent_delete": "opponent_list",
|
|
"product_list": "product_list",
|
|
"order_list": "order_list",
|
|
"discount_list": "discount_list",
|
|
"invoice_list": "invoice_list",
|
|
"form_list": "form_list",
|
|
"submission_list": "form_list",
|
|
}
|
|
|
|
|
|
def active_nav_section(request):
|
|
"""Which management nav item is currently active, derived from the resolved
|
|
URL name. Guarded on the "management" namespace so a same-named url_name in
|
|
some other app can never leak into this."""
|
|
match = request.resolver_match
|
|
if match is None or match.namespace != "management":
|
|
return {"nav": None}
|
|
|
|
return {"nav": _NAV_SECTIONS.get(match.url_name)}
|
|
|
|
|
|
def is_admin(request):
|
|
club = getattr(request, "club", None)
|
|
if club is None or not request.user.is_authenticated:
|
|
return {"is_club_admin": False}
|
|
|
|
return {"is_club_admin": is_club_admin(request.user, club)}
|
|
|
|
|
|
def management_position(request):
|
|
"""Whether the signed-in user holds a management position (or is ADMIN) --
|
|
gates the nav's Locations/Opponents links, which ``ManagementPositionRequiredMixin``
|
|
restricts to exactly this group (unlike most staff-visible sections)."""
|
|
club = getattr(request, "club", None)
|
|
if club is None or not request.user.is_authenticated:
|
|
return {"has_management_position": False}
|
|
|
|
return {"has_management_position": is_club_admin(request.user, club) or is_coach_manager(request.user, club)}
|
|
|
|
|
|
def management_link(request):
|
|
"""Whether to show a "Management" link in the global navbar (templates/_base.html),
|
|
next to the Django admin one -- only on a club subdomain, and only for someone with
|
|
real authority there (see has_management_access)."""
|
|
club = getattr(request, "club", None)
|
|
if club is None or not request.user.is_authenticated:
|
|
return {"has_management_access": False}
|
|
|
|
return {"has_management_access": has_management_access(request.user, club)}
|
|
|
|
|
|
def news_permissions(request):
|
|
"""Whether the "New news item" action should show -- viewing the News
|
|
section itself is open to any staff, same as Roster/Staff."""
|
|
club = getattr(request, "club", None)
|
|
if club is None or not request.user.is_authenticated:
|
|
return {"can_add_news": False}
|
|
|
|
return {"can_add_news": can_add_news(request.user, club)}
|