django-unfold 0.58.0__py3-none-any.whl → 0.60.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {django_unfold-0.58.0.dist-info → django_unfold-0.60.0.dist-info}/METADATA +3 -2
- {django_unfold-0.58.0.dist-info → django_unfold-0.60.0.dist-info}/RECORD +60 -50
- unfold/admin.py +45 -13
- unfold/checks.py +24 -2
- unfold/contrib/filters/templates/unfold/filters/filters_numeric_slider.html +2 -2
- unfold/contrib/inlines/admin.py +11 -6
- unfold/contrib/inlines/forms.py +3 -1
- unfold/contrib/location_field/__init__.py +0 -0
- unfold/contrib/location_field/apps.py +6 -0
- unfold/contrib/location_field/templates/location_field/map_widget.html +5 -0
- unfold/decorators.py +27 -13
- unfold/fields.py +18 -20
- unfold/forms.py +99 -3
- unfold/layout.py +16 -0
- unfold/mixins/action_model_admin.py +22 -14
- unfold/mixins/base_model_admin.py +15 -1
- unfold/paginator.py +12 -1
- unfold/settings.py +8 -1
- unfold/sites.py +27 -29
- unfold/static/admin/js/admin/RelatedObjectLookups.js +9 -3
- unfold/static/unfold/css/styles.css +1 -1
- unfold/styles.css +12 -16
- unfold/templates/admin/app_list.html +4 -1
- unfold/templates/admin/base.html +1 -1
- unfold/templates/admin/change_form.html +2 -1
- unfold/templates/admin/edit_inline/stacked.html +12 -8
- unfold/templates/admin/edit_inline/tabular.html +2 -0
- unfold/templates/admin/includes/fieldset.html +9 -3
- unfold/templates/admin/login.html +45 -88
- unfold/templates/admin/pagination.html +1 -1
- unfold/templates/unfold/components/button.html +10 -1
- unfold/templates/unfold/helpers/account_links.html +14 -6
- unfold/templates/unfold/helpers/app_list.html +5 -2
- unfold/templates/unfold/helpers/app_list_default.html +2 -1
- unfold/templates/unfold/helpers/change_list_actions.html +2 -2
- unfold/templates/unfold/helpers/change_list_filter_actions.html +1 -1
- unfold/templates/unfold/helpers/edit_inline/tabular_heading.html +1 -1
- unfold/templates/unfold/helpers/empty_results.html +2 -2
- unfold/templates/unfold/helpers/field.html +5 -3
- unfold/templates/unfold/helpers/header.html +1 -1
- unfold/templates/unfold/helpers/language_form.html +10 -0
- unfold/templates/unfold/helpers/language_switch.html +17 -19
- unfold/templates/unfold/helpers/navigation.html +1 -1
- unfold/templates/unfold/helpers/pagination_infinite.html +3 -3
- unfold/templates/unfold/helpers/pagination_inline.html +28 -0
- unfold/templates/unfold/helpers/theme_switch.html +29 -27
- unfold/templates/unfold/helpers/unauthenticated_header.html +15 -0
- unfold/templates/unfold/helpers/unauthenticated_title.html +11 -0
- unfold/templates/unfold/helpers/userlinks.html +2 -6
- unfold/templates/unfold/helpers/welcomemsg.html +9 -7
- unfold/templates/unfold/layouts/unauthenticated.html +37 -0
- unfold/templates/unfold/widgets/select.html +1 -1
- unfold/templates/unfold/widgets/text.html +28 -0
- unfold/templates/unfold_crispy/layout/fieldset.html +3 -1
- unfold/templates/unfold_crispy/layout/fieldset_subheader.html +3 -0
- unfold/templatetags/unfold.py +37 -2
- unfold/utils.py +2 -2
- unfold/widgets.py +49 -3
- {django_unfold-0.58.0.dist-info → django_unfold-0.60.0.dist-info}/LICENSE.md +0 -0
- {django_unfold-0.58.0.dist-info → django_unfold-0.60.0.dist-info}/WHEEL +0 -0
unfold/fields.py
CHANGED
@@ -17,33 +17,24 @@ from django.utils.module_loading import import_string
|
|
17
17
|
from django.utils.safestring import SafeText, mark_safe
|
18
18
|
from django.utils.text import capfirst
|
19
19
|
|
20
|
-
from unfold.mixins import BaseModelAdminMixin
|
21
20
|
from unfold.settings import get_config
|
22
21
|
from unfold.utils import display_for_field, prettify_json
|
23
|
-
from unfold.widgets import
|
22
|
+
from unfold.widgets import (
|
23
|
+
CHECKBOX_LABEL_CLASSES,
|
24
|
+
INPUT_CLASSES,
|
25
|
+
LABEL_CLASSES,
|
26
|
+
)
|
24
27
|
|
25
28
|
|
26
29
|
class UnfoldAdminReadonlyField(helpers.AdminReadonlyField):
|
27
30
|
def label_tag(self) -> SafeText:
|
28
|
-
from .admin import ModelAdmin
|
29
|
-
|
30
|
-
if not isinstance(self.model_admin, ModelAdmin) and not isinstance(
|
31
|
-
self.model_admin, BaseModelAdminMixin
|
32
|
-
):
|
33
|
-
return super().label_tag()
|
34
|
-
|
35
31
|
attrs = {
|
36
32
|
"class": " ".join(LABEL_CLASSES + ["mb-2"]),
|
37
33
|
}
|
38
34
|
|
39
35
|
label = self.field["label"]
|
40
36
|
|
41
|
-
return format_html(
|
42
|
-
"<label{}>{}{}</label>",
|
43
|
-
flatatt(attrs),
|
44
|
-
capfirst(label),
|
45
|
-
self.form.label_suffix,
|
46
|
-
)
|
37
|
+
return format_html("<label{}>{}</label>", flatatt(attrs), capfirst(label))
|
47
38
|
|
48
39
|
def is_json(self) -> bool:
|
49
40
|
field, obj, model_admin = (
|
@@ -140,7 +131,7 @@ class UnfoldAdminReadonlyField(helpers.AdminReadonlyField):
|
|
140
131
|
):
|
141
132
|
result_repr = self.get_admin_url(f.remote_field, value)
|
142
133
|
elif isinstance(f, models.JSONField):
|
143
|
-
formatted_output = prettify_json(value)
|
134
|
+
formatted_output = prettify_json(value, f.encoder)
|
144
135
|
|
145
136
|
if formatted_output:
|
146
137
|
return formatted_output
|
@@ -174,12 +165,19 @@ class UnfoldAdminReadonlyField(helpers.AdminReadonlyField):
|
|
174
165
|
|
175
166
|
|
176
167
|
class UnfoldAdminField(helpers.AdminField):
|
168
|
+
def __init__(self, *args, **kwargs):
|
169
|
+
super().__init__(*args, **kwargs)
|
170
|
+
|
171
|
+
try:
|
172
|
+
from location_field.widgets import LocationWidget
|
173
|
+
|
174
|
+
if isinstance(self.field.field.widget, LocationWidget):
|
175
|
+
self.field.field.widget.attrs["class"] = " ".join(INPUT_CLASSES)
|
176
|
+
except ImportError:
|
177
|
+
pass
|
178
|
+
|
177
179
|
def label_tag(self) -> SafeText:
|
178
180
|
classes = []
|
179
|
-
if not self.field.field.widget.__class__.__name__.startswith(
|
180
|
-
"Unfold"
|
181
|
-
) and not self.field.field.widget.template_name.startswith("unfold"):
|
182
|
-
return super().label_tag()
|
183
181
|
|
184
182
|
# TODO load config from current AdminSite (override Fieldline.__iter__ method)
|
185
183
|
for lang, flag in get_config()["EXTENSIONS"]["modeltranslation"][
|
unfold/forms.py
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
from collections.abc import Generator
|
1
2
|
from typing import Optional
|
2
3
|
|
3
4
|
from django import forms
|
@@ -11,14 +12,23 @@ from django.contrib.auth.forms import (
|
|
11
12
|
AdminPasswordChangeForm as BaseAdminPasswordChangeForm,
|
12
13
|
)
|
13
14
|
from django.contrib.auth.models import User
|
15
|
+
from django.contrib.contenttypes.forms import BaseGenericInlineFormSet
|
16
|
+
from django.core.paginator import Page, Paginator
|
17
|
+
from django.db.models import QuerySet
|
18
|
+
from django.forms import BaseInlineFormSet
|
19
|
+
from django.http import HttpRequest
|
20
|
+
|
21
|
+
from unfold.fields import UnfoldAdminField, UnfoldAdminReadonlyField
|
14
22
|
|
15
23
|
try:
|
16
24
|
from django.contrib.auth.forms import AdminUserCreationForm as BaseUserCreationForm
|
17
25
|
except ImportError:
|
18
26
|
from django.contrib.auth.forms import UserCreationForm as BaseUserCreationForm
|
27
|
+
from django.contrib.admin.helpers import AdminForm as BaseAdminForm
|
28
|
+
from django.contrib.admin.helpers import Fieldline as BaseFieldline
|
29
|
+
from django.contrib.admin.helpers import Fieldset as BaseFieldset
|
19
30
|
from django.contrib.auth.forms import ReadOnlyPasswordHashWidget
|
20
31
|
from django.contrib.auth.forms import UserChangeForm as BaseUserChangeForm
|
21
|
-
from django.http import HttpRequest
|
22
32
|
from django.utils.safestring import mark_safe
|
23
33
|
from django.utils.translation import gettext_lazy as _
|
24
34
|
|
@@ -43,15 +53,17 @@ class ActionForm(forms.Form):
|
|
43
53
|
"class": " ".join(
|
44
54
|
[
|
45
55
|
"appearance-none",
|
46
|
-
"bg-white/20",
|
56
|
+
"!bg-white/20",
|
47
57
|
"font-medium",
|
48
58
|
"grow",
|
49
59
|
"px-3",
|
50
60
|
"py-2",
|
51
61
|
"pr-8",
|
52
62
|
"rounded-default",
|
53
|
-
"text-white",
|
63
|
+
"!text-white",
|
54
64
|
"truncate",
|
65
|
+
"!outline-primary-400",
|
66
|
+
"dark:!outline-primary-700",
|
55
67
|
"*:text-base-700",
|
56
68
|
"lg:w-72",
|
57
69
|
]
|
@@ -144,3 +156,87 @@ class AdminOwnPasswordChangeForm(BaseAdminOwnPasswordChangeForm):
|
|
144
156
|
self.fields["old_password"].widget.attrs["class"] = " ".join(INPUT_CLASSES)
|
145
157
|
self.fields["new_password1"].widget.attrs["class"] = " ".join(INPUT_CLASSES)
|
146
158
|
self.fields["new_password2"].widget.attrs["class"] = " ".join(INPUT_CLASSES)
|
159
|
+
|
160
|
+
|
161
|
+
class AdminForm(BaseAdminForm):
|
162
|
+
def __iter__(self) -> Generator["Fieldset", None, None]:
|
163
|
+
for name, options in self.fieldsets:
|
164
|
+
yield Fieldset(
|
165
|
+
self.form,
|
166
|
+
name,
|
167
|
+
readonly_fields=self.readonly_fields,
|
168
|
+
model_admin=self.model_admin,
|
169
|
+
**options,
|
170
|
+
)
|
171
|
+
|
172
|
+
|
173
|
+
class Fieldset(BaseFieldset):
|
174
|
+
def __iter__(self) -> Generator["Fieldline", None, None]:
|
175
|
+
for field in self.fields:
|
176
|
+
yield Fieldline(
|
177
|
+
self.form, field, self.readonly_fields, model_admin=self.model_admin
|
178
|
+
)
|
179
|
+
|
180
|
+
|
181
|
+
class Fieldline(BaseFieldline):
|
182
|
+
def __iter__(
|
183
|
+
self,
|
184
|
+
) -> Generator["UnfoldAdminReadonlyField | UnfoldAdminField", None, None]:
|
185
|
+
for i, field in enumerate(self.fields):
|
186
|
+
if field in self.readonly_fields:
|
187
|
+
yield UnfoldAdminReadonlyField(
|
188
|
+
self.form, field, is_first=(i == 0), model_admin=self.model_admin
|
189
|
+
)
|
190
|
+
else:
|
191
|
+
yield UnfoldAdminField(self.form, field, is_first=(i == 0))
|
192
|
+
|
193
|
+
|
194
|
+
class PaginationFormSetMixin:
|
195
|
+
queryset: Optional[QuerySet] = None
|
196
|
+
request: Optional[HttpRequest] = None
|
197
|
+
per_page: Optional[int] = None
|
198
|
+
|
199
|
+
def __init__(
|
200
|
+
self,
|
201
|
+
request: Optional[HttpRequest] = None,
|
202
|
+
per_page: Optional[int] = None,
|
203
|
+
*args,
|
204
|
+
**kwargs,
|
205
|
+
):
|
206
|
+
self.request = request
|
207
|
+
self.per_page = per_page
|
208
|
+
|
209
|
+
super().__init__(*args, **kwargs)
|
210
|
+
|
211
|
+
if self.per_page:
|
212
|
+
self.paginator = Paginator(self.queryset, self.per_page)
|
213
|
+
self.page = self.get_page(self.paginator, self.get_page_num())
|
214
|
+
self._queryset = self.page.object_list
|
215
|
+
|
216
|
+
def get_pagination_key(self) -> str:
|
217
|
+
return f"{self.prefix}-page"
|
218
|
+
|
219
|
+
def get_page_num(self) -> int:
|
220
|
+
page = self.request.GET.get(self.get_pagination_key())
|
221
|
+
if page and page.isnumeric() and page > "0":
|
222
|
+
return int(page)
|
223
|
+
|
224
|
+
page = self.request.POST.get(self.get_pagination_key())
|
225
|
+
if page and page.isnumeric() and page > "0":
|
226
|
+
return int(page)
|
227
|
+
|
228
|
+
return 1
|
229
|
+
|
230
|
+
def get_page(self, paginator: Paginator, page: int) -> Page:
|
231
|
+
if page <= paginator.num_pages:
|
232
|
+
return paginator.page(page)
|
233
|
+
|
234
|
+
return paginator.page(1)
|
235
|
+
|
236
|
+
|
237
|
+
class PaginationInlineFormSet(PaginationFormSetMixin, BaseInlineFormSet):
|
238
|
+
pass
|
239
|
+
|
240
|
+
|
241
|
+
class PaginationGenericInlineFormSet(PaginationFormSetMixin, BaseGenericInlineFormSet):
|
242
|
+
pass
|
unfold/layout.py
CHANGED
@@ -25,6 +25,22 @@ class Button(ButtonClassesMixin, BaseInput):
|
|
25
25
|
input_type = "button"
|
26
26
|
|
27
27
|
|
28
|
+
class FieldsetSubheader(LayoutObject):
|
29
|
+
template = "unfold_crispy/layout/fieldset_subheader.html"
|
30
|
+
|
31
|
+
def __init__(self, title=None, *args, **kwargs):
|
32
|
+
self.title = title
|
33
|
+
super().__init__(*args, **kwargs)
|
34
|
+
|
35
|
+
def render(self, form, context, template_pack=TEMPLATE_PACK, **kwargs):
|
36
|
+
return render_to_string(
|
37
|
+
self.template,
|
38
|
+
{
|
39
|
+
"title": self.title,
|
40
|
+
},
|
41
|
+
)
|
42
|
+
|
43
|
+
|
28
44
|
class Hr(LayoutObject):
|
29
45
|
template = "unfold_crispy/layout/hr.html"
|
30
46
|
|
@@ -316,19 +316,27 @@ class ActionModelAdminMixin:
|
|
316
316
|
filtered_actions.append(action)
|
317
317
|
continue
|
318
318
|
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
319
|
+
permission_rules = []
|
320
|
+
|
321
|
+
for permission in action.method.allowed_permissions:
|
322
|
+
if "." in permission:
|
323
|
+
permission_rules.append(permission)
|
324
|
+
else:
|
325
|
+
permission_rules.append(
|
326
|
+
getattr(self, f"has_{permission}_permission")
|
327
|
+
)
|
328
|
+
|
329
|
+
permission_checks = []
|
330
|
+
|
331
|
+
for permission_rule in permission_rules:
|
332
|
+
if isinstance(permission_rule, str) and "." in permission_rule:
|
333
|
+
permission_checks.append(request.user.has_perm(permission_rule))
|
334
|
+
elif object_id:
|
335
|
+
permission_checks.append(permission_rule(request, object_id))
|
336
|
+
else:
|
337
|
+
permission_checks.append(permission_rule(request))
|
338
|
+
|
339
|
+
if all(permission_checks):
|
340
|
+
filtered_actions.append(action)
|
333
341
|
|
334
342
|
return filtered_actions
|
@@ -1,6 +1,7 @@
|
|
1
1
|
import copy
|
2
|
-
from typing import Optional
|
2
|
+
from typing import Any, Optional
|
3
3
|
|
4
|
+
from django.contrib.admin import helpers
|
4
5
|
from django.contrib.admin.sites import AdminSite
|
5
6
|
from django.contrib.admin.widgets import RelatedFieldWidgetWrapper
|
6
7
|
from django.db import models
|
@@ -27,6 +28,19 @@ class BaseModelAdminMixin:
|
|
27
28
|
|
28
29
|
super().__init__(model, admin_site)
|
29
30
|
|
31
|
+
def changeform_view(
|
32
|
+
self,
|
33
|
+
request: HttpRequest,
|
34
|
+
object_id: Optional[str] = None,
|
35
|
+
form_url: str = "",
|
36
|
+
extra_context: Optional[dict[str, bool]] = None,
|
37
|
+
) -> Any:
|
38
|
+
from unfold.forms import AdminForm, Fieldline
|
39
|
+
|
40
|
+
helpers.AdminForm = AdminForm
|
41
|
+
helpers.Fieldline = Fieldline
|
42
|
+
return super().changeform_view(request, object_id, form_url, extra_context)
|
43
|
+
|
30
44
|
def formfield_for_choice_field(
|
31
45
|
self, db_field: Field, request: HttpRequest, **kwargs
|
32
46
|
) -> TypedChoiceField:
|
unfold/paginator.py
CHANGED
@@ -1,10 +1,21 @@
|
|
1
|
-
from django.core.paginator import Paginator
|
1
|
+
from django.core.paginator import Page, Paginator
|
2
2
|
from django.utils.functional import cached_property
|
3
3
|
|
4
4
|
|
5
|
+
class InfinitePage(Page):
|
6
|
+
def has_next(self):
|
7
|
+
if len(self.object_list) == 0:
|
8
|
+
return False
|
9
|
+
|
10
|
+
return self.number < self.paginator.num_pages
|
11
|
+
|
12
|
+
|
5
13
|
class InfinitePaginator(Paginator):
|
6
14
|
template_name = "unfold/helpers/pagination_infinite.html"
|
7
15
|
|
8
16
|
@cached_property
|
9
17
|
def count(self):
|
10
18
|
return 9_999_999_999
|
19
|
+
|
20
|
+
def _get_page(self, *args, **kwargs):
|
21
|
+
return InfinitePage(*args, **kwargs)
|
unfold/settings.py
CHANGED
@@ -57,10 +57,17 @@ CONFIG_DEFAULTS = {
|
|
57
57
|
"ENVIRONMENT_TITLE_PREFIX": None,
|
58
58
|
"STYLES": [],
|
59
59
|
"SCRIPTS": [],
|
60
|
+
"ACCOUNT": {
|
61
|
+
"navigation": [],
|
62
|
+
},
|
63
|
+
"LANGUAGES": {
|
64
|
+
"action": None,
|
65
|
+
"navigation": [],
|
66
|
+
},
|
60
67
|
"SIDEBAR": {
|
61
68
|
"show_search": False,
|
62
69
|
"show_all_applications": False,
|
63
|
-
"navigation":
|
70
|
+
"navigation": [],
|
64
71
|
},
|
65
72
|
"TABS": [],
|
66
73
|
"LOGIN": {
|
unfold/sites.py
CHANGED
@@ -4,15 +4,12 @@ from typing import Any, Callable, Optional, Union
|
|
4
4
|
from urllib.parse import parse_qs, urlparse
|
5
5
|
|
6
6
|
from django.contrib.admin import AdminSite
|
7
|
-
from django.contrib.auth import REDIRECT_FIELD_NAME
|
8
7
|
from django.core.validators import EMPTY_VALUES
|
9
8
|
from django.http import HttpRequest, HttpResponse
|
10
9
|
from django.template.response import TemplateResponse
|
11
10
|
from django.urls import URLPattern, path, reverse, reverse_lazy
|
12
|
-
from django.utils.decorators import method_decorator
|
13
11
|
from django.utils.functional import lazy
|
14
12
|
from django.utils.module_loading import import_string
|
15
|
-
from django.views.decorators.cache import never_cache
|
16
13
|
|
17
14
|
from unfold.dataclasses import DropdownItem, Favicon
|
18
15
|
|
@@ -91,6 +88,9 @@ class UnfoldAdminSite(AdminSite):
|
|
91
88
|
"site_icon": self._get_theme_images("SITE_ICON", request),
|
92
89
|
"site_symbol": self._get_config("SITE_SYMBOL", request),
|
93
90
|
"site_favicons": self._get_favicons("SITE_FAVICONS", request),
|
91
|
+
"login_image": self._get_value(
|
92
|
+
get_config(self.settings_name)["LOGIN"].get("image"), request
|
93
|
+
),
|
94
94
|
"show_history": self._get_config("SHOW_HISTORY", request),
|
95
95
|
"show_view_on_site": self._get_config("SHOW_VIEW_ON_SITE", request),
|
96
96
|
"show_languages": self._get_config("SHOW_LANGUAGES", request),
|
@@ -102,6 +102,13 @@ class UnfoldAdminSite(AdminSite):
|
|
102
102
|
"environment_title_prefix": self._get_config(
|
103
103
|
"ENVIRONMENT_TITLE_PREFIX", request
|
104
104
|
),
|
105
|
+
"languages_list": self._get_value(
|
106
|
+
self._get_config("LANGUAGES", request).get("navigation"), request
|
107
|
+
),
|
108
|
+
"languages_action": self._get_value(
|
109
|
+
self._get_config("LANGUAGES", request).get("action"), request
|
110
|
+
),
|
111
|
+
"account_links": self._get_account_links(request),
|
105
112
|
"tab_list": self.get_tabs_list(request),
|
106
113
|
"styles": self._get_list("STYLES", request),
|
107
114
|
"scripts": self._get_list("SCRIPTS", request),
|
@@ -191,32 +198,6 @@ class UnfoldAdminSite(AdminSite):
|
|
191
198
|
},
|
192
199
|
)
|
193
200
|
|
194
|
-
@method_decorator(never_cache)
|
195
|
-
@login_not_required
|
196
|
-
def login(
|
197
|
-
self, request: HttpRequest, extra_context: Optional[dict[str, Any]] = None
|
198
|
-
) -> HttpResponse:
|
199
|
-
extra_context = {} if extra_context is None else extra_context
|
200
|
-
image = self._get_value(
|
201
|
-
get_config(self.settings_name)["LOGIN"].get("image"), request
|
202
|
-
)
|
203
|
-
|
204
|
-
redirect_field_name = self._get_value(
|
205
|
-
get_config(self.settings_name)["LOGIN"].get("redirect_after"), request
|
206
|
-
)
|
207
|
-
|
208
|
-
if image not in EMPTY_VALUES:
|
209
|
-
extra_context.update(
|
210
|
-
{
|
211
|
-
"image": image,
|
212
|
-
}
|
213
|
-
)
|
214
|
-
|
215
|
-
if redirect_field_name not in EMPTY_VALUES:
|
216
|
-
extra_context.update({REDIRECT_FIELD_NAME: redirect_field_name})
|
217
|
-
|
218
|
-
return super().login(request, extra_context)
|
219
|
-
|
220
201
|
def password_change(
|
221
202
|
self, request: HttpRequest, extra_context: Optional[dict[str, Any]] = None
|
222
203
|
) -> HttpResponse:
|
@@ -296,6 +277,23 @@ class UnfoldAdminSite(AdminSite):
|
|
296
277
|
|
297
278
|
return allowed_items
|
298
279
|
|
280
|
+
def _get_account_links(self, request: HttpRequest) -> list[dict[str, Any]]:
|
281
|
+
links = []
|
282
|
+
|
283
|
+
navigation = self._get_value(
|
284
|
+
get_config(self.settings_name)["ACCOUNT"].get("navigation"), request
|
285
|
+
)
|
286
|
+
|
287
|
+
for item in navigation:
|
288
|
+
links.append(
|
289
|
+
{
|
290
|
+
"title": self._get_value(item["title"], request),
|
291
|
+
"link": self._get_value(item["link"], request),
|
292
|
+
}
|
293
|
+
)
|
294
|
+
|
295
|
+
return links
|
296
|
+
|
299
297
|
def get_tabs_list(self, request: HttpRequest) -> list[dict[str, Any]]:
|
300
298
|
tabs = copy.deepcopy(self._get_config("TABS", request))
|
301
299
|
|
@@ -74,9 +74,15 @@
|
|
74
74
|
|
75
75
|
function updateRelatedObjectLinks(triggeringLink) {
|
76
76
|
const $this = $(triggeringLink);
|
77
|
-
|
78
|
-
|
79
|
-
|
77
|
+
// !CHANGED from original
|
78
|
+
// const siblings = $this.nextAll(
|
79
|
+
// ".view-related, .change-related, .delete-related"
|
80
|
+
// );
|
81
|
+
|
82
|
+
const siblings = $this
|
83
|
+
.closest(".related-widget-wrapper")
|
84
|
+
.find(".view-related, .change-related, .delete-related");
|
85
|
+
|
80
86
|
if (!siblings.length) {
|
81
87
|
return;
|
82
88
|
}
|