django-smartbase-admin 0.2.54__py3-none-any.whl → 1.0.38__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.
Files changed (179) hide show
  1. django_smartbase_admin/actions/admin_action_list.py +74 -38
  2. django_smartbase_admin/actions/advanced_filters.py +24 -1
  3. django_smartbase_admin/admin/admin_base.py +401 -96
  4. django_smartbase_admin/admin/site.py +93 -35
  5. django_smartbase_admin/admin/widgets.py +589 -26
  6. django_smartbase_admin/apps.py +2 -0
  7. django_smartbase_admin/engine/actions.py +34 -16
  8. django_smartbase_admin/engine/admin_base_view.py +252 -115
  9. django_smartbase_admin/engine/configuration.py +186 -4
  10. django_smartbase_admin/engine/const.py +6 -0
  11. django_smartbase_admin/engine/dashboard.py +44 -23
  12. django_smartbase_admin/engine/fake_inline.py +15 -11
  13. django_smartbase_admin/engine/field.py +42 -12
  14. django_smartbase_admin/engine/field_formatter.py +22 -8
  15. django_smartbase_admin/engine/filter_widgets.py +309 -20
  16. django_smartbase_admin/engine/menu_item.py +8 -5
  17. django_smartbase_admin/engine/modal_view.py +12 -7
  18. django_smartbase_admin/engine/request.py +2 -0
  19. django_smartbase_admin/integration/__init__.py +0 -0
  20. django_smartbase_admin/integration/django_cms.py +43 -0
  21. django_smartbase_admin/locale/sk/LC_MESSAGES/django.mo +0 -0
  22. django_smartbase_admin/locale/sk/LC_MESSAGES/django.po +268 -37
  23. django_smartbase_admin/migrations/0005_sbadminuserconfiguration.py +26 -0
  24. django_smartbase_admin/migrations/0006_alter_sbadminuserconfiguration_color_scheme.py +18 -0
  25. django_smartbase_admin/models.py +22 -0
  26. django_smartbase_admin/monkeypatch/admin_readonly_field_monkeypatch.py +96 -0
  27. django_smartbase_admin/monkeypatch/fake_inline_monkeypatch.py +1 -1
  28. django_smartbase_admin/querysets.py +3 -0
  29. django_smartbase_admin/services/configuration.py +30 -0
  30. django_smartbase_admin/services/thread_local.py +6 -19
  31. django_smartbase_admin/services/views.py +80 -13
  32. django_smartbase_admin/services/xlsx_export.py +6 -0
  33. django_smartbase_admin/static/sb_admin/build/tailwind.config.js +1 -0
  34. django_smartbase_admin/static/sb_admin/build/tailwind_config_partials/colors.js +4 -0
  35. django_smartbase_admin/static/sb_admin/build/tailwind_config_partials/spacing.js +1 -0
  36. django_smartbase_admin/static/sb_admin/build/webpack.common.js +11 -8
  37. django_smartbase_admin/static/sb_admin/css/ckeditor/ckeditor_content_dark.css +208 -0
  38. django_smartbase_admin/static/sb_admin/css/coloris/coloris.min.css +1 -0
  39. django_smartbase_admin/static/sb_admin/dist/calendar.js +1 -0
  40. django_smartbase_admin/static/sb_admin/dist/calendar_style.css +1 -0
  41. django_smartbase_admin/static/sb_admin/dist/calendar_style.js +0 -0
  42. django_smartbase_admin/static/sb_admin/dist/chart.js +1 -1
  43. django_smartbase_admin/static/sb_admin/dist/main.js +1 -1
  44. django_smartbase_admin/static/sb_admin/dist/main_style.css +1 -1
  45. django_smartbase_admin/static/sb_admin/dist/table.js +1 -1
  46. django_smartbase_admin/static/sb_admin/dist/tree_widget.js +1 -0
  47. django_smartbase_admin/static/sb_admin/dist/tree_widget_style.css +1 -0
  48. django_smartbase_admin/static/sb_admin/dist/tree_widget_style.js +0 -0
  49. django_smartbase_admin/static/sb_admin/fancytree/jquery.fancytree-all-deps.min.js +1 -0
  50. django_smartbase_admin/static/sb_admin/images/file_types/file-csv.svg +11 -0
  51. django_smartbase_admin/static/sb_admin/images/file_types/file-doc.svg +11 -0
  52. django_smartbase_admin/static/sb_admin/images/file_types/file-docx.svg +11 -0
  53. django_smartbase_admin/static/sb_admin/images/file_types/file-other.svg +13 -0
  54. django_smartbase_admin/static/sb_admin/images/file_types/file-pdf.svg +11 -0
  55. django_smartbase_admin/static/sb_admin/images/file_types/file-ppt.svg +11 -0
  56. django_smartbase_admin/static/sb_admin/images/file_types/file-xls.svg +11 -0
  57. django_smartbase_admin/static/sb_admin/images/file_types/file-xlsx.svg +11 -0
  58. django_smartbase_admin/static/sb_admin/images/file_types/file-zip.svg +18 -0
  59. django_smartbase_admin/static/sb_admin/images/flags/de-at.png +0 -0
  60. django_smartbase_admin/static/sb_admin/images/flags/de-ch.png +0 -0
  61. django_smartbase_admin/static/sb_admin/images/logo_light.svg +21 -0
  62. django_smartbase_admin/static/sb_admin/js/coloris/coloris.min.js +6 -0
  63. django_smartbase_admin/static/sb_admin/js/fullcalendar.min.js +14804 -0
  64. django_smartbase_admin/static/sb_admin/sprites/sb_admin/Bolt-one.svg +3 -0
  65. django_smartbase_admin/static/sb_admin/sprites/sb_admin/Calendar.svg +3 -0
  66. django_smartbase_admin/static/sb_admin/sprites/sb_admin/Caution.svg +3 -0
  67. django_smartbase_admin/static/sb_admin/sprites/sb_admin/Electric-drill.svg +3 -0
  68. django_smartbase_admin/static/sb_admin/sprites/sb_admin/Fire-extinguisher.svg +3 -0
  69. django_smartbase_admin/static/sb_admin/sprites/sb_admin/Gas.svg +3 -0
  70. django_smartbase_admin/static/sb_admin/sprites/sb_admin/Lightning-fill.svg +3 -0
  71. django_smartbase_admin/static/sb_admin/sprites/sb_admin/Moon.svg +3 -0
  72. django_smartbase_admin/static/sb_admin/sprites/sb_admin/Phone-telephone.svg +3 -0
  73. django_smartbase_admin/static/sb_admin/sprites/sb_admin/Printer.svg +3 -0
  74. django_smartbase_admin/static/sb_admin/sprites/sb_admin/Pull.svg +3 -0
  75. django_smartbase_admin/static/sb_admin/sprites/sb_admin/Sun-one.svg +3 -0
  76. django_smartbase_admin/static/sb_admin/sprites/sb_admin/Time.svg +3 -0
  77. django_smartbase_admin/static/sb_admin/src/css/_base.css +5 -1
  78. django_smartbase_admin/static/sb_admin/src/css/_colors.css +257 -82
  79. django_smartbase_admin/static/sb_admin/src/css/_components.css +61 -13
  80. django_smartbase_admin/static/sb_admin/src/css/_datepicker.css +8 -1
  81. django_smartbase_admin/static/sb_admin/src/css/_filer.css +60 -0
  82. django_smartbase_admin/static/sb_admin/src/css/_inlines.css +51 -10
  83. django_smartbase_admin/static/sb_admin/src/css/_tabulator.css +8 -2
  84. django_smartbase_admin/static/sb_admin/src/css/calendar.css +162 -0
  85. django_smartbase_admin/static/sb_admin/src/css/components/_button.css +41 -1
  86. django_smartbase_admin/static/sb_admin/src/css/components/_dropdown.css +26 -8
  87. django_smartbase_admin/static/sb_admin/src/css/components/_input.css +62 -20
  88. django_smartbase_admin/static/sb_admin/src/css/components/_modal.css +1 -1
  89. django_smartbase_admin/static/sb_admin/src/css/components/_query-builder.css +21 -2
  90. django_smartbase_admin/static/sb_admin/src/css/components/_toggle.css +12 -1
  91. django_smartbase_admin/static/sb_admin/src/css/components/_tooltip.css +8 -22
  92. django_smartbase_admin/static/sb_admin/src/css/style.css +17 -0
  93. django_smartbase_admin/static/sb_admin/src/css/tree_widget.css +411 -0
  94. django_smartbase_admin/static/sb_admin/src/js/autocomplete.js +63 -5
  95. django_smartbase_admin/static/sb_admin/src/js/calendar.js +56 -0
  96. django_smartbase_admin/static/sb_admin/src/js/chart.js +8 -22
  97. django_smartbase_admin/static/sb_admin/src/js/choices.js +18 -8
  98. django_smartbase_admin/static/sb_admin/src/js/datepicker.js +97 -336
  99. django_smartbase_admin/static/sb_admin/src/js/datepicker_plugins.js +357 -0
  100. django_smartbase_admin/static/sb_admin/src/js/main.js +304 -31
  101. django_smartbase_admin/static/sb_admin/src/js/multiselect.js +50 -41
  102. django_smartbase_admin/static/sb_admin/src/js/range.js +3 -2
  103. django_smartbase_admin/static/sb_admin/src/js/table.js +34 -5
  104. django_smartbase_admin/static/sb_admin/src/js/table_modules/advanced_filter_module.js +43 -20
  105. django_smartbase_admin/static/sb_admin/src/js/table_modules/data_edit_module.js +8 -10
  106. django_smartbase_admin/static/sb_admin/src/js/table_modules/filter_module.js +3 -3
  107. django_smartbase_admin/static/sb_admin/src/js/table_modules/header_tabs_module.js +11 -11
  108. django_smartbase_admin/static/sb_admin/src/js/table_modules/selection_module.js +28 -8
  109. django_smartbase_admin/static/sb_admin/src/js/table_modules/table_params_module.js +6 -0
  110. django_smartbase_admin/static/sb_admin/src/js/table_modules/views_module.js +6 -0
  111. django_smartbase_admin/static/sb_admin/src/js/tree_widget.js +406 -0
  112. django_smartbase_admin/static/sb_admin/src/js/utils.js +56 -21
  113. django_smartbase_admin/templates/sb_admin/actions/change_form.html +169 -114
  114. django_smartbase_admin/templates/sb_admin/actions/dashboard.html +2 -2
  115. django_smartbase_admin/templates/sb_admin/actions/list.html +79 -39
  116. django_smartbase_admin/templates/sb_admin/actions/partials/action_link.html +14 -0
  117. django_smartbase_admin/templates/sb_admin/actions/partials/tabulator_header_v2.html +2 -2
  118. django_smartbase_admin/templates/sb_admin/actions/tree_list.html +63 -0
  119. django_smartbase_admin/templates/sb_admin/authentification/login_base.html +5 -1
  120. django_smartbase_admin/templates/sb_admin/components/columns.html +1 -1
  121. django_smartbase_admin/templates/sb_admin/components/filters_v2.html +99 -85
  122. django_smartbase_admin/templates/sb_admin/dashboard/calendar_widget.html +69 -0
  123. django_smartbase_admin/templates/sb_admin/dashboard/chart_widget.html +21 -2
  124. django_smartbase_admin/templates/sb_admin/dashboard/list_widget.html +6 -0
  125. django_smartbase_admin/templates/sb_admin/dashboard/widget_base.html +1 -1
  126. django_smartbase_admin/templates/sb_admin/filter_widgets/advanced_filters/date_field.html +18 -8
  127. django_smartbase_admin/templates/sb_admin/filter_widgets/advanced_filters/multiple_choice_field.html +1 -1
  128. django_smartbase_admin/templates/sb_admin/filter_widgets/advanced_filters/tree_select_filter.html +2 -0
  129. django_smartbase_admin/templates/sb_admin/filter_widgets/date_field.html +18 -4
  130. django_smartbase_admin/templates/sb_admin/filter_widgets/multiple_choice_field.html +14 -0
  131. django_smartbase_admin/templates/sb_admin/filter_widgets/partials/clear.html +10 -5
  132. django_smartbase_admin/templates/sb_admin/filter_widgets/radio_choice_field.html +2 -2
  133. django_smartbase_admin/templates/sb_admin/filter_widgets/tree_select_filter.html +16 -0
  134. django_smartbase_admin/templates/sb_admin/includes/change_form_title.html +3 -1
  135. django_smartbase_admin/templates/sb_admin/includes/inline_fieldset.html +48 -39
  136. django_smartbase_admin/templates/sb_admin/includes/notifications.html +2 -1
  137. django_smartbase_admin/templates/sb_admin/includes/readonly_boolean_field.html +9 -0
  138. django_smartbase_admin/templates/sb_admin/includes/readonly_field.html +12 -0
  139. django_smartbase_admin/templates/sb_admin/includes/table_inline_delete_button.html +4 -5
  140. django_smartbase_admin/templates/sb_admin/inlines/stacked_inline.html +68 -40
  141. django_smartbase_admin/templates/sb_admin/inlines/table_inline.html +76 -34
  142. django_smartbase_admin/templates/sb_admin/integrations/filer/folder_list.html +18 -0
  143. django_smartbase_admin/templates/sb_admin/navigation.html +166 -158
  144. django_smartbase_admin/templates/sb_admin/partials/modal/modal_content.html +2 -6
  145. django_smartbase_admin/templates/sb_admin/sb_admin_base.html +49 -4
  146. django_smartbase_admin/templates/sb_admin/sb_admin_base_no_sidebar.html +27 -11
  147. django_smartbase_admin/templates/sb_admin/sb_admin_js_trans.html +3 -0
  148. django_smartbase_admin/templates/sb_admin/sprites/sb_admin.svg +1 -1
  149. django_smartbase_admin/templates/sb_admin/tailwind_whitelist.html +6 -3
  150. django_smartbase_admin/templates/sb_admin/widgets/array.html +0 -1
  151. django_smartbase_admin/templates/sb_admin/widgets/attributes.html +68 -0
  152. django_smartbase_admin/templates/sb_admin/widgets/autocomplete.html +13 -2
  153. django_smartbase_admin/templates/sb_admin/widgets/{checkbox_select.html → checkbox_dropdown.html} +2 -2
  154. django_smartbase_admin/templates/sb_admin/widgets/clearable_file_input.html +2 -2
  155. django_smartbase_admin/templates/sb_admin/widgets/color_field.html +30 -0
  156. django_smartbase_admin/templates/sb_admin/widgets/date.html +8 -1
  157. django_smartbase_admin/templates/sb_admin/widgets/filer_file.html +84 -0
  158. django_smartbase_admin/templates/sb_admin/widgets/includes/related_item_buttons.html +38 -0
  159. django_smartbase_admin/templates/sb_admin/widgets/multiwidget.html +1 -1
  160. django_smartbase_admin/templates/sb_admin/widgets/radio.html +3 -2
  161. django_smartbase_admin/templates/sb_admin/widgets/radio_dropdown.html +30 -0
  162. django_smartbase_admin/templates/sb_admin/widgets/read_only_password_hash.html +3 -0
  163. django_smartbase_admin/templates/sb_admin/widgets/time.html +8 -1
  164. django_smartbase_admin/templates/sb_admin/widgets/toggle.html +1 -1
  165. django_smartbase_admin/templates/sb_admin/widgets/tree_base.html +59 -0
  166. django_smartbase_admin/templates/sb_admin/widgets/tree_select.html +24 -0
  167. django_smartbase_admin/templates/sb_admin/widgets/tree_select_inline.html +12 -0
  168. django_smartbase_admin/templatetags/sb_admin_tags.py +85 -4
  169. django_smartbase_admin/utils.py +22 -3
  170. django_smartbase_admin/views/dashboard_view.py +6 -0
  171. django_smartbase_admin/views/global_filter_view.py +8 -2
  172. django_smartbase_admin/views/translations_view.py +12 -5
  173. django_smartbase_admin/views/user_config_view.py +52 -0
  174. django_smartbase_admin-1.0.38.dist-info/METADATA +166 -0
  175. {django_smartbase_admin-0.2.54.dist-info → django_smartbase_admin-1.0.38.dist-info}/RECORD +177 -115
  176. {django_smartbase_admin-0.2.54.dist-info → django_smartbase_admin-1.0.38.dist-info}/WHEEL +1 -1
  177. django_smartbase_admin/templates/sb_admin/integrations/sorting/change_list.html +0 -401
  178. django_smartbase_admin-0.2.54.dist-info/METADATA +0 -25
  179. {django_smartbase_admin-0.2.54.dist-info → django_smartbase_admin-1.0.38.dist-info}/LICENSE.md +0 -0
