Add a news app: coach_manager authoring, team tagging, photos, editor release flow
News and NewsPhoto (team-tagged instead of categorised, one photo taggable as main via a partial unique constraint), gated per club/services/access.py: any current-season coach_manager, EDITOR, or ADMIN can draft and edit a news item; only EDITOR/ADMIN can publish it, or edit it once it's live. Publishing takes a date so it can be scheduled ahead of time rather than only right now. Authoring/release only for now -- no member-facing reading page or public API yet, the visibility field (internal/external/both) is there for when those land.
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
|
||||
from django.http import Http404
|
||||
|
||||
from .services.access import has_management_access, is_club_admin, teams_managed_by
|
||||
from .services.access import can_add_news, can_edit_news, can_publish_news, has_management_access, is_club_admin, teams_managed_by
|
||||
|
||||
|
||||
class ClubStaffRequiredMixin(LoginRequiredMixin, UserPassesTestMixin):
|
||||
@@ -49,3 +49,31 @@ class TeamManagerRequiredMixin(ClubStaffRequiredMixin):
|
||||
if is_club_admin(user, club):
|
||||
return True
|
||||
return teams_managed_by(user, club).filter(pk=self.get_team().pk).exists()
|
||||
|
||||
|
||||
class NewsAuthorRequiredMixin(ClubStaffRequiredMixin):
|
||||
"""ADMIN, EDITOR, or a current-season coach_manager -- who's trusted to
|
||||
author club content in the first place (creating a draft)."""
|
||||
|
||||
def test_func(self):
|
||||
return can_add_news(self.request.user, self.request.club)
|
||||
|
||||
|
||||
class NewsPublisherRequiredMixin(ClubStaffRequiredMixin):
|
||||
"""ADMIN/EDITOR only -- the release-flow gate for pushing a news item live
|
||||
(or pulling it back)."""
|
||||
|
||||
def test_func(self):
|
||||
return can_publish_news(self.request.user, self.request.club)
|
||||
|
||||
|
||||
class NewsEditRequiredMixin(ClubStaffRequiredMixin):
|
||||
"""Whoever may edit *this* news item right now: broad while it's a draft,
|
||||
editor/admin-only once published. ``self.get_news_item()`` must return the
|
||||
News the view acts on before ``test_func`` runs."""
|
||||
|
||||
def get_news_item(self):
|
||||
raise NotImplementedError("Subclasses must return the News item this view acts on.")
|
||||
|
||||
def test_func(self):
|
||||
return can_edit_news(self.request.user, self.get_news_item())
|
||||
|
||||
@@ -151,3 +151,22 @@ def can_edit_event(user: User, event: Event) -> bool:
|
||||
|
||||
def can_manage_shop(user: User, club: Club) -> bool:
|
||||
return is_club_admin(user, club)
|
||||
|
||||
|
||||
def can_add_news(user: User, club: Club) -> bool:
|
||||
"""ADMIN, EDITOR, or a current-season coach_manager -- who's trusted to
|
||||
author club content, not just anyone on staff (a physio shouldn't post news)."""
|
||||
return is_club_admin(user, club) or has_club_role(user, club, ClubRole.Roles.EDITOR) or is_coach_manager(user, club)
|
||||
|
||||
|
||||
def can_publish_news(user: User, club: Club) -> bool:
|
||||
"""Only ADMIN/EDITOR may push a news item live -- the release-flow gate."""
|
||||
return is_club_admin(user, club) or has_club_role(user, club, ClubRole.Roles.EDITOR)
|
||||
|
||||
|
||||
def can_edit_news(user: User, news_item) -> bool:
|
||||
"""Broad while it's a draft (anyone who could create one); editor/admin-only
|
||||
once published -- an editor is accountable for what's actually live."""
|
||||
if news_item.status == news_item.Status.PUBLISHED:
|
||||
return can_publish_news(user, news_item.club)
|
||||
return can_add_news(user, news_item.club)
|
||||
|
||||
Reference in New Issue
Block a user