This repository has been archived on 2026-03-24. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
docs/src/backend/core/choices.py
Samuel Paccoud - DINUM 8f67e382ba ♻️(backend) refactor get_select_options to take definitions dict
This will allow us to simplify the get_abilities method. It is also
more efficient because we have computed this definitions dict and
the the get_select_options method was doing the conversion again.
2025-07-08 13:49:32 +02:00

113 lines
3.9 KiB
Python

"""Declare and configure choices for Docs' core application."""
from django.db.models import TextChoices
from django.utils.translation import gettext_lazy as _
class PriorityTextChoices(TextChoices):
"""
This class inherits from Django's TextChoices and provides a method to get the priority
of a given value based on its position in the class.
"""
@classmethod
def get_priority(cls, value):
"""Returns the priority of the given value based on its order in the class."""
members = list(cls.__members__.values())
return members.index(value) + 1 if value in members else 0
@classmethod
def max(cls, *roles):
"""
Return the highest-priority role among the given roles, using get_priority().
If no valid roles are provided, returns None.
"""
valid_roles = [role for role in roles if cls.get_priority(role) is not None]
if not valid_roles:
return None
return max(valid_roles, key=cls.get_priority)
class LinkRoleChoices(PriorityTextChoices):
"""Defines the possible roles a link can offer on a document."""
READER = "reader", _("Reader") # Can read
EDITOR = "editor", _("Editor") # Can read and edit
class RoleChoices(PriorityTextChoices):
"""Defines the possible roles a user can have in a resource."""
READER = "reader", _("Reader") # Can read
EDITOR = "editor", _("Editor") # Can read and edit
ADMIN = "administrator", _("Administrator") # Can read, edit, delete and share
OWNER = "owner", _("Owner")
PRIVILEGED_ROLES = [RoleChoices.ADMIN, RoleChoices.OWNER]
class LinkReachChoices(PriorityTextChoices):
"""Defines types of access for links"""
RESTRICTED = (
"restricted",
_("Restricted"),
) # Only users with a specific access can read/edit the document
AUTHENTICATED = (
"authenticated",
_("Authenticated"),
) # Any authenticated user can access the document
PUBLIC = "public", _("Public") # Even anonymous users can access the document
@classmethod
def get_select_options(cls, link_reach, link_role):
"""
Determines the valid select options for link reach and link role depending on the
list of ancestors' link reach/role definitions.
Returns:
Dictionary mapping possible reach levels to their corresponding possible roles.
"""
# If no ancestors, return all options
if not link_reach:
return {
reach: LinkRoleChoices.values if reach != cls.RESTRICTED else None
for reach in cls.values
}
# Initialize the result for all reaches with possible roles
result = {
reach: set(LinkRoleChoices.values) if reach != cls.RESTRICTED else None
for reach in cls.values
}
# Handle special rules directly with early returns for efficiency
if link_role == LinkRoleChoices.EDITOR:
# Rule 1: public/editor → override everything
if link_reach == cls.PUBLIC:
return {cls.PUBLIC: [LinkRoleChoices.EDITOR]}
# Rule 2: authenticated/editor
if link_reach == cls.AUTHENTICATED:
result[cls.AUTHENTICATED].discard(LinkRoleChoices.READER)
result.pop(cls.RESTRICTED, None)
if link_role == LinkRoleChoices.READER:
# Rule 3: public/reader
if link_reach == cls.PUBLIC:
result.pop(cls.AUTHENTICATED, None)
result.pop(cls.RESTRICTED, None)
# Rule 4: authenticated/reader
if link_reach == cls.AUTHENTICATED:
result.pop(cls.RESTRICTED, None)
# Convert sets to ordered lists where applicable
return {
reach: sorted(roles, key=LinkRoleChoices.get_priority) if roles else roles
for reach, roles in result.items()
}