@@ -1,18 +1,25 @@
1
1
  import json
2
2
  import urllib.parse
3
3
  from collections import defaultdict
4
+ from collections.abc import Iterable
5
+ from typing import Any, TYPE_CHECKING
4
6
 
7
+ from django.conf import settings
8
+ from django.contrib import messages
5
9
  from django.contrib.admin.actions import delete_selected
6
10
  from django.core.exceptions import PermissionDenied
7
11
  from django.db.models import F
8
- from django.http import HttpResponse, Http404, JsonResponse
12
+ from django.http import HttpResponse, Http404, JsonResponse, HttpRequest
9
13
  from django.shortcuts import redirect
10
14
  from django.template.response import TemplateResponse
11
15
  from django.urls import reverse
12
16
  from django.utils.translation import gettext_lazy as _
13
17
 
14
18
  from django_smartbase_admin.actions.admin_action_list import SBAdminListAction
15
- from django_smartbase_admin.engine.actions import SBAdminCustomAction
19
+ from django_smartbase_admin.engine.actions import (
20
+ SBAdminCustomAction,
21
+ SBAdminFormViewAction,
22
+ )
16
23
  from django_smartbase_admin.engine.const import (
17
24
  Action,
18
25
  OBJECT_ID_PLACEHOLDER,
@@ -24,6 +31,15 @@ from django_smartbase_admin.engine.const import (
24
31
  GLOBAL_FILTER_ALIAS_WIDGET_ID,
25
32
  OVERRIDE_CONTENT_OF_NOTIFICATION,
26
33
  FilterVersions,
34
+ BASE_PARAMS_NAME,
35
+ TABLE_RELOAD_DATA_EVENT_NAME,
36
+ TABLE_UPDATE_ROW_DATA_EVENT_NAME,
37
+ SELECT_ALL_KEYWORD,
38
+ IGNORE_LIST_SELECTION,
39
+ SUPPORTED_FILE_TYPE_ICONS,
40
+ )
41
+ from django_smartbase_admin.services.configuration import (
42
+ SBAdminUserConfigurationService,
27
43
  )
28
44
  from django_smartbase_admin.services.views import SBAdminViewService
29
45
  from django_smartbase_admin.services.xlsx_export import (
@@ -31,14 +47,26 @@ from django_smartbase_admin.services.xlsx_export import (
31
47
  SBAdminXLSXOptions,
32
48
  SBAdminXLSXFormat,
33
49
  )
34
- from django_smartbase_admin.utils import is_htmx_request
50
+ from django_smartbase_admin.utils import is_htmx_request, render_notifications, is_modal
51
+
52
+ if TYPE_CHECKING:
53
+ from django_smartbase_admin.engine.field import SBAdminField
54
+
55
+ SBADMIN_IS_MODAL_VAR = "sbadmin_is_modal"
56
+ SBADMIN_PARENT_INSTANCE_FIELD_NAME_VAR = "sbadmin_parent_instance_field"
57
+ SBADMIN_PARENT_INSTANCE_PK_VAR = "sbadmin_parent_instance_pk"
58
+ SBADMIN_PARENT_INSTANCE_LABEL_VAR = "sbadmin_parent_instance_label"
59
+ SBADMIN_RELOAD_ON_SAVE_VAR = "sbadmin_reload_on_save"
35
60
 
36
61
 
37
62
  class SBAdminBaseView(object):
38
- menu_label = None
39
63
  global_filter_data_map = None
40
64
  field_cache = None
41
65
  sbadmin_detail_actions = None
66
+ menu_label: str | None = None
67
+ add_label: str | None = None
68
+ change_label: str | None = None
69
+ delete_confirmation_template = "sb_admin/actions/delete_confirmation.html"
42
70
 
43
71
  def init_view_static(self, configuration, model, admin_site):
44
72
  pass
@@ -46,39 +74,64 @@ class SBAdminBaseView(object):
46
74
  def get_id(self):
47
75
  raise NotImplementedError
48
76
 
49
- def get_menu_label(self):
77
+ def get_menu_label(self) -> str:
50
78
  return self.menu_label or self.model._meta.verbose_name_plural
51
79
 
52
- def has_permission(self, request, obj=None, permission=None):
80
+ def has_permission(self, request, obj=None, permission=None) -> bool:
53
81
  return SBAdminViewService.has_permission(
54
82
  request=request, view=self, model=self.model, obj=obj, permission=permission
55
83
  )
56
84
 
57
- def has_add_permission(self, request, obj=None):
85
+ def has_add_permission(self, request, obj=None) -> bool:
58
86
  return self.has_permission(request, obj, "add")
59
87
 
60
- def has_view_permission(self, request, obj=None):
88
+ def has_view_permission(self, request, obj=None) -> bool:
61
89
  return self.has_permission(request, obj, "view")
62
90
 
63
- def has_change_permission(self, request, obj=None):
91
+ def has_change_permission(self, request, obj=None) -> bool:
64
92
  return self.has_permission(request, obj, "change")
65
93
 
66
- def has_delete_permission(self, request, obj=None):
94
+ def has_delete_permission(self, request, obj=None) -> bool:
67
95
  return self.has_permission(request, obj, "delete")
68
96
 
69
- def has_permission_for_action(self, request, action):
97
+ def has_permission_for_action(self, request, action: SBAdminCustomAction) -> bool:
70
98
  return self.has_permission(
71
99
  request=request,
72
100
  obj=None,
73
101
  permission=action,
74
102
  )
75
103
 
76
- def has_view_or_change_permission(self, request, obj=None):
104
+ def has_view_or_change_permission(self, request, obj=None) -> bool:
77
105
  return self.has_view_permission(request, obj) or self.has_change_permission(
78
106
  request, obj
79
107
  )
80
108
 
81
- def process_actions_permissions(self, request, actions):
109
+ def delegate_to_action_view(self, processed_action):
110
+ def inner_view(request, modifier):
111
+ return processed_action.target_view.as_view(view=self)(request)
112
+
113
+ return inner_view
114
+
115
+ def process_actions(
116
+ self, request, actions: list[SBAdminCustomAction]
117
+ ) -> list[SBAdminCustomAction]:
118
+ processed_actions = self.process_actions_permissions(request, actions)
119
+ for processed_action in processed_actions:
120
+ if isinstance(processed_action, SBAdminFormViewAction):
121
+ action_id = processed_action.target_view.__name__
122
+ setattr(
123
+ self,
124
+ action_id,
125
+ self.delegate_to_action_view(processed_action),
126
+ )
127
+ processed_action.url = self.get_action_url(action_id)
128
+ processed_action.action_id = action_id
129
+
130
+ return processed_actions
131
+
132
+ def process_actions_permissions(
133
+ self, request, actions: list[SBAdminCustomAction]
134
+ ) -> list[SBAdminCustomAction]:
82
135
  result = []
83
136
  for action in actions:
84
137
  if self.has_permission_for_action(request, action):
@@ -92,8 +145,8 @@ class SBAdminBaseView(object):
92
145
  def get_field_map(self, request):
93
146
  return self.field_cache
94
147
 
95
- def init_fields_cache(self, fields_source, configuration):
96
- if self.field_cache:
148
+ def init_fields_cache(self, fields_source, configuration, force=False):
149
+ if not force and self.field_cache:
97
150
  return self.field_cache.values()
98
151
  from django_smartbase_admin.engine.field import SBAdminField
99
152
 
@@ -144,7 +197,7 @@ class SBAdminBaseView(object):
144
197
  field.model_field = model_field
145
198
  return field
146
199
 
147
- def get_username_data(self, request):
200
+ def get_username_data(self, request) -> dict[str, Any]:
148
201
  if request.request_data.user.first_name and request.request_data.user.last_name:
149
202
  return {
150
203
  "full_name": f"{request.request_data.user.first_name} {request.request_data.user.last_name}",
@@ -155,30 +208,82 @@ class SBAdminBaseView(object):
155
208
  "initials": request.request_data.user.username[0],
156
209
  }
157
210
 
158
- def get_sbadmin_detail_actions(self, object_id):
211
+ def get_sbadmin_detail_actions(
212
+ self, request, object_id: int | str | None = None
213
+ ) -> Iterable[SBAdminCustomAction] | None:
159
214
  return self.sbadmin_detail_actions
160
215
 
161
- def get_global_context(self, request, object_id=None):
216
+ def get_color_scheme_context(self, request):
217
+ from django_smartbase_admin.views.user_config_view import ColorSchemeForm
218
+
219
+ user_config = SBAdminUserConfigurationService.get_user_config(request)
220
+ color_scheme_form = ColorSchemeForm(instance=user_config)
221
+ return {
222
+ "user_config": user_config,
223
+ "color_scheme_form": color_scheme_form,
224
+ }
225
+
226
+ def get_add_label(
227
+ self, request: HttpRequest, object_id: str | None = None
228
+ ) -> str | None:
229
+ return self.add_label
230
+
231
+ def get_change_label(
232
+ self, request: HttpRequest, object_id: str | None = None
233
+ ) -> str | None:
234
+ return self.change_label
235
+
236
+ def get_global_context(
237
+ self, request, object_id: int | str | None = None
238
+ ) -> dict[str, Any]:
162
239
  return {
163
240
  "view_id": self.get_id(),
164
241
  "configuration": request.request_data.configuration,
165
242
  "request_data": request.request_data,
243
+ "add_label": self.get_add_label(request, object_id),
244
+ "change_label": self.get_change_label(request, object_id),
166
245
  "DETAIL_STRUCTURE_RIGHT_CLASS": DETAIL_STRUCTURE_RIGHT_CLASS,
167
246
  "OVERRIDE_CONTENT_OF_NOTIFICATION": OVERRIDE_CONTENT_OF_NOTIFICATION,
168
247
  "username_data": self.get_username_data(request),
169
- "detail_actions": self.get_sbadmin_detail_actions(object_id),
248
+ "detail_actions": self.get_sbadmin_detail_actions(request, object_id),
249
+ SBADMIN_IS_MODAL_VAR: is_modal(request),
250
+ SBADMIN_RELOAD_ON_SAVE_VAR: SBADMIN_RELOAD_ON_SAVE_VAR in request.GET
251
+ or SBADMIN_RELOAD_ON_SAVE_VAR in request.POST,
170
252
  "const": json.dumps(
171
253
  {
172
254
  "MULTISELECT_FILTER_MAX_CHOICES_SHOWN": MULTISELECT_FILTER_MAX_CHOICES_SHOWN,
173
255
  "AUTOCOMPLETE_PAGE_SIZE": AUTOCOMPLETE_PAGE_SIZE,
174
256
  "GLOBAL_FILTER_ALIAS_WIDGET_ID": GLOBAL_FILTER_ALIAS_WIDGET_ID,
257
+ "TABLE_RELOAD_DATA_EVENT_NAME": TABLE_RELOAD_DATA_EVENT_NAME,
258
+ "TABLE_UPDATE_ROW_DATA_EVENT_NAME": TABLE_UPDATE_ROW_DATA_EVENT_NAME,
259
+ "SELECT_ALL_KEYWORD": SELECT_ALL_KEYWORD,
260
+ "SUPPORTED_FILE_TYPE_ICONS": SUPPORTED_FILE_TYPE_ICONS,
261
+ "STATIC_BASE_PATH": f"{settings.STATIC_URL}sb_admin",
175
262
  }
176
263
  ),
264
+ **self.get_color_scheme_context(request),
177
265
  }
178
266
 
179
- def get_model_path(self):
267
+ def get_model_path(self) -> str:
180
268
  return SBAdminViewService.get_model_path(self.model)
181
269
 
270
+ def process_field_data(
271
+ self,
272
+ request,
273
+ field: "SBAdminField",
274
+ obj_id: Any,
275
+ value: Any,
276
+ additional_data: dict[str, Any],
277
+ ) -> Any:
278
+ is_xlsx_export = request.request_data.action == Action.XLSX_EXPORT.value
279
+ if field.view_method:
280
+ value = field.view_method(obj_id, value, **additional_data)
281
+ if is_xlsx_export and getattr(field.xlsx_options, "python_formatter", None):
282
+ value = field.xlsx_options.python_formatter(obj_id, value)
283
+ elif field.python_formatter:
284
+ value = field.python_formatter(obj_id, value)
285
+ return value
286
+
182
287
 
183
288
  class SBAdminBaseQuerysetMixin(object):
184
289
  def get_queryset(self, request=None):
@@ -205,11 +310,13 @@ class SBAdminBaseListView(SBAdminBaseView):
205
310
  sbadmin_list_reorder_field = None
206
311
  search_field_placeholder = _("Search...")
207
312
  filters_version = None
313
+ sbadmin_actions_initialized = False
314
+ sbadmin_list_action_class = SBAdminListAction
208
315
 
209
- def activate_reorder(self, request):
316
+ def activate_reorder(self, request) -> None:
210
317
  request.reorder_active = True
211
318
 
212
- def action_list_json_reorder(self, request, modifier):
319
+ def action_list_json_reorder(self, request, modifier) -> JsonResponse:
213
320
  self.activate_reorder(request)
214
321
  return self.action_list_json(request, modifier, page_size=100)
215
322
 
@@ -238,24 +345,25 @@ class SBAdminBaseListView(SBAdminBaseView):
238
345
  url=self.get_menu_view_url(request),
239
346
  ),
240
347
  ],
