django-unfold 0.38.0__py3-none-any.whl → 0.40.0__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- django_unfold-0.40.0.dist-info/METADATA +69 -0
- {django_unfold-0.38.0.dist-info → django_unfold-0.40.0.dist-info}/RECORD +24 -23
- unfold/admin.py +4 -0
- unfold/contrib/guardian/templates/unfold/guardian/group_form.html +2 -2
- unfold/contrib/guardian/templates/unfold/guardian/user_form.html +3 -3
- unfold/forms.py +12 -9
- unfold/sites.py +12 -0
- unfold/static/admin/js/admin/RelatedObjectLookups.js +295 -0
- unfold/static/unfold/css/styles.css +1 -1
- unfold/static/unfold/js/app.js +3 -1
- unfold/templates/admin/app_list.html +1 -1
- unfold/templates/admin/change_form.html +8 -1
- unfold/templates/admin/change_list.html +8 -0
- unfold/templates/admin/edit_inline/stacked.html +59 -66
- unfold/templates/admin/edit_inline/tabular.html +127 -135
- unfold/templates/admin/login.html +2 -0
- unfold/templates/unfold/components/button.html +4 -3
- unfold/templates/unfold/components/card.html +1 -1
- unfold/templates/unfold/components/progress.html +15 -15
- unfold/templates/unfold/components/separator.html +1 -1
- unfold/templates/unfold/helpers/app_list.html +2 -2
- unfold/templates/unfold/helpers/welcomemsg.html +1 -1
- django_unfold-0.38.0.dist-info/METADATA +0 -70
- {django_unfold-0.38.0.dist-info → django_unfold-0.40.0.dist-info}/LICENSE.md +0 -0
- {django_unfold-0.38.0.dist-info → django_unfold-0.40.0.dist-info}/WHEEL +0 -0
@@ -0,0 +1,69 @@
|
|
1
|
+
Metadata-Version: 2.1
|
2
|
+
Name: django-unfold
|
3
|
+
Version: 0.40.0
|
4
|
+
Summary: Modern Django admin theme for seamless interface development
|
5
|
+
Home-page: https://unfoldadmin.com
|
6
|
+
License: MIT
|
7
|
+
Keywords: django,admin,tailwind,theme
|
8
|
+
Requires-Python: >=3.8
|
9
|
+
Classifier: Environment :: Web Environment
|
10
|
+
Classifier: Framework :: Django
|
11
|
+
Classifier: Intended Audience :: Developers
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
13
|
+
Classifier: Operating System :: OS Independent
|
14
|
+
Classifier: Programming Language :: Python
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
16
|
+
Classifier: Programming Language :: Python :: 3.8
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
21
|
+
Requires-Dist: django (>=3.2)
|
22
|
+
Project-URL: Repository, https://github.com/unfoldadmin/django-unfold
|
23
|
+
Description-Content-Type: text/markdown
|
24
|
+
|
25
|
+

