- Follow home/user-guides/ through one GUIDE_PATH_PREFIX constant. - Add a read-only Origin Zone detail view with the map as the main panel. - Name due diligence explicitly and add its section headings. - Add admin-only, four-eyes specification approval (portal and API). - Empty the New-zone geometry placeholder instead of showing null.
80 lines
2.8 KiB
Python
80 lines
2.8 KiB
Python
from django.db import models
|
|
|
|
# One structural prefix for every wiki guide path. Wiki.js now keeps all pages
|
|
# under a single `home/` root; change this line if that ever moves again.
|
|
GUIDE_PATH_PREFIX = "home/user-guides/"
|
|
|
|
|
|
class GuidePage(models.Model):
|
|
class Audience(models.TextChoices):
|
|
SHARED = "shared", "Shared"
|
|
BUYER = "buyer", "Buyer"
|
|
OPS = "ops", "Operations"
|
|
ADMIN = "admin", "Administrator"
|
|
|
|
AUDIENCE_PREFIXES = (
|
|
(f"{GUIDE_PATH_PREFIX}index", Audience.SHARED),
|
|
(f"{GUIDE_PATH_PREFIX}buyer/", Audience.BUYER),
|
|
(f"{GUIDE_PATH_PREFIX}ops/", Audience.OPS),
|
|
(f"{GUIDE_PATH_PREFIX}admin/", Audience.ADMIN),
|
|
)
|
|
|
|
path = models.CharField(max_length=255, unique=True)
|
|
title = models.CharField(max_length=255)
|
|
audience = models.CharField(max_length=20, choices=Audience.choices)
|
|
content = models.TextField(blank=True, default="")
|
|
updated_at = models.DateTimeField()
|
|
synced_at = models.DateTimeField(auto_now=True)
|
|
|
|
class Meta:
|
|
ordering = ["path"]
|
|
|
|
def __str__(self):
|
|
return self.title
|
|
|
|
@property
|
|
def page_slug(self):
|
|
if self.path.startswith(GUIDE_PATH_PREFIX):
|
|
return self.path[len(GUIDE_PATH_PREFIX):]
|
|
return self.path
|
|
|
|
def preview(self, max_chars=180):
|
|
for line in self.content.splitlines():
|
|
line = line.strip()
|
|
if not line or line.startswith(("#", "-", ">", "|", "*", "![", "```", "<")):
|
|
continue
|
|
text = line.replace("**", "").replace("`", "").replace("*", "").strip()
|
|
if text:
|
|
return text[:max_chars]
|
|
return ""
|
|
|
|
@classmethod
|
|
def map_audience(cls, path):
|
|
for prefix, audience in cls.AUDIENCE_PREFIXES:
|
|
if path == prefix or path.startswith(prefix):
|
|
return audience
|
|
return None
|
|
|
|
|
|
# The help centre is scoped by PORTAL first, then by role. Which guides are
|
|
# listed depends on which portal you are standing in, not only on who you are:
|
|
# an admin browsing the buyer portal sees the buyer guides, because it is the
|
|
# buyer portal. The admin-only guides never surface there.
|
|
AUDIENCES_BY_PORTAL = {
|
|
"portal": frozenset({GuidePage.Audience.SHARED, GuidePage.Audience.BUYER}),
|
|
"ops": frozenset({GuidePage.Audience.SHARED, GuidePage.Audience.OPS}),
|
|
}
|
|
|
|
# Extra audiences unlocked by ROLE, per portal. Admin guides are ops-side
|
|
# material, so they are only ever added inside the ops portal.
|
|
ADMIN_EXTRA_AUDIENCES = {
|
|
"ops": frozenset({GuidePage.Audience.ADMIN}),
|
|
}
|
|
|
|
|
|
def allowed_audiences(portal, role=None, is_superuser=False):
|
|
"""Guide audiences visible in `portal` ("portal" or "ops") for this user."""
|
|
allowed = set(AUDIENCES_BY_PORTAL.get(portal, frozenset()))
|
|
if is_superuser or role == "admin":
|
|
allowed |= ADMIN_EXTRA_AUDIENCES.get(portal, frozenset())
|
|
return allowed
|