Make url a property on MenuItem/PluginMenuItem etc, overridable via a setter
Some checks failed
CI / build (20.x, 3.10) (push) Has been cancelled
CI / build (20.x, 3.11) (push) Has been cancelled
CI / build (20.x, 3.12) (push) Has been cancelled

This commit is contained in:
Brian Tiemann 2025-07-13 20:19:46 -04:00
parent 7338898ccb
commit 0c95ac6b1a
3 changed files with 56 additions and 7 deletions

View File

@ -1,6 +1,8 @@
from dataclasses import dataclass
from typing import Sequence, Optional
from django.urls import reverse
__all__ = (
'get_model_item',
@ -22,20 +24,46 @@ class MenuItemButton:
link: str
title: str
icon_class: str
_url: Optional[str] = None
permissions: Optional[Sequence[str]] = ()
color: Optional[str] = None
def __post_init__(self):
if self.link:
self._url = reverse(self.link)
@property
def url(self):
return self._url
@url.setter
def url(self, value):
self._url = value
@dataclass
class MenuItem:
link: str
link_text: str
_url: Optional[str] = None
permissions: Optional[Sequence[str]] = ()
auth_required: Optional[bool] = False
staff_only: Optional[bool] = False
buttons: Optional[Sequence[MenuItemButton]] = ()
def __post_init__(self):
if self.link:
self._url = reverse(self.link)
@property
def url(self):
return self._url
@url.setter
def url(self, value):
self._url = value
@dataclass
class MenuGroup:

View File

@ -1,3 +1,4 @@
from django.urls import reverse
from django.utils.text import slugify
from django.utils.translation import gettext as _
@ -33,20 +34,22 @@ class PluginMenuItem:
specifying additional link buttons that appear to the right of the item in the van menu.
Links are specified as Django reverse URL strings suitable for rendering via {% url item.link %}.
Alternatively, a pre-generated url can be specified which will be rendered literally.
Alternatively, a pre-generated url can be set on the object which will be rendered literally.
Buttons are each specified as a list of PluginMenuButton instances.
"""
permissions = []
buttons = []
_url = None
def __init__(
self, link, link_text, url=None, auth_required=False, staff_only=False, permissions=None, buttons=None
self, link, link_text, auth_required=False, staff_only=False, permissions=None, buttons=None
):
self.link = link
self.link_text = link_text
self.url = url
self.auth_required = auth_required
self.staff_only = staff_only
if link:
self._url = reverse(link)
if permissions is not None:
if type(permissions) not in (list, tuple):
raise TypeError(_("Permissions must be passed as a tuple or list."))
@ -56,6 +59,14 @@ class PluginMenuItem:
raise TypeError(_("Buttons must be passed as a tuple or list."))
self.buttons = buttons
@property
def url(self):
return self._url
@url.setter
def url(self, value):
self._url = value
class PluginMenuButton:
"""
@ -64,12 +75,14 @@ class PluginMenuButton:
"""
color = ButtonColorChoices.DEFAULT
permissions = []
_url = None
def __init__(self, link, title, icon_class, url=None, color=None, permissions=None):
def __init__(self, link, title, icon_class, color=None, permissions=None):
self.link = link
self.title = title
self.icon_class = icon_class
self.url = url
if link:
self._url = reverse(link)
if permissions is not None:
if type(permissions) not in (list, tuple):
raise TypeError(_("Permissions must be passed as a tuple or list."))
@ -78,3 +91,11 @@ class PluginMenuButton:
if color not in ButtonColorChoices.values():
raise ValueError(_("Button color must be a choice within ButtonColorChoices."))
self.color = color
@property
def url(self):
return self._url
@url.setter
def url(self, value):
self._url = value

View File

@ -41,11 +41,11 @@
</div>
{% for item, buttons in items %}
<div class="dropdown-item d-flex justify-content-between ps-3 py-0">
<a href="{% if item.url %}{{ item.url }}{% else %}{% url item.link %}{% endif %}" class="d-inline-flex flex-fill py-1">{{ item.link_text }}</a>
<a href="{{ item.url }}" class="d-inline-flex flex-fill py-1">{{ item.link_text }}</a>
{% if buttons %}
<div class="btn-group ms-1">
{% for button in buttons %}
<a href="{% if button.url %}{{ button.url }}{% else %}{% url button.link %}{% endif %}" class="btn btn-sm btn-{{ button.color|default:"outline" }} lh-2 px-2" title="{{ button.title }}">
<a href="{{ button.url }}" class="btn btn-sm btn-{{ button.color|default:"outline" }} lh-2 px-2" title="{{ button.title }}">
<i class="{{ button.icon_class }}"></i>
</a>
{% endfor %}