|
26
|
+
|
27
|
+
## Unfold Django Admin Theme
|
28
|
+
|
29
|
+
[](https://pypi.org/project/django-unfold/)
|
30
|
+
[](https://github.com/unfoldadmin/django-unfold/actions?query=workflow%3Arelease)
|
31
|
+

|
32
|
+

|
33
|
+
|
34
|
+
Unfold is a theme for Django admin that incorporates common best practices for building full-fledged admin areas. It is designed to work on top of the default administration provided by Django.
|
35
|
+
|
36
|
+
- **Documentation:** Full docs are available at [unfoldadmin.com](https://unfoldadmin.com?utm_medium=github&utm_source=unfold).
|
37
|
+
- **Unfold:** Demo site is available at [unfoldadmin.com](https://unfoldadmin.com?utm_medium=github&utm_source=unfold).
|
38
|
+
- **Formula:** Repository with demo implementation at [github.com/unfoldadmin/formula](https://github.com/unfoldadmin/formula?utm_medium=github&utm_source=unfold).
|
39
|
+
- **Turbo:** Django & Next.js boilerplate implementing Unfold at [github.com/unfoldadmin/turbo](https://github.com/unfoldadmin/turbo?utm_medium=github&utm_source=unfold).
|
40
|
+
|
41
|
+
## Are you using Unfold and need help?
|
42
|
+
|
43
|
+
Have you decided to start using Unfold but don’t have time to make the switch from the native Django admin? [Get in touch with us](https://unfoldadmin.com/consulting?utm_medium=github&utm_source=unfold) and let’s supercharge your development with our expertise.
|
44
|
+
|
45
|
+
## Features
|
46
|
+
|
47
|
+
- **Visual**: Provides a new user interface based on the Tailwind CSS framework.
|
48
|
+
- **Sidebar:** Simplifies the creation of sidebar navigation with icons, collapsibles, and more.
|
49
|
+
- **Dark mode:** Supports both light and dark mode versions.
|
50
|
+
- **Actions:** Offers multiple ways to define actions within different parts of the admin interface.
|
51
|
+
- **Filters:** Custom dropdowns, numeric, datetime, and text fields.
|
52
|
+
- **Dashboard:** Includes helpers for creating custom dashboard pages.
|
53
|
+
- **Components:** Reusable UI components such as cards, buttons, and charts.
|
54
|
+
- **WYSIWYG widget:** Built-in support for WYSIWYG (Trix).
|
55
|
+
- **Array widget:** Built-in widget for `django.contrib.postgres.fields.ArrayField`.
|
56
|
+
- **Inline tabs:** Groups inlines into tab navigation in the change form.
|
57
|
+
- **Model tabs:** Allows defining custom tab navigation for models.
|
58
|
+
- **Fieldset tabs:** Merges multiple fieldsets into tabs in the change form.
|
59
|
+
- **Environment label**: Distinguishes between environments by displaying a label.
|
60
|
+
- **Nonrelated inlines**: Displays nonrelated models as inlines in the change form.
|
61
|
+
- **Favicons**: Built-in support for configuring various site favicons.
|
62
|
+
- **Colors:** Allows customization of the default color scheme.
|
63
|
+
- **Changeform modes:** Displays fields in compressed mode in the change form.
|
64
|
+
- **Parallel admin**: Supports [running the default admin](https://unfoldadmin.com/blog/migrating-django-admin-unfold/?utm_medium=github&utm_source=unfold) alongside Unfold.
|
65
|
+
- **Third party packages:** Default support for multiple popular applications.
|
66
|
+
- **Configuration:** Most basic options can be changed in `settings.py`.
|
67
|
+
- **Dependencies:** Fully based on `django.contrib.admin`.
|
68
|
+
- **VS Code**: Project configuration and development container included.
|
69
|
+
|
@@ -1,5 +1,5 @@
|
|
1
1
|
unfold/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
-
unfold/admin.py,sha256=
|
2
|
+
unfold/admin.py,sha256=GgzQctPm2zdCOd4NHfhWXFDtqmY_byvGOkFuKc7ma1Q,19757
|
3
3
|
unfold/apps.py,sha256=SlBXPYrUd2uXn67qFbRvbXSUk3XFWrF4-5WELgDCvho,381
|
4
4
|
unfold/checks.py,sha256=Smgji9w19hnYjJElJ_FJnnyTEAE-E-OUB6otHu7lasY,1670
|
5
5
|
unfold/contrib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -34,8 +34,8 @@ unfold/contrib/guardian/templates/admin/guardian/model/field.html,sha256=V9ZgmYi
|
|
34
34
|
unfold/contrib/guardian/templates/admin/guardian/model/obj_perms_manage.html,sha256=p7GmZOyDqj1urDAh9pyZhatnSpe-G6gXtQ_DpRK-J8k,1426
|
35
35
|
unfold/contrib/guardian/templates/admin/guardian/model/obj_perms_manage_group.html,sha256=mcm8nqyzz0-cIHQME8QLDNGZUmatwxorvnTAE82tHiU,2322
|
36
36
|
unfold/contrib/guardian/templates/admin/guardian/model/obj_perms_manage_user.html,sha256=mqASTrSSATOCJlG3G7qlWHQbLAMgLKhhKbn1klQAZAE,2320
|
37
|
-
unfold/contrib/guardian/templates/unfold/guardian/group_form.html,sha256=
|
38
|
-
unfold/contrib/guardian/templates/unfold/guardian/user_form.html,sha256=
|
37
|
+
unfold/contrib/guardian/templates/unfold/guardian/group_form.html,sha256=3W-CCW8eb_VAzjLHtbNYBzlxFP_wofQFl9EMO-E4ivQ,3925
|
38
|
+
unfold/contrib/guardian/templates/unfold/guardian/user_form.html,sha256=ZGcndXOn-MeFW3b2bNUr5-PFS85HxLhDoPeR7AIKQ4Q,3914
|
39
39
|
unfold/contrib/import_export/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
40
40
|
unfold/contrib/import_export/apps.py,sha256=SdJu6Qh90VqGWY19FSDhhpUqhTbaIYsJKny3zX5baHI,149
|
41
41
|
unfold/contrib/import_export/forms.py,sha256=kh8psUoTObrRb2wtkYhvRu1D5QM02xWk4UNLQT5lQZA,2038
|
@@ -68,11 +68,12 @@ unfold/dataclasses.py,sha256=4PAWLVlUhyGMq1Mwai7afCgrRO0Inulw0opFUKVEOBk,483
|
|
68
68
|
unfold/decorators.py,sha256=jyjE2sunazp8gK1QIsNRJGEEDwO7RDKWDKnOI3GDWZE,3338
|
69
69
|
unfold/exceptions.py,sha256=gcCj1ox61E137bk_0Cqy4YC3SttdPgB-fiJUqpmyHSE,43
|
70
70
|
unfold/fields.py,sha256=yhtpDfqycpOxqdQlbncCg9qhELxGk3AtXizkZyzzH0g,7410
|
71
|
-
unfold/forms.py,sha256=
|
71
|
+
unfold/forms.py,sha256=raPZVYuZ1UQwo5qv8M6KDdprfCvRgjFnYfLdkmBRojI,4118
|
72
72
|
unfold/settings.py,sha256=8rjtIipRX3IrkmsSy5OrbheGhjmkYvKxueihSRIM9c8,2111
|
73
|
-
unfold/sites.py,sha256=
|
73
|
+
unfold/sites.py,sha256=JXl1-epLzodNp4--lueQZBaZ-X6JfZ0U2U1CEpvAARM,13841
|
74
|
+
unfold/static/admin/js/admin/RelatedObjectLookups.js,sha256=alI0-yq7YPDJJJn-yg1ce79-Cv88yQDUrfaGqFZnsaY,9048
|
74
75
|
unfold/static/unfold/css/simplebar.css,sha256=5LLaEM11pKi6JFCOLt4XKuZxTpT9rpdq_tNlaQytFlU,4647
|
75
|
-
unfold/static/unfold/css/styles.css,sha256=
|
76
|
+
unfold/static/unfold/css/styles.css,sha256=XeyDlhHiJodpPBgFv9GUnCL4u_FleYXAK2frlP3C334,98178
|
76
77
|
unfold/static/unfold/fonts/inter/Inter-Bold.woff2,sha256=O88EyjAeRPE_QEyKBKpK5wf2epUOEu8wwjj5bnhCZqE,46552
|
77
78
|
unfold/static/unfold/fonts/inter/Inter-Medium.woff2,sha256=O88EyjAeRPE_QEyKBKpK5wf2epUOEu8wwjj5bnhCZqE,46552
|
78
79
|
unfold/static/unfold/fonts/inter/Inter-Regular.woff2,sha256=O88EyjAeRPE_QEyKBKpK5wf2epUOEu8wwjj5bnhCZqE,46552
|
@@ -83,7 +84,7 @@ unfold/static/unfold/fonts/material-symbols/styles.css,sha256=U0oiGd2DS7LXnbuqgs
|
|
83
84
|
unfold/static/unfold/js/alpine.anchor.js,sha256=2Jg9aUq749pjFynzr_H1NK3lf-nXrbHMtO9wvlWJyIQ,15524
|
84
85
|
unfold/static/unfold/js/alpine.js,sha256=NY2a-7GrW--i9IBhowd25bzXcH9BCmBrqYX5i8OxwDQ,44659
|
85
86
|
unfold/static/unfold/js/alpine.persist.js,sha256=jFBwr6faTqqhp3sVi4_VTxJ0FpaF9YGZN1ZGLl_5QYM,837
|
86
|
-
unfold/static/unfold/js/app.js,sha256=
|
87
|
+
unfold/static/unfold/js/app.js,sha256=VirG6nw5PPnKMxQqd3eGfmzWH_MaoutOTPxWHfMo7-M,7440
|
87
88
|
unfold/static/unfold/js/chart.js,sha256=22W6cFERR-CElMOKRgMMicueMVP0Vf7FBEBYH8Z8tCk,200633
|
88
89
|
unfold/static/unfold/js/htmx.js,sha256=XOLqvnZiyEx46EW9vaJTBUaaWg8CGVVfXJkVsUmJbpI,42820
|
89
90
|
unfold/static/unfold/js/select2.init.js,sha256=SzpjSL1mD9tHedjSeXlmEwHjuuyqlnpilpSeEEKeYLE,265
|
@@ -91,26 +92,26 @@ unfold/static/unfold/js/simplebar.js,sha256=t-uG1FAD6ZoiMeN--wac0XRS7SxoDVG6zvRn
|
|
91
92
|
unfold/styles.css,sha256=7BtTH3fddTLCVDdMqohVzUXePMY_xoMaufLJbyOtY7M,18226
|
92
93
|
unfold/templates/admin/actions.html,sha256=8GqELAxzywGyfQJiQeuhGIQUakdK_WeneILY06C7mEo,2746
|
93
94
|
unfold/templates/admin/app_index.html,sha256=lVjMIFsspHQ09LGHKfdfg7TlqlL39AX5LbwoeoZjFhk,1335
|
94
|
-
unfold/templates/admin/app_list.html,sha256=
|
95
|
+
unfold/templates/admin/app_list.html,sha256=avKk-Dj_4GY_izBKWsb_yNHxUyVpdRbDBZuoxlIEdN0,2817
|
95
96
|
unfold/templates/admin/auth/user/add_form.html,sha256=L0ylU7BLEIoZc3nS4Rq6DA0H94HQ3aXFr-iwK0vp2Bs,482
|
96
97
|
unfold/templates/admin/auth/user/change_password.html,sha256=-Wa9ml3yss-kDz0YQxCiwoxs91KQD8eetCt5l6xekWM,2892
|
97
98
|
unfold/templates/admin/base.html,sha256=RptiEpgXZ6seYrPu-1Stoe8ECHpi9PjkJjFheGecYF0,2358
|
98
99
|
unfold/templates/admin/base_site.html,sha256=FM8OOaXsQqJlYapeJvBznXCOt7poMiWrweTWcsMHzeI,365
|
99
|
-
unfold/templates/admin/change_form.html,sha256=
|
100
|
+
unfold/templates/admin/change_form.html,sha256=2EKrHLNmKdg2cTmHW9N9Om6zpOxBDIzkOb6UoMKtr6U,4981
|
100
101
|
unfold/templates/admin/change_form_object_tools.html,sha256=eyeH-i2HgEM0Yi-OJA2D1VnKJyC19A_my1IDGxxoP8Y,593
|
101
|
-
unfold/templates/admin/change_list.html,sha256=
|
102
|
+
unfold/templates/admin/change_list.html,sha256=yuiCYvLa46ZBmhT-gHxQ3tHH-meiJCAEeOhVvsnexyU,5295
|
102
103
|
unfold/templates/admin/change_list_object_tools.html,sha256=cmMiT2nT20Ph5yfpj9aHPr76Z-JP4aSXp0o-Rnad28s,147
|
103
104
|
unfold/templates/admin/change_list_results.html,sha256=Nb-NbQYalqGJ9TRW4B1RZrS2ePhbyWtXi_K-tBn1DQs,5502
|
104
105
|
unfold/templates/admin/date_hierarchy.html,sha256=BfUPbsLpHZVa40BHBahz1H9RSVuz36Jc3yrlobOiIpw,1306
|
105
106
|
unfold/templates/admin/delete_confirmation.html,sha256=pg2Ei9ebjmsoM_GqaK0uasuGFYsoiFeoxsxGrOYk3kE,5082
|
106
107
|
unfold/templates/admin/delete_selected_confirmation.html,sha256=MRh1_fC5deThAb42kyoMu3t-o9OiF7UDAbRgun23KFQ,4865
|
107
|
-
unfold/templates/admin/edit_inline/stacked.html,sha256=
|
108
|
-
unfold/templates/admin/edit_inline/tabular.html,sha256=
|
108
|
+
unfold/templates/admin/edit_inline/stacked.html,sha256=cdj3JsLoZPL92NMeqKw2IIIxKQF_BFwMsuJnRaygUfk,6638
|
109
|
+
unfold/templates/admin/edit_inline/tabular.html,sha256=o1FQcVuoxJow-TE_1BjnnZdeqRiEEM3TcUesEEjnM14,12841
|
109
110
|
unfold/templates/admin/filter.html,sha256=wgYuP_ZmTofljsavLe4t8djAZnXRfEmz5U40SI-2Hjc,1640
|
110
111
|
unfold/templates/admin/includes/fieldset.html,sha256=eyo5icIIrihKBLT7k3FCgoVhK55Lb78eIp7tIUNXLvU,975
|
111
112
|
unfold/templates/admin/includes/object_delete_summary.html,sha256=w_lPVnBrng0oqvFy6rzvr8c5bzdU8GpqvYUAvaXza0c,444
|
112
113
|
unfold/templates/admin/index.html,sha256=pkGdKWdD3zzOvkRdELvdb15sleSpfl4eHPA14PAh7z0,684
|
113
|
-
unfold/templates/admin/login.html,sha256=
|
114
|
+
unfold/templates/admin/login.html,sha256=Xw50tZZxESEwz9YOcV1CSPeyyI2H1l0q4x4zBrC4dps,4170
|
114
115
|
unfold/templates/admin/nav_sidebar.html,sha256=31g1dbGHe7m_8GXFrehLqWD84Nx7jEoeLhyNBZbaqRM,1163
|
115
116
|
unfold/templates/admin/object_history.html,sha256=mRD6nbIVmnsz2OG6UsuXdI55bflY3Vs8zbIE9X-Oi74,4816
|
116
117
|
unfold/templates/admin/pagination.html,sha256=KrwOM6gRizfpTMP59TESQaNGcQzkga2k02opl_7BxLo,1130
|
@@ -121,23 +122,23 @@ unfold/templates/registration/logged_out.html,sha256=cg4YjMbVtuMdOJD3hkkJZntv0G8
|
|
121
122
|
unfold/templates/registration/password_change_done.html,sha256=bTZHs1KsGcRgT6WI5exF4ANFEXatgI24rIeENxJZl6Q,1070
|
122
123
|
unfold/templates/registration/password_change_form.html,sha256=zzmqFHfMExS7czgEBdEtetXk2Ae_7jzaID7CpE_xlgo,2225
|
123
124
|
unfold/templates/unfold/change_list_filter.html,sha256=9wP_sZF8uKmJhegPC3Z-7ZZXLwVIYDrjzzgTNxLuFbc,3344
|
124
|
-
unfold/templates/unfold/components/button.html,sha256=
|
125
|
-
unfold/templates/unfold/components/card.html,sha256=
|
125
|
+
unfold/templates/unfold/components/button.html,sha256=E9B5Y0ZzUrFmw50tK8Fi8-V0V6-ONbWcBMuyoxaDjSI,476
|
126
|
+
unfold/templates/unfold/components/card.html,sha256=3HHpqXOYS1b9yPa-ipbsWHBJUuI4knmk2od5B9kMcIs,1087
|
126
127
|
unfold/templates/unfold/components/chart/bar.html,sha256=nDdDCUXvFd793OpNgNPQo0vBmvYngMsQyPHVTf1vfOw,297
|
127
128
|
unfold/templates/unfold/components/chart/line.html,sha256=DHaZUFTlCs5cX7ioYdTzoN4ITHWNcEoNSUp7v3hlo_E,298
|
128
129
|
unfold/templates/unfold/components/container.html,sha256=WBk1rGHnTsObi3pJCpL3dfFAUQ2abDmokU0Lee7R9CI,83
|
129
130
|
unfold/templates/unfold/components/flex.html,sha256=_hBsQLJ6-7MfEfXNp5Wy-ykNtPMnnNg_io2IMGegn3s,134
|
130
131
|
unfold/templates/unfold/components/icon.html,sha256=Qlhcf41FOwNVJ-nvA0RJ_Dn6JW6PUFXO6tOR3Xtf6e8,99
|
131
132
|
unfold/templates/unfold/components/navigation.html,sha256=KNDfB6oT_dgbD8Fy09oRt6Txw7ERHa9iv-TPyFEo2wg,892
|
132
|
-
unfold/templates/unfold/components/progress.html,sha256=
|
133
|
-
unfold/templates/unfold/components/separator.html,sha256=
|
133
|
+
unfold/templates/unfold/components/progress.html,sha256=BnH2uC_ZL5ITvMjHiHoM0gjtbRTLNU1-tFcfCuJO0BE,808
|
134
|
+
unfold/templates/unfold/components/separator.html,sha256=TlKnvg1h3diDvm9A_FWc2atflAxb4e3HtnxYTxR75SU,168
|
134
135
|
unfold/templates/unfold/components/table.html,sha256=iC7mhLlPLRgCaV0sVjEkxIR7A4NLV2-rSHqEbmwpaHw,2028
|
135
136
|
unfold/templates/unfold/components/text.html,sha256=-GjxvdiaBQIaNfPSzT6SSIwnc3R27FkSDQMoF3FDPso,102
|
136
137
|
unfold/templates/unfold/components/title.html,sha256=aMYSO30ybFYqPnqwlJVh2uNmcgnVrRycZkHTjJTjpX8,171
|
137
138
|
unfold/templates/unfold/helpers/account_links.html,sha256=Vd80PCa91D5v3gJJKKuEJ7ycL80n54XJEo-039ELsRU,1879
|
138
139
|
unfold/templates/unfold/helpers/actions_row.html,sha256=d80pwsZ80J1pvlBMDj9dOfC-ybZ6KV9bNGsv4Yny8ZQ,1819
|
139
140
|
unfold/templates/unfold/helpers/add_link.html,sha256=CUFZ3quvp1JMAPmTkFRjpUXSy9GVXnIWNcRzv_gpq1A,810
|
140
|
-
unfold/templates/unfold/helpers/app_list.html,sha256=
|
141
|
+
unfold/templates/unfold/helpers/app_list.html,sha256=zeMeiSK-vlxrgP-jDkIN1k1mYCog033ybBSxw5POWvs,5516
|
141
142
|
unfold/templates/unfold/helpers/app_list_default.html,sha256=T8xXI0LsEZCyttxXZGomGMkqEd8Wxj61RCYQ4YDxlHE,3908
|
142
143
|
unfold/templates/unfold/helpers/attrs.html,sha256=Mwpj72kuwYj8hOT3J2T8qx6f1r_4xwwaS1slHA-82jI,166
|
143
144
|
unfold/templates/unfold/helpers/boolean.html,sha256=6gXAMEJcNVB14je9uN32dxImXP3KMCiWPeKU5EAnawA,569
|
@@ -171,7 +172,7 @@ unfold/templates/unfold/helpers/tab_action.html,sha256=Hq20p5yRWi-wPwkFVI5uekoAF
|
|
171
172
|
unfold/templates/unfold/helpers/tab_list.html,sha256=b84OUC12G1VpiL9jD8KWU2Xtvs5A8MZRxamjVTqGv2c,4497
|
172
173
|
unfold/templates/unfold/helpers/theme_switch.html,sha256=21r_6v7rdpv1favvw5-5uwkRMUWwsAxcghdqDP38HYw,2212
|
173
174
|
unfold/templates/unfold/helpers/userlinks.html,sha256=qWjtBt9Q_tU8a874ii0Qqg8t_d-SSYBTB_3QZfNlx9g,634
|
174
|
-
unfold/templates/unfold/helpers/welcomemsg.html,sha256=
|
175
|
+
unfold/templates/unfold/helpers/welcomemsg.html,sha256=u-svEbNA-lg_hYM2ZGWJWuffSjkDNyyu8qa0O-5Q0yE,1263
|
175
176
|
unfold/templates/unfold/layouts/base.html,sha256=bAXZDbyiyxNiE-49mqr7pHUFhC2mHZQzIDUY-js_yZ0,379
|
176
177
|
unfold/templates/unfold/layouts/base_simple.html,sha256=T37-ncKZBLNnOlsUQq90rGl3sLdRjFaxWCPG399pkbE,1179
|
177
178
|
unfold/templates/unfold/layouts/skeleton.html,sha256=J8ch2DbrGLeKdggdtjpuVG1m7VwcJoLjfBKROsOESKI,4068
|
@@ -197,7 +198,7 @@ unfold/typing.py,sha256=1P8PWM2oeaceUJtA5j071RbKEBpHYaux441u7Hd6wv4,643
|
|
197
198
|
unfold/utils.py,sha256=hs16AzAEtKb_tdTpLXuz3NqYhtP5JipSc_lkv5ULbbY,4921
|
198
199
|
unfold/views.py,sha256=hQCyeeMa9kcJV1IZeeYqj8PGW7J4QWME8n-5n0UGmiU,1003
|
199
200
|
unfold/widgets.py,sha256=HXihQ-dCvlN8uvHq1B1tzJlyBt4x800k4_5X14eiqKg,17086
|
200
|
-
django_unfold-0.
|
201
|
-
django_unfold-0.
|
202
|
-
django_unfold-0.
|
203
|
-
django_unfold-0.
|
201
|
+
django_unfold-0.40.0.dist-info/LICENSE.md,sha256=Ltk_quRyyvV3J5v3brtOqmibeZSw2Hrb8bY1W3ya0Ik,1077
|
202
|
+
django_unfold-0.40.0.dist-info/METADATA,sha256=k65GzN27Ixtkay0eI2PMLrT00g23rxQh3SMbTok8APQ,4487
|
203
|
+
django_unfold-0.40.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
204
|
+
django_unfold-0.40.0.dist-info/RECORD,,
|
unfold/admin.py
CHANGED
@@ -235,6 +235,10 @@ class ModelAdmin(ModelAdminMixin, BaseModelAdmin):
|
|
235
235
|
list_filter_submit = False
|
236
236
|
list_fullwidth = False
|
237
237
|
list_disable_select_all = False
|
238
|
+
list_before_template = None
|
239
|
+
list_after_template = None
|
240
|
+
change_form_before_template = None
|
241
|
+
change_form_after_template = None
|
238
242
|
compressed_fields = False
|
239
243
|
readonly_preprocess_fields = {}
|
240
244
|
warn_unsaved_form = False
|
@@ -39,9 +39,9 @@
|
|
39
39
|
{% for perm in model_perms %}
|
40
40
|
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto first:border-t-0 lg:before:hidden lg:first:border-t lg:py-3 lg:table-cell dark:border-gray-800" data-label="{{ perm.name }}">
|
41
41
|
{% if perm.codename in group_perms %}
|
42
|
-
{% include "unfold/helpers/boolean.html" with value=False %}
|
43
|
-
{% else %}
|
44
42
|
{% include "unfold/helpers/boolean.html" with value=True %}
|
43
|
+
{% else %}
|
44
|
+
{% include "unfold/helpers/boolean.html" with value=False %}
|
45
45
|
{% endif %}
|
46
46
|
</td>
|
47
47
|
{% endfor %}
|
@@ -38,10 +38,10 @@
|
|
38
38
|
|
39
39
|
{% for perm in model_perms %}
|
40
40
|
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto first:border-t-0 lg:before:hidden lg:first:border-t lg:py-3 lg:table-cell dark:border-gray-800" data-label="{{ perm.name }}">
|
41
|
-
{% if perm.codename in
|
42
|
-
{% include "unfold/helpers/boolean.html" with value=False %}
|
43
|
-
{% else %}
|
41
|
+
{% if perm.codename in user_perms %}
|
44
42
|
{% include "unfold/helpers/boolean.html" with value=True %}
|
43
|
+
{% else %}
|
44
|
+
{% include "unfold/helpers/boolean.html" with value=False %}
|
45
45
|
{% endif %}
|
46
46
|
</td>
|
47
47
|
{% endfor %}
|
unfold/forms.py
CHANGED
@@ -10,9 +10,14 @@ from django.contrib.admin.forms import (
|
|
10
10
|
from django.contrib.auth.forms import (
|
11
11
|
AdminPasswordChangeForm as BaseAdminPasswordChangeForm,
|
12
12
|
)
|
13
|
+
from django.contrib.auth.models import User
|
14
|
+
|
15
|
+
try:
|
16
|
+
from django.contrib.auth.forms import AdminUserCreationForm as BaseUserCreationForm
|
17
|
+
except ImportError:
|
18
|
+
from django.contrib.auth.forms import UserCreationForm as BaseUserCreationForm
|
13
19
|
from django.contrib.auth.forms import ReadOnlyPasswordHashWidget
|
14
20
|
from django.contrib.auth.forms import UserChangeForm as BaseUserChangeForm
|
15
|
-
from django.contrib.auth.forms import UserCreationForm as BaseUserCreationForm
|
16
21
|
from django.http import HttpRequest
|
17
22
|
from django.utils.safestring import mark_safe
|
18
23
|
from django.utils.translation import gettext_lazy as _
|
@@ -66,11 +71,10 @@ class AuthenticationForm(AdminAuthenticationForm):
|
|
66
71
|
class UserCreationForm(BaseUserCreationForm):
|
67
72
|
def __init__(
|
68
73
|
self,
|
69
|
-
request: Optional[HttpRequest] = None,
|
70
74
|
*args,
|
71
75
|
**kwargs,
|
72
76
|
) -> None:
|
73
|
-
super().__init__(
|
77
|
+
super().__init__(*args, **kwargs)
|
74
78
|
|
75
79
|
self.fields["password1"].widget = UnfoldAdminPasswordInput(
|
76
80
|
attrs={"autocomplete": "new-password"}
|
@@ -88,11 +92,10 @@ class UserCreationForm(BaseUserCreationForm):
|
|
88
92
|
class UserChangeForm(BaseUserChangeForm):
|
89
93
|
def __init__(
|
90
94
|
self,
|
91
|
-
request: Optional[HttpRequest] = None,
|
92
95
|
*args,
|
93
96
|
**kwargs,
|
94
97
|
) -> None:
|
95
|
-
super().__init__(
|
98
|
+
super().__init__(*args, **kwargs)
|
96
99
|
self.fields["password"].widget = UnfoldReadOnlyPasswordHashWidget()
|
97
100
|
|
98
101
|
self.fields["password"].help_text = _(
|
@@ -109,19 +112,19 @@ class UserChangeForm(BaseUserChangeForm):
|
|
109
112
|
class AdminPasswordChangeForm(BaseAdminPasswordChangeForm):
|
110
113
|
def __init__(
|
111
114
|
self,
|
112
|
-
|
115
|
+
user: User,
|
113
116
|
*args,
|
114
117
|
**kwargs,
|
115
118
|
) -> None:
|
116
|
-
super().__init__(
|
119
|
+
super().__init__(user, *args, **kwargs)
|
117
120
|
|
118
121
|
self.fields["password1"].widget.attrs["class"] = " ".join(INPUT_CLASSES)
|
119
122
|
self.fields["password2"].widget.attrs["class"] = " ".join(INPUT_CLASSES)
|
120
123
|
|
121
124
|
|
122
125
|
class AdminOwnPasswordChangeForm(BaseAdminOwnPasswordChangeForm):
|
123
|
-
def __init__(self, *args, **kwargs) -> None:
|
124
|
-
super().__init__(
|
126
|
+
def __init__(self, user: User, *args, **kwargs) -> None:
|
127
|
+
super().__init__(user, *args, **kwargs)
|
125
128
|
|
126
129
|
self.fields["old_password"].widget.attrs["class"] = " ".join(INPUT_CLASSES)
|
127
130
|
self.fields["new_password1"].widget.attrs["class"] = " ".join(INPUT_CLASSES)
|
unfold/sites.py
CHANGED
@@ -7,8 +7,18 @@ from django.core.validators import EMPTY_VALUES
|
|
7
7
|
from django.http import HttpRequest, HttpResponse
|
8
8
|
from django.template.response import TemplateResponse
|
9
9
|
from django.urls import URLPattern, path, reverse, reverse_lazy
|
10
|
+
from django.utils.decorators import method_decorator
|
10
11
|
from django.utils.functional import lazy
|
11
12
|
from django.utils.module_loading import import_string
|
13
|
+
from django.views.decorators.cache import never_cache
|
14
|
+
|
15
|
+
try:
|
16
|
+
from django.contrib.auth.decorators import login_not_required
|
17
|
+
except ImportError:
|
18
|
+
|
19
|
+
def login_not_required(func: Callable) -> Callable:
|
20
|
+
return func
|
21
|
+
|
12
22
|
|
13
23
|
from .dataclasses import Favicon
|
14
24
|
from .settings import get_config
|
@@ -178,6 +188,8 @@ class UnfoldAdminSite(AdminSite):
|
|
178
188
|
},
|
179
189
|
)
|
180
190
|
|
191
|
+
@method_decorator(never_cache)
|
192
|
+
@login_not_required
|
181
193
|
def login(
|
182
194
|
self, request: HttpRequest, extra_context: Optional[Dict[str, Any]] = None
|
183
195
|
) -> HttpResponse:
|
@@ -0,0 +1,295 @@
|
|
1
|
+
/*global SelectBox, interpolate*/
|
2
|
+
// Handles related-objects functionality: lookup link for raw_id_fields
|
3
|
+
// and Add Another links.
|
4
|
+
"use strict";
|
5
|
+
{
|
6
|
+
const $ = django.jQuery;
|
7
|
+
let popupIndex = 0;
|
8
|
+
const relatedWindows = [];
|
9
|
+
|
10
|
+
function dismissChildPopups() {
|
11
|
+
relatedWindows.forEach(function (win) {
|
12
|
+
if (!win.closed) {
|
13
|
+
win.dismissChildPopups();
|
14
|
+
win.close();
|
15
|
+
}
|
16
|
+
});
|
17
|
+
}
|
18
|
+
|
19
|
+
function setPopupIndex() {
|
20
|
+
if (document.getElementsByName("_popup").length > 0) {
|
21
|
+
const index = window.name.lastIndexOf("__") + 2;
|
22
|
+
popupIndex = parseInt(window.name.substring(index));
|
23
|
+
} else {
|
24
|
+
popupIndex = 0;
|
25
|
+
}
|
26
|
+
}
|
27
|
+
|
28
|
+
function addPopupIndex(name) {
|
29
|
+
return name + "__" + (popupIndex + 1);
|
30
|
+
}
|
31
|
+
|
32
|
+
function removePopupIndex(name) {
|
33
|
+
return name.replace(new RegExp("__" + (popupIndex + 1) + "$"), "");
|
34
|
+
}
|
35
|
+
|
36
|
+
function showAdminPopup(triggeringLink, name_regexp, add_popup) {
|
37
|
+
const name = addPopupIndex(triggeringLink.id.replace(name_regexp, ""));
|
38
|
+
const href = new URL(triggeringLink.href);
|
39
|
+
if (add_popup) {
|
40
|
+
href.searchParams.set("_popup", 1);
|
41
|
+
}
|
42
|
+
const win = window.open(
|
43
|
+
href,
|
44
|
+
name,
|
45
|
+
"height=768,width=1024,resizable=yes,scrollbars=yes"
|
46
|
+
);
|
47
|
+
relatedWindows.push(win);
|
48
|
+
win.focus();
|
49
|
+
return false;
|
50
|
+
}
|
51
|
+
|
52
|
+
function showRelatedObjectLookupPopup(triggeringLink) {
|
53
|
+
return showAdminPopup(triggeringLink, /^lookup_/, true);
|
54
|
+
}
|
55
|
+
|
56
|
+
function dismissRelatedLookupPopup(win, chosenId) {
|
57
|
+
const name = removePopupIndex(win.name);
|
58
|
+
const elem = document.getElementById(name);
|
59
|
+
if (elem.classList.contains("vManyToManyRawIdAdminField") && elem.value) {
|
60
|
+
elem.value += "," + chosenId;
|
61
|
+
} else {
|
62
|
+
document.getElementById(name).value = chosenId;
|
63
|
+
}
|
64
|
+
const index = relatedWindows.indexOf(win);
|
65
|
+
if (index > -1) {
|
66
|
+
relatedWindows.splice(index, 1);
|
67
|
+
}
|
68
|
+
win.close();
|
69
|
+
}
|
70
|
+
|
71
|
+
function showRelatedObjectPopup(triggeringLink) {
|
72
|
+
return showAdminPopup(triggeringLink, /^(change|add|delete)_/, false);
|
73
|
+
}
|
74
|
+
|
75
|
+
function updateRelatedObjectLinks(triggeringLink) {
|
76
|
+
const $this = $(triggeringLink);
|
77
|
+
const siblings = $this.nextAll(
|
78
|
+
".view-related, .change-related, .delete-related"
|
79
|
+
);
|
80
|
+
if (!siblings.length) {
|
81
|
+
return;
|
82
|
+
}
|
83
|
+
const value = $this.val();
|
84
|
+
if (value) {
|
85
|
+
siblings.each(function () {
|
86
|
+
const elm = $(this);
|
87
|
+
elm.attr(
|
88
|
+
"href",
|
89
|
+
elm.attr("data-href-template").replace("__fk__", value)
|
90
|
+
);
|
91
|
+
elm.removeAttr("aria-disabled");
|
92
|
+
});
|
93
|
+
} else {
|
94
|
+
siblings.removeAttr("href");
|
95
|
+
siblings.attr("aria-disabled", true);
|
96
|
+
}
|
97
|
+
}
|
98
|
+
|
99
|
+
function updateRelatedSelectsOptions(
|
100
|
+
currentSelect,
|
101
|
+
win,
|
102
|
+
objId,
|
103
|
+
newRepr,
|
104
|
+
newId,
|
105
|
+
skipIds = []
|
106
|
+
) {
|
107
|
+
// After create/edit a model from the options next to the current
|
108
|
+
// select (+ or :pencil:) update ForeignKey PK of the rest of selects
|
109
|
+
// in the page.
|
110
|
+
|
111
|
+
const path = win.location.pathname;
|
112
|
+
// Extract the model from the popup url '.../<model>/add/' or
|
113
|
+
// '.../<model>/<id>/change/' depending the action (add or change).
|
114
|
+
const modelName = path.split("/")[path.split("/").length - (objId ? 4 : 3)];
|
115
|
+
// Select elements with a specific model reference and context of "available-source".
|
116
|
+
const selectsRelated = document.querySelectorAll(
|
117
|
+
`[data-model-ref="${modelName}"] [data-context="available-source"]`
|
118
|
+
);
|
119
|
+
|
120
|
+
selectsRelated.forEach(function (select) {
|
121
|
+
if (
|
122
|
+
currentSelect === select ||
|
123
|
+
(skipIds && skipIds.includes(select.id))
|
124
|
+
) {
|
125
|
+
return;
|
126
|
+
}
|
127
|
+
|
128
|
+
let option = select.querySelector(`option[value="${objId}"]`);
|
129
|
+
|
130
|
+
if (!option) {
|
131
|
+
option = new Option(newRepr, newId);
|
132
|
+
select.options.add(option);
|
133
|
+
// Update SelectBox cache for related fields.
|
134
|
+
if (
|
135
|
+
window.SelectBox !== undefined &&
|
136
|
+
!SelectBox.cache[currentSelect.id]
|
137
|
+
) {
|
138
|
+
SelectBox.add_to_cache(select.id, option);
|
139
|
+
SelectBox.redisplay(select.id);
|
140
|
+
}
|
141
|
+
return;
|
142
|
+
}
|
143
|
+
|
144
|
+
option.textContent = newRepr;
|
145
|
+
option.value = newId;
|
146
|
+
});
|
147
|
+
}
|
148
|
+
|
149
|
+
function dismissAddRelatedObjectPopup(win, newId, newRepr) {
|
150
|
+
const name = removePopupIndex(win.name);
|
151
|
+
const elem = document.getElementById(name);
|
152
|
+
if (elem) {
|
153
|
+
const elemName = elem.nodeName.toUpperCase();
|
154
|
+
if (elemName === "SELECT") {
|
155
|
+
elem.options[elem.options.length] = new Option(
|
156
|
+
newRepr,
|
157
|
+
newId,
|
158
|
+
true,
|
159
|
+
true
|
160
|
+
);
|
161
|
+
updateRelatedSelectsOptions(elem, win, null, newRepr, newId);
|
162
|
+
} else if (elemName === "INPUT") {
|
163
|
+
if (
|
164
|
+
elem.classList.contains("vManyToManyRawIdAdminField") &&
|
165
|
+
elem.value
|
166
|
+
) {
|
167
|
+
elem.value += "," + newId;
|
168
|
+
} else {
|
169
|
+
elem.value = newId;
|
170
|
+
}
|
171
|
+
}
|
172
|
+
// Trigger a change event to update related links if required.
|
173
|
+
$(elem).trigger("change");
|
174
|
+
} else {
|
175
|
+
const toId = name + "_to";
|
176
|
+
const toElem = document.getElementById(toId);
|
177
|
+
const o = new Option(newRepr, newId);
|
178
|
+
SelectBox.add_to_cache(toId, o);
|
179
|
+
SelectBox.redisplay(toId);
|
180
|
+
if (toElem && toElem.nodeName.toUpperCase() === "SELECT") {
|
181
|
+
const skipIds = [name + "_from"];
|
182
|
+
updateRelatedSelectsOptions(toElem, win, null, newRepr, newId, skipIds);
|
183
|
+
}
|
184
|
+
}
|
185
|
+
const index = relatedWindows.indexOf(win);
|
186
|
+
if (index > -1) {
|
187
|
+
relatedWindows.splice(index, 1);
|
188
|
+
}
|
189
|
+
win.close();
|
190
|
+
}
|
191
|
+
|
192
|
+
function dismissChangeRelatedObjectPopup(win, objId, newRepr, newId) {
|
193
|
+
const id = removePopupIndex(win.name.replace(/^edit_/, ""));
|
194
|
+
const selectsSelector = interpolate("#%s, #%s_from, #%s_to", [id, id, id]);
|
195
|
+
const selects = $(selectsSelector);
|
196
|
+
selects
|
197
|
+
.find("option")
|
198
|
+
.each(function () {
|
199
|
+
if (this.value === objId) {
|
200
|
+
this.textContent = newRepr;
|
201
|
+
this.value = newId;
|
202
|
+
}
|
203
|
+
})
|
204
|
+
.trigger("change");
|
205
|
+
updateRelatedSelectsOptions(selects[0], win, objId, newRepr, newId);
|
206
|
+
selects
|
207
|
+
.next()
|
208
|
+
.find(".select2-selection__rendered")
|
209
|
+
.each(function () {
|
210
|
+
// The element can have a clear button as a child.
|
211
|
+
// Use the lastChild to modify only the displayed value.
|
212
|
+
this.lastChild.textContent = newRepr;
|
213
|
+
this.title = newRepr;
|
214
|
+
});
|
215
|
+
const index = relatedWindows.indexOf(win);
|
216
|
+
if (index > -1) {
|
217
|
+
relatedWindows.splice(index, 1);
|
218
|
+
}
|
219
|
+
win.close();
|
220
|
+
}
|
221
|
+
|
222
|
+
function dismissDeleteRelatedObjectPopup(win, objId) {
|
223
|
+
const id = removePopupIndex(win.name.replace(/^delete_/, ""));
|
224
|
+
const selectsSelector = interpolate("#%s, #%s_from, #%s_to", [id, id, id]);
|
225
|
+
const selects = $(selectsSelector);
|
226
|
+
selects
|
227
|
+
.find("option")
|
228
|
+
.each(function () {
|
229
|
+
if (this.value === objId) {
|
230
|
+
$(this).remove();
|
231
|
+
}
|
232
|
+
})
|
233
|
+
.trigger("change");
|
234
|
+
const index = relatedWindows.indexOf(win);
|
235
|
+
if (index > -1) {
|
236
|
+
relatedWindows.splice(index, 1);
|
237
|
+
}
|
238
|
+
win.close();
|
239
|
+
}
|
240
|
+
|
241
|
+
window.showRelatedObjectLookupPopup = showRelatedObjectLookupPopup;
|
242
|
+
window.dismissRelatedLookupPopup = dismissRelatedLookupPopup;
|
243
|
+
window.showRelatedObjectPopup = showRelatedObjectPopup;
|
244
|
+
window.updateRelatedObjectLinks = updateRelatedObjectLinks;
|
245
|
+
window.dismissAddRelatedObjectPopup = dismissAddRelatedObjectPopup;
|
246
|
+
window.dismissChangeRelatedObjectPopup = dismissChangeRelatedObjectPopup;
|
247
|
+
window.dismissDeleteRelatedObjectPopup = dismissDeleteRelatedObjectPopup;
|
248
|
+
window.dismissChildPopups = dismissChildPopups;
|
249
|
+
|
250
|
+
// Kept for backward compatibility
|
251
|
+
window.showAddAnotherPopup = showRelatedObjectPopup;
|
252
|
+
window.dismissAddAnotherPopup = dismissAddRelatedObjectPopup;
|
253
|
+
|
254
|
+
window.addEventListener("unload", function (evt) {
|
255
|
+
window.dismissChildPopups();
|
256
|
+
});
|
257
|
+
|
258
|
+
$(document).ready(function () {
|
259
|
+
setPopupIndex();
|
260
|
+
$("a[data-popup-opener]").on("click", function (event) {
|
261
|
+
event.preventDefault();
|
262
|
+
opener.dismissRelatedLookupPopup(window, $(this).data("popup-opener"));
|
263
|
+
});
|
264
|
+
$("body").on(
|
265
|
+
"click",
|
266
|
+
'.related-widget-wrapper-link[data-popup="yes"]',
|
267
|
+
function (e) {
|
268
|
+
e.preventDefault();
|
269
|
+
if (this.href) {
|
270
|
+
const event = $.Event("django:show-related", { href: this.href });
|
271
|
+
$(this).trigger(event);
|
272
|
+
if (!event.isDefaultPrevented()) {
|
273
|
+
showRelatedObjectPopup(this);
|
274
|
+
}
|
275
|
+
}
|
276
|
+
}
|
277
|
+
);
|
278
|
+
$("body").on("change", ".related-widget-wrapper select", function (e) {
|
279
|
+
const event = $.Event("django:update-related");
|
280
|
+
$(this).trigger(event);
|
281
|
+
if (!event.isDefaultPrevented()) {
|
282
|
+
updateRelatedObjectLinks(this);
|
283
|
+
}
|
284
|
+
});
|
285
|
+
$(".related-widget-wrapper select").trigger("change");
|
286
|
+
$("body").on("click", ".related-lookup", function (e) {
|
287
|
+
e.preventDefault();
|
288
|
+
const event = $.Event("django:lookup-related");
|
289
|
+
$(this).trigger(event);
|
290
|
+
if (!event.isDefaultPrevented()) {
|
291
|
+
showRelatedObjectLookupPopup(this);
|
292
|
+
}
|
293
|
+
});
|
294
|
+
});
|
295
|
+
}
|