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.
113 lines
3.9 KiB
Python
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()
|
|
}
|