348
+ template=self.reorder_list_template,
241
349
  )
242
350
 
243
- def is_reorder_active(self, request):
351
+ def is_reorder_active(self, request) -> bool:
244
352
  return (
245
- self.is_reorder_available()
353
+ self.is_reorder_available(request)
246
354
  and getattr(request, "reorder_active", False) == True
247
355
  )
248
356
 
249
- def is_reorder_available(self):
357
+ def is_reorder_available(self, request) -> str | None:
250
358
  return self.sbadmin_list_reorder_field
251
359
 
252
- def action_table_reorder(self, request, modifier):
360
+ def action_table_reorder(self, request, modifier) -> JsonResponse:
253
361
  self.activate_reorder(request)
254
362
  qs = self.get_queryset(request)
255
363
  pk_field = SBAdminViewService.get_pk_field_for_model(self.model).name
256
364
  old_order = dict(
257
365
  qs.values_list(pk_field, self.sbadmin_list_reorder_field).order_by(
258
- *self.get_list_ordering()
366
+ *self.get_list_ordering(request)
259
367
  )
260
368
  )
261
369
  current_row_id = json.loads(request.POST.get("currentRowId", ""))
@@ -283,31 +391,50 @@ class SBAdminBaseListView(SBAdminBaseView):
283
391
  )
284
392
  return JsonResponse({"message": request.POST})
285
393
 
286
- def action_table_data_edit(self, request, modifier):
394
+ def action_table_data_edit(self, request, modifier) -> HttpResponse:
287
395
  current_row_id = json.loads(request.POST.get("currentRowId", ""))
288
396
  column_field_name = request.POST.get("columnFieldName", "")
289
397
  cell_value = request.POST.get("cellValue", "")
290
- return JsonResponse({"message": "Not Implemented"})
398
+ messages.add_message(request, messages.ERROR, "Not Implemented")
399
+ return HttpResponse(status=200, content=render_notifications(request))
291
400
 
292
- def init_view_dynamic(self, request, request_data=None, **kwargs):
401
+ def init_actions(self, request) -> None:
402
+ if self.sbadmin_actions_initialized:
403
+ return
404
+ self.process_actions(request, self.get_sbadmin_list_selection_actions(request))
405
+ self.process_actions(request, self.get_sbadmin_list_actions(request))
406
+ self.sbadmin_actions_initialized = True
407
+
408
+ def init_view_dynamic(self, request, request_data=None, **kwargs) -> None:
293
409
  super().init_view_dynamic(request, request_data, **kwargs)
294
410
  self.init_fields_cache(
295
- self.get_sbamin_list_display(request), request.request_data.configuration
411
+ self.get_sbadmin_list_display(request), request.request_data.configuration
296
412
  )
413
+ self.init_actions(request)
297
414
 
298
- def get_sbamin_list_display(self, request):
299
- return self.sbadmin_list_display or self.list_display
415
+ def get_sbadmin_list_display(self, request) -> list[str] | list:
416
+ return self.sbadmin_list_display or self.list_display or []
300
417
 
301
- def register_autocomplete_views(self, request):
418
+ def register_autocomplete_views(self, request) -> None:
302
419
  super().register_autocomplete_views(request)
303
420
  self.init_fields_cache(
304
- self.get_sbamin_list_display(request), request.request_data.configuration
421
+ self.get_sbadmin_list_display(request),
422
+ request.request_data.configuration,
423
+ force=True,
305
424
  )
306
-
307
- def get_list_display(self, request):
425
+ all_list_actions = self.get_sbadmin_list_selection_actions(
426
+ request
427
+ ) + self.get_sbadmin_list_actions(request)
428
+ for list_action in all_list_actions:
429
+ if isinstance(list_action, SBAdminFormViewAction):
430
+ form = list_action.target_view.form_class
431
+ form.view = self
432
+ form()
433
+
434
+ def get_list_display(self, request) -> list[str] | list:
308
435
  return [
309
436
  getattr(field, "name", field)
310
- for field in self.get_sbamin_list_display(request)
437
+ for field in self.get_sbadmin_list_display(request)
311
438
  ]
312
439
 
313
440
  def get_search_fields(self, request):
@@ -316,12 +443,12 @@ class SBAdminBaseListView(SBAdminBaseView):
316
443
  else:
317
444
  return []
318
445
 
319
- def get_list_ordering(self):
446
+ def get_list_ordering(self, request) -> Iterable[str] | list:
320
447
  return self.ordering or []
321
448
 
322
- def get_list_initial_order(self):
449
+ def get_list_initial_order(self, request) -> list[dict[str, Any]]:
323
450
  order = []
324
- for order_field in self.get_list_ordering():
451
+ for order_field in self.get_list_ordering(request):
325
452
  direction = "desc" if order_field.startswith("-") else "asc"
326
453
  order.append(
327
454
  {
@@ -331,15 +458,15 @@ class SBAdminBaseListView(SBAdminBaseView):
331
458
  )
332
459
  return order
333
460
 
334
- def get_list_per_page(self):
461
+ def get_list_per_page(self, request) -> int | None:
335
462
  return self.list_per_page
336
463
 
337
- def has_add_permission(self, request):
464
+ def has_add_permission(self, request, obj=None) -> bool:
338
465
  if self.is_reorder_active(request):
339
466
  return False
340
467
  return super().has_add_permission(request)
341
468
 
342
- def get_tabulator_definition(self, request):
469
+ def get_tabulator_definition(self, request) -> dict[str, Any]:
343
470
  view_id = self.get_id()
344
471
  tabulator_definition = {
345
472
  "viewId": view_id,
@@ -356,8 +483,8 @@ class SBAdminBaseListView(SBAdminBaseView):
356
483
  Action.TABLE_REORDER_ACTION.value
357
484
  ),
358
485
  "tableDetailUrl": self.get_detail_url(),
359
- "tableInitialSort": self.get_list_initial_order(),
360
- "tableInitialPageSize": self.get_list_per_page(),
486
+ "tableInitialSort": self.get_list_initial_order(request),
487
+ "tableInitialPageSize": self.get_list_per_page(request),
361
488
  "tableHistoryEnabled": self.sbadmin_table_history_enabled,
362
489
  # used to initialize all columns with these values
363
490
  "defaultColumnData": {},
@@ -399,9 +526,9 @@ class SBAdminBaseListView(SBAdminBaseView):
399
526
  )
400
527
  return tabulator_definition
401
528
 
402
- def _get_sbadmin_list_actions(self):
403
- list_actions = [*(self.get_sbadmin_list_actions() or [])]
404
- if self.is_reorder_available():
529
+ def _get_sbadmin_list_actions(self, request) -> list[SBAdminCustomAction] | list:
530
+ list_actions = [*(self.get_sbadmin_list_actions(request) or [])]
531
+ if self.is_reorder_available(request):
405
532
  list_actions = [
406
533
  *list_actions,
407
534
  SBAdminCustomAction(
@@ -413,18 +540,19 @@ class SBAdminBaseListView(SBAdminBaseView):
413
540
  ]
414
541
  return list_actions
415
542
 
416
- def get_sbadmin_list_actions(self):
543
+ def get_sbadmin_list_actions(self, request) -> list[SBAdminCustomAction]:
417
544
  if not self.sbadmin_list_actions:
418
545
  self.sbadmin_list_actions = [
419
546
  SBAdminCustomAction(
420
547
  title=_("Download XLSX"),
421
548
  view=self,
422
549
  action_id=Action.XLSX_EXPORT.value,
550
+ action_modifier=IGNORE_LIST_SELECTION,
423
551
  )
424
552
  ]
425
553
  return self.sbadmin_list_actions
426
554
 
427
- def get_sbadmin_list_selection_actions(self):
555
+ def get_sbadmin_list_selection_actions(self, request) -> list[SBAdminCustomAction]:
428
556
  if not self.sbadmin_list_selection_actions:
429
557
  self.sbadmin_list_selection_actions = [
430
558
  SBAdminCustomAction(
@@ -441,10 +569,12 @@ class SBAdminBaseListView(SBAdminBaseView):
441
569
  ]
442
570
  return self.sbadmin_list_selection_actions
443
571
 
444
- def get_sbadmin_list_selection_actions_grouped(self, request):
572
+ def get_sbadmin_list_selection_actions_grouped(
573
+ self, request
574
+ ) -> dict[str, list[SBAdminCustomAction]]:
445
575
  result = {}
446
- list_selection_actions = self.process_actions_permissions(
447
- request, self.get_sbadmin_list_selection_actions()
576
+ list_selection_actions = self.process_actions(
577
+ request, self.get_sbadmin_list_selection_actions(request)
448
578
  )
449
579
  for action in list_selection_actions:
450
580
  if not result.get(action.group):
@@ -452,7 +582,7 @@ class SBAdminBaseListView(SBAdminBaseView):
452
582
  result[action.group].append(action)
453
583
  return result
454
584
 
455
- def get_sbadmin_xlsx_options(self):
585
+ def get_sbadmin_xlsx_options(self, request) -> SBAdminXLSXOptions:
456
586
  self.sbadmin_xlsx_options = self.sbadmin_xlsx_options or SBAdminXLSXOptions(
457
587
  header_cell_format=SBAdminXLSXFormat(
458
588
  bg_color="#00aaa7", font_color="#ffffff", bold=True
@@ -464,13 +594,28 @@ class SBAdminBaseListView(SBAdminBaseView):
464
594
  )
465
595
  return self.sbadmin_xlsx_options
466
596
 
467
- def action_xlsx_export(self, request, modifier):
468
- action = SBAdminListAction(self, request)
469
- data = action.get_xlsx_data()
597
+ def action_xlsx_export(self, request, modifier) -> HttpResponse:
598
+ action = self.sbadmin_list_action_class(self, request)
599
+ data = action.get_xlsx_data(request)
470
600
  return SBAdminXLSXExportService.create_workbook_http_respone(*data)
471
601
 
472
602
  def action_bulk_delete(self, request, modifier):
473
- action = SBAdminListAction(self, request)
603
+ action = self.sbadmin_list_action_class(self, request)
604
+ if (
605
+ request.request_data.request_method == "POST"
606
+ and request.headers.get("X-TabulatorRequest", None) == "true"
607
+ ):
608
+ return redirect(
609
+ self.get_action_url("action_bulk_delete")
610
+ + "?"
611
+ + urllib.parse.urlencode(
612
+ {BASE_PARAMS_NAME: json.dumps(action.all_params)}
613
+ )
614
+ )
615
+ if not action.selection_data:
616
+ # don't run with no selection data as it will result in delete of all records
617
+ messages.error(request, _("No selection made."))
618
+ return redirect(self.get_menu_view_url(request))
474
619
  additional_filter = action.get_selection_queryset()
475
620
  response = delete_selected(
476
621
  self, request, self.get_queryset(request).filter(additional_filter)
@@ -479,36 +624,29 @@ class SBAdminBaseListView(SBAdminBaseView):
479
624
  return redirect(self.get_menu_view_url(request))
480
625
  return response
481
626
 
482
- def action_config(self, request, config_name=None):
483
- from django_smartbase_admin.models import SBAdminListViewConfiguration
484
-
485
- config_name = config_name if config_name != "None" else None
627
+ def action_config(self, request, config_id=None):
628
+ config_id = config_id if config_id != "None" else None
486
629
 
487
- name = config_name or request.POST.get(CONFIG_NAME, None)
488
- if name:
489
- name = urllib.parse.unquote(name)
630
+ config_name = request.POST.get(CONFIG_NAME, None)
631
+ if config_name:
632
+ config_name = urllib.parse.unquote(config_name)
490
633
  updated_configuration = None
491
634
  if request.request_data.request_method == "POST":
492
- if name:
493
- updated_configuration, created = (
494
- SBAdminListViewConfiguration.objects.update_or_create(
495
- name=name,
496
- user_id=request.request_data.user.id,
497
- defaults={
498
- "url_params": request.request_data.request_post.get(
499
- URL_PARAMS_NAME
500
- ),
501
- "view": self.get_id(),
502
- "action": None,
503
- "modifier": None,
504
- },
505
- )
635
+ updated_configuration = (
636
+ SBAdminUserConfigurationService.create_or_update_saved_view(
637
+ request,
638
+ view_id=self.get_id(),
639
+ config_id=config_id,
640
+ config_name=config_name,
641
+ url_params=request.request_data.request_post.get(URL_PARAMS_NAME),
506
642
  )
643
+ )
507
644
  if request.request_data.request_method == "DELETE":
508
- if name:
509
- SBAdminListViewConfiguration.objects.by_user_id(
510
- request.request_data.user.id
511
- ).by_name(name).by_view_action_modifier(view=self.get_id()).delete()
645
+ SBAdminUserConfigurationService.delete_saved_view(
646
+ request,
647
+ view_id=self.get_id(),
648
+ config_id=config_id,
649
+ )
512
650
 
513
651
  redirect_to = self.get_redirect_url_from_request(request, updated_configuration)
514
652
 
@@ -536,8 +674,9 @@ class SBAdminBaseListView(SBAdminBaseView):
536
674
  tabulator_definition=None,
537
675
  extra_context=None,
538
676
  list_actions=None,
677
+ template=None,
539
678
  ):
540
- action = SBAdminListAction(
679
+ action = self.sbadmin_list_action_class(
541
680
  self,
542
681
  request,
543
682
  page_size=page_size,
@@ -558,7 +697,8 @@ class SBAdminBaseListView(SBAdminBaseView):
558
697
 
559
698
  return TemplateResponse(
560
699
  request,
561
- self.change_list_template
700
+ template
701
+ or self.change_list_template
562
702
  or [
563
703
  "admin/%s/%s/change_list.html"
564
704
  % (self.model._meta.app_label, self.model._meta.model_name),
@@ -568,20 +708,20 @@ class SBAdminBaseListView(SBAdminBaseView):
568
708
  extra_context,
569
709
  )
570
710
 
571
- def action_list_json(self, request, modifier, page_size=None):
572
- action = SBAdminListAction(self, request, page_size=page_size)
711
+ def action_list_json(self, request, modifier, page_size=None) -> JsonResponse:
712
+ action = self.sbadmin_list_action_class(self, request, page_size=page_size)
573
713
  data = action.get_json_data()
574
714
  return JsonResponse(data=data, safe=False)
575
715
 
576
- def get_sbadmin_list_filter(self, request):
716
+ def get_sbadmin_list_filter(self, request) -> Iterable | None:
577
717
  return self.sbadmin_list_filter
578
718
 
579
- def get_all_config(self, request):
719
+ def get_all_config(self, request) -> dict[str, Any]:
580
720
  all_config = {"name": _("All"), "url_params": {}, "default": True}
581
721
  list_filter = self.get_sbadmin_list_filter(request) or []
582
722
  if not list_filter:
583
723
  return all_config
584
- list_fields = self.get_sbamin_list_display(request) or []
724
+ list_fields = self.get_sbadmin_list_display(request) or []
585
725
  self.init_fields_cache(list_fields, request.request_data.configuration)
586
726
  base_filter = {
587
727
  getattr(field, "filter_field", field): ""
@@ -600,45 +740,42 @@ class SBAdminBaseListView(SBAdminBaseView):
600
740
  }
601
741
  return all_config
602
742
 
603
- def get_sbadmin_list_view_config(self, request):
743
+ def get_sbadmin_list_view_config(self, request) -> list:
604
744
  return self.sbadmin_list_view_config or []
605
745
 
606
- def get_base_config(self, request):
746
+ def get_base_config(self, request) -> list[dict[str, Any]]:
607
747
  sbadmin_list_config = self.get_sbadmin_list_view_config(request)
608
748
  list_view_config = [self.get_all_config(request), *sbadmin_list_config]
609
749
  views = []
610
750
  for defined_view in list_view_config:
751
+ url_params = SBAdminViewService.process_url_params(
752
+ view_id=self.get_id(),
753
+ url_params=defined_view["url_params"],
754
+ filter_version=self.get_filters_version(request),
755
+ )
611
756
  views.append(
612
757
  {
613
758
  "name": defined_view["name"],
614
- "url_params": SBAdminViewService.json_dumps_for_url(
615
- defined_view["url_params"]
616
- ),
759
+ "url_params": SBAdminViewService.json_dumps_for_url(url_params),
617
760
  "default": True,
618
761
  }
619
762
  )
620
763
  return views
621
764
 
622
- def get_config_data(self, request):
623
- from django_smartbase_admin.models import SBAdminListViewConfiguration
624
-
625
- current_views = list(
626
- SBAdminListViewConfiguration.objects.by_user_id(
627
- request.request_data.user.id
628
- )
629
- .by_view_action_modifier(view=self.get_id())
630
- .values()
765
+ def get_config_data(self, request) -> dict[str, list[dict[str, Any]]]:
766
+ current_views = SBAdminUserConfigurationService.get_saved_views(
767
+ request, view_id=self.get_id()
631
768
  )
632
769
  for view in current_views:
633
- view["detail_url"] = self.get_config_url(view["name"])
770
+ view["detail_url"] = self.get_config_url(request, view["id"])
634
771
  config_views = self.get_base_config(request)
635
772
  config_views.extend(current_views)
636
773
  return {"current_views": config_views}
637
774
 
638
- def get_ajax_url(self):
775
+ def get_ajax_url(self) -> str:
639
776
  return self.get_action_url(Action.LIST_JSON.value)
640
777
 
641
- def get_detail_url(self):
778
+ def get_detail_url(self) -> str:
642
779
  url = reverse(
643
780
  "sb_admin:sb_admin_base",
644
781
  kwargs={
@@ -649,21 +786,21 @@ class SBAdminBaseListView(SBAdminBaseView):
649
786
  )
650
787
  return f"{url}/{OBJECT_ID_PLACEHOLDER}"
651
788
 
652
- def get_config_url(self, config_name=None):
789
+ def get_config_url(self, request, config_name=None) -> str:
653
790
  return self.get_action_url(Action.CONFIG.value, config_name)
654
791
 
655
- def get_new_url(self):
792
+ def get_new_url(self, request) -> None:
656
793
  return None
657
794
 
658
- def get_context_data(self, request):
795
+ def get_context_data(self, request) -> dict:
659
796
  return {}
660
797
 
661
- def get_filters_version(self, request):
798
+ def get_filters_version(self, request) -> FilterVersions:
662
799
  return (
663
800
  self.filters_version or request.request_data.configuration.filters_version
664
801
  )
665
802
 
666
- def get_filters_template_name(self, request):
803
+ def get_filters_template_name(self, request) -> str:
667
804
  filters_version = self.get_filters_version(request)
668
805
  if filters_version is FilterVersions.FILTERS_VERSION_2:
669
806
  return "sb_admin/components/filters_v2.html"
@@ -671,7 +808,7 @@ class SBAdminBaseListView(SBAdminBaseView):
671
808
  # default
672
809
  return "sb_admin/components/filters.html"
673
810
 
674
- def get_tabulator_header_template_name(self, request):
811
+ def get_tabulator_header_template_name(self, request) -> str:
675
812
  filters_version = self.get_filters_version(request)
676
813
  if filters_version is FilterVersions.FILTERS_VERSION_2:
677
814
  return "sb_admin/actions/partials/tabulator_header_v2.html"
@@ -679,5 +816,5 @@ class SBAdminBaseListView(SBAdminBaseView):
679
816
  # default
680
817
  return "sb_admin/actions/partials/tabulator_header_v1.html"
681
818
 
682
- def get_search_field_placeholder(self):
819
+ def get_search_field_placeholder(self, request) -> str:
683
820
  return self.search_field_placeholder