wbcore 1.46.0__py2.py3-none-any.whl → 1.58.2__py2.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 (321) hide show
  1. wbcore/cache/decorators.py +5 -3
  2. wbcore/cache/registry.py +14 -7
  3. wbcore/configs/__init__.py +1 -0
  4. wbcore/configs/configs.py +5 -0
  5. wbcore/configs/decorators.py +1 -1
  6. wbcore/configurations/configurations/apps.py +3 -2
  7. wbcore/configurations/configurations/authentication.py +1 -1
  8. wbcore/configurations/configurations/base.py +1 -1
  9. wbcore/configurations/configurations/cache.py +1 -1
  10. wbcore/configurations/configurations/i18nl10n.py +2 -1
  11. wbcore/configurations/configurations/maintenance.py +1 -1
  12. wbcore/configurations/configurations/media.py +1 -1
  13. wbcore/configurations/configurations/middleware.py +1 -1
  14. wbcore/configurations/configurations/rest_framework.py +1 -1
  15. wbcore/configurations/configurations/static.py +3 -3
  16. wbcore/configurations/configurations/wbcore.py +1 -1
  17. wbcore/content_type/serializers.py +13 -5
  18. wbcore/content_type/utils.py +3 -3
  19. wbcore/content_type/viewsets.py +2 -2
  20. wbcore/contrib/agenda/filters/calendar_item.py +5 -4
  21. wbcore/contrib/agenda/locale/de/LC_MESSAGES/django.po +145 -52
  22. wbcore/contrib/agenda/locale/de/LC_MESSAGES/django.po.translated +236 -0
  23. wbcore/contrib/agenda/locale/en/LC_MESSAGES/django.po +200 -0
  24. wbcore/contrib/agenda/locale/fr/LC_MESSAGES/django.po +201 -0
  25. wbcore/contrib/agenda/viewsets/calendar_items.py +7 -7
  26. wbcore/contrib/agenda/viewsets/menu/calendar_items.py +0 -6
  27. wbcore/contrib/ai/exceptions.py +5 -5
  28. wbcore/contrib/ai/llm/config.py +76 -27
  29. wbcore/contrib/ai/llm/mixins.py +5 -8
  30. wbcore/contrib/ai/llm/utils.py +50 -26
  31. wbcore/contrib/authentication/admin.py +2 -2
  32. wbcore/contrib/authentication/factories/__init__.py +8 -1
  33. wbcore/contrib/authentication/factories/users.py +19 -0
  34. wbcore/contrib/authentication/filters.py +1 -2
  35. wbcore/contrib/authentication/locale/de/LC_MESSAGES/django.po +209 -187
  36. wbcore/contrib/authentication/locale/de/LC_MESSAGES/django.po.translated +634 -0
  37. wbcore/contrib/authentication/locale/en/LC_MESSAGES/django.po +590 -0
  38. wbcore/contrib/authentication/locale/fr/LC_MESSAGES/django.po +592 -0
  39. wbcore/contrib/authentication/models/users.py +3 -3
  40. wbcore/contrib/authentication/models/users_activities.py +1 -1
  41. wbcore/contrib/authentication/serializers/users.py +2 -2
  42. wbcore/contrib/authentication/tests/test_tokens.py +3 -3
  43. wbcore/contrib/authentication/tests/test_users.py +0 -1
  44. wbcore/contrib/authentication/urls.py +0 -4
  45. wbcore/contrib/authentication/viewsets/endpoints/user_activities.py +2 -11
  46. wbcore/contrib/authentication/viewsets/endpoints/users.py +0 -3
  47. wbcore/contrib/authentication/viewsets/user_activities.py +2 -1
  48. wbcore/contrib/authentication/viewsets/users.py +6 -4
  49. wbcore/contrib/color/models.py +2 -1
  50. wbcore/contrib/currency/factories.py +1 -1
  51. wbcore/contrib/currency/import_export/backends/fixerio/currency_fx_rates.py +3 -1
  52. wbcore/contrib/currency/models.py +30 -8
  53. wbcore/contrib/currency/serializers.py +5 -1
  54. wbcore/contrib/currency/tests/test_serializers.py +7 -3
  55. wbcore/contrib/currency/tests/test_viewsets.py +1 -1
  56. wbcore/contrib/currency/viewsets/currency.py +2 -2
  57. wbcore/contrib/currency/viewsets/endpoints/currency_fx_rates.py +0 -9
  58. wbcore/contrib/dataloader/tests/test/dataloaders/protocols.py +1 -2
  59. wbcore/contrib/dataloader/utils.py +2 -2
  60. wbcore/contrib/directory/factories/__init__.py +1 -1
  61. wbcore/contrib/directory/factories/entries.py +2 -1
  62. wbcore/contrib/directory/filters/entries.py +9 -0
  63. wbcore/contrib/directory/locale/de/LC_MESSAGES/django.po +728 -714
  64. wbcore/contrib/directory/locale/de/LC_MESSAGES/django.po.translated +1779 -0
  65. wbcore/contrib/directory/locale/en/LC_MESSAGES/django.po +1652 -0
  66. wbcore/contrib/directory/locale/fr/LC_MESSAGES/django.po +1654 -0
  67. wbcore/contrib/directory/migrations/0011_person_description_person_i18n.py +24 -0
  68. wbcore/contrib/directory/migrations/0012_alter_person_managers.py +20 -0
  69. wbcore/contrib/directory/migrations/0013_alter_clientmanagerrelationship_options.py +17 -0
  70. wbcore/contrib/directory/models/contacts.py +2 -2
  71. wbcore/contrib/directory/models/entries.py +31 -5
  72. wbcore/contrib/directory/models/relationships.py +31 -35
  73. wbcore/contrib/directory/permissions.py +6 -0
  74. wbcore/contrib/directory/serializers/companies.py +16 -8
  75. wbcore/contrib/directory/serializers/contacts.py +8 -8
  76. wbcore/contrib/directory/serializers/entries.py +26 -15
  77. wbcore/contrib/directory/serializers/entry_representations.py +4 -2
  78. wbcore/contrib/directory/serializers/persons.py +12 -10
  79. wbcore/contrib/directory/serializers/relationships.py +2 -2
  80. wbcore/contrib/directory/tests/conftest.py +2 -0
  81. wbcore/contrib/directory/tests/disable_signals.py +11 -1
  82. wbcore/contrib/directory/tests/signals.py +2 -2
  83. wbcore/contrib/directory/tests/test_models.py +88 -66
  84. wbcore/contrib/directory/tests/test_serializers.py +1 -1
  85. wbcore/contrib/directory/tests/test_viewsets.py +8 -8
  86. wbcore/contrib/directory/viewsets/buttons/__init__.py +1 -1
  87. wbcore/contrib/directory/viewsets/buttons/relationships.py +32 -0
  88. wbcore/contrib/directory/viewsets/contacts.py +6 -6
  89. wbcore/contrib/directory/viewsets/display/__init__.py +1 -1
  90. wbcore/contrib/directory/viewsets/display/contacts.py +1 -14
  91. wbcore/contrib/directory/viewsets/display/entries.py +68 -38
  92. wbcore/contrib/directory/viewsets/display/relationships.py +26 -50
  93. wbcore/contrib/directory/viewsets/endpoints/relationships.py +1 -26
  94. wbcore/contrib/directory/viewsets/entries.py +8 -6
  95. wbcore/contrib/directory/viewsets/previews/entries.py +3 -3
  96. wbcore/contrib/directory/viewsets/relationships.py +16 -2
  97. wbcore/contrib/directory/viewsets/titles/relationships.py +2 -3
  98. wbcore/contrib/documents/filters.py +0 -2
  99. wbcore/contrib/documents/locale/de/LC_MESSAGES/django.po +103 -94
  100. wbcore/contrib/documents/locale/de/LC_MESSAGES/django.po.translated +285 -0
  101. wbcore/contrib/documents/locale/en/LC_MESSAGES/django.po +271 -0
  102. wbcore/contrib/documents/locale/fr/LC_MESSAGES/django.po +270 -0
  103. wbcore/contrib/documents/tests/test_models.py +32 -28
  104. wbcore/contrib/documents/viewsets/endpoints/shareable_links.py +2 -21
  105. wbcore/contrib/dynamic_preferences/types.py +108 -0
  106. wbcore/contrib/dynamic_preferences/viewsets.py +27 -0
  107. wbcore/contrib/example_app/filters/event.py +3 -1
  108. wbcore/contrib/example_app/filters/match.py +1 -1
  109. wbcore/contrib/example_app/models.py +91 -22
  110. wbcore/contrib/example_app/serializers/person_team.py +4 -4
  111. wbcore/contrib/example_app/templates/example_app/embedded_view.html +19 -0
  112. wbcore/contrib/example_app/tests/e2e/test_teams.py +1 -1
  113. wbcore/contrib/example_app/tests/test_models/test_match.py +17 -7
  114. wbcore/contrib/example_app/urls.py +2 -0
  115. wbcore/contrib/example_app/views.py +7 -0
  116. wbcore/contrib/example_app/viewsets/displays/team.py +23 -4
  117. wbcore/contrib/example_app/viewsets/menu/menus.py +1 -1
  118. wbcore/contrib/example_app/viewsets/menus.py +1 -1
  119. wbcore/contrib/geography/tests/conftest.py +14 -0
  120. wbcore/contrib/geography/tests/test_models.py +23 -8
  121. wbcore/contrib/geography/tests/test_viewsets.py +96 -2
  122. wbcore/contrib/guardian/tests/test_model_mixins.py +3 -4
  123. wbcore/contrib/guardian/tests/test_tasks.py +9 -9
  124. wbcore/contrib/guardian/tests/test_viewsets.py +2 -2
  125. wbcore/contrib/guardian/viewsets/configs/__init__.py +1 -1
  126. wbcore/contrib/guardian/viewsets/configs/buttons.py +9 -0
  127. wbcore/contrib/guardian/viewsets/configs/endpoints.py +7 -0
  128. wbcore/contrib/guardian/viewsets/viewsets.py +2 -0
  129. wbcore/contrib/i18n/__init__.py +2 -0
  130. wbcore/contrib/i18n/buttons.py +33 -0
  131. wbcore/contrib/i18n/serializers/__init__.py +0 -0
  132. wbcore/contrib/i18n/serializers/fields.py +20 -0
  133. wbcore/contrib/i18n/serializers/mixins.py +13 -0
  134. wbcore/contrib/i18n/tests/conftest.py +11 -0
  135. wbcore/contrib/i18n/tests/test_viewsets.py +67 -0
  136. wbcore/contrib/i18n/translation.py +140 -0
  137. wbcore/contrib/i18n/viewsets.py +36 -0
  138. wbcore/contrib/icons/backends/default.py +1 -0
  139. wbcore/contrib/icons/backends/material.py +1 -0
  140. wbcore/contrib/icons/icons.py +5 -8
  141. wbcore/contrib/io/admin.py +1 -0
  142. wbcore/contrib/io/backends/mail.py +3 -2
  143. wbcore/contrib/io/backends/utils.py +14 -17
  144. wbcore/contrib/io/exceptions.py +8 -0
  145. wbcore/contrib/io/factories.py +1 -1
  146. wbcore/contrib/io/import_export/backends/mail.py +1 -0
  147. wbcore/contrib/io/import_export/backends/sftp.py +29 -20
  148. wbcore/contrib/io/import_export/backends/stream.py +2 -2
  149. wbcore/contrib/io/import_export/parsers/__init__.py +0 -0
  150. wbcore/contrib/io/import_export/parsers/base_csv.py +36 -0
  151. wbcore/contrib/io/import_export/parsers/resources.py +50 -0
  152. wbcore/contrib/io/imports.py +33 -25
  153. wbcore/contrib/io/locale/de/LC_MESSAGES/django.po +114 -22
  154. wbcore/contrib/io/locale/de/LC_MESSAGES/django.po.translated +103 -0
  155. wbcore/contrib/io/locale/en/LC_MESSAGES/django.po +138 -0
  156. wbcore/contrib/io/locale/fr/LC_MESSAGES/django.po +138 -0
  157. wbcore/contrib/io/migrations/0008_importsource_resource_kwargs.py +18 -0
  158. wbcore/contrib/io/models.py +65 -45
  159. wbcore/contrib/io/resources.py +0 -6
  160. wbcore/contrib/io/serializers.py +2 -2
  161. wbcore/contrib/io/signals.py +4 -0
  162. wbcore/contrib/io/tests/test_backends.py +19 -13
  163. wbcore/contrib/io/tests/test_exports.py +1 -1
  164. wbcore/contrib/io/tests/test_imports.py +1 -1
  165. wbcore/contrib/io/tests/test_models.py +47 -14
  166. wbcore/contrib/io/tests/test_viewsets.py +271 -0
  167. wbcore/contrib/io/viewset_mixins.py +41 -54
  168. wbcore/contrib/notifications/admin.py +1 -0
  169. wbcore/contrib/notifications/apps.py +2 -1
  170. wbcore/contrib/notifications/backends/abstract_backend.py +2 -4
  171. wbcore/contrib/notifications/backends/firebase/backends.py +5 -2
  172. wbcore/contrib/notifications/dispatch.py +18 -7
  173. wbcore/contrib/notifications/factories/notification_types.py +1 -0
  174. wbcore/contrib/notifications/locale/de/LC_MESSAGES/django.po +25 -19
  175. wbcore/contrib/notifications/locale/de/LC_MESSAGES/django.po.translated +63 -0
  176. wbcore/contrib/notifications/locale/en/LC_MESSAGES/django.po +61 -0
  177. wbcore/contrib/notifications/locale/fr/LC_MESSAGES/django.po +62 -0
  178. wbcore/contrib/notifications/migrations/0008_notificationtype_is_lock.py +18 -0
  179. wbcore/contrib/notifications/migrations/0009_alter_notificationtypesetting_options_and_more.py +32 -0
  180. wbcore/contrib/notifications/models/notification_types.py +67 -24
  181. wbcore/contrib/notifications/serializers/notification_types.py +16 -1
  182. wbcore/contrib/notifications/tests/test_models/test_tokens.py +8 -0
  183. wbcore/contrib/notifications/tests/test_serializers/test_notification_types.py +5 -0
  184. wbcore/contrib/notifications/tests/test_viewsets/test_notification_types.py +3 -5
  185. wbcore/contrib/notifications/utils.py +3 -2
  186. wbcore/contrib/notifications/viewsets/configs/notification_types.py +28 -6
  187. wbcore/contrib/notifications/viewsets/menus.py +1 -1
  188. wbcore/contrib/notifications/viewsets/notification_types.py +12 -2
  189. wbcore/contrib/pandas/fields.py +38 -10
  190. wbcore/contrib/pandas/filters.py +4 -1
  191. wbcore/contrib/pandas/filterset.py +8 -7
  192. wbcore/contrib/pandas/tests/test_fields/test_number_fields.py +2 -7
  193. wbcore/contrib/pandas/utils.py +1 -1
  194. wbcore/contrib/pandas/views.py +14 -13
  195. wbcore/contrib/tags/models/tags.py +4 -1
  196. wbcore/contrib/workflow/factories/display.py +2 -2
  197. wbcore/contrib/workflow/factories/transition.py +16 -15
  198. wbcore/contrib/workflow/locale/de/LC_MESSAGES/django.po +457 -566
  199. wbcore/contrib/workflow/locale/de/LC_MESSAGES/django.po.translated +1326 -0
  200. wbcore/contrib/workflow/locale/en/LC_MESSAGES/django.po +1102 -0
  201. wbcore/contrib/workflow/locale/fr/LC_MESSAGES/django.po +1114 -0
  202. wbcore/contrib/workflow/models/data.py +7 -4
  203. wbcore/contrib/workflow/models/process.py +2 -2
  204. wbcore/contrib/workflow/models/step.py +57 -15
  205. wbcore/contrib/workflow/serializers/data.py +8 -8
  206. wbcore/contrib/workflow/serializers/process.py +3 -2
  207. wbcore/contrib/workflow/tests/conftest.py +224 -0
  208. wbcore/contrib/workflow/tests/test_dispatch.py +82 -77
  209. wbcore/contrib/workflow/tests/test_displays.py +10 -88
  210. wbcore/contrib/workflow/tests/test_filters.py +57 -40
  211. wbcore/contrib/workflow/tests/test_models/step/test_decision_step.py +71 -68
  212. wbcore/contrib/workflow/tests/test_models/step/test_email_step.py +78 -38
  213. wbcore/contrib/workflow/tests/test_models/step/test_finish_step.py +152 -90
  214. wbcore/contrib/workflow/tests/test_models/step/test_join_step.py +100 -110
  215. wbcore/contrib/workflow/tests/test_models/step/test_step.py +168 -33
  216. wbcore/contrib/workflow/tests/test_models/test_condition.py +1 -1
  217. wbcore/contrib/workflow/tests/test_models/test_workflow.py +3 -3
  218. wbcore/contrib/workflow/tests/test_serializers.py +172 -150
  219. wbcore/contrib/workflow/tests/test_viewsets.py +264 -323
  220. wbcore/contrib/workflow/tests/test_workflow_assignees.py +215 -205
  221. wbcore/contrib/workflow/viewsets/process.py +4 -1
  222. wbcore/contrib/workflow/workflows/assignees.py +12 -7
  223. wbcore/dynamic_preferences_registry.py +102 -0
  224. wbcore/enums.py +2 -51
  225. wbcore/filters/fields/choices.py +4 -6
  226. wbcore/filters/fields/content_type.py +15 -4
  227. wbcore/filters/fields/datetime.py +50 -25
  228. wbcore/filters/fields/models.py +18 -9
  229. wbcore/filters/fields/numbers.py +9 -8
  230. wbcore/filters/filterset.py +27 -6
  231. wbcore/filters/mixins.py +41 -42
  232. wbcore/forms.py +6 -6
  233. wbcore/fsm/markdown_extensions.py +1 -1
  234. wbcore/fsm/mixins.py +20 -6
  235. wbcore/locale/de/LC_MESSAGES/django.po +982 -397
  236. wbcore/locale/de/LC_MESSAGES/django.po.translated +1580 -0
  237. wbcore/locale/en/LC_MESSAGES/django.po +1234 -0
  238. wbcore/locale/fr/LC_MESSAGES/django.po +1235 -0
  239. wbcore/markdown/models.py +8 -5
  240. wbcore/markdown/views.py +1 -1
  241. wbcore/menus/menus.py +2 -2
  242. wbcore/metadata/configs/buttons/bases.py +10 -7
  243. wbcore/metadata/configs/buttons/buttons.py +2 -1
  244. wbcore/metadata/configs/buttons/enums.py +50 -0
  245. wbcore/metadata/configs/buttons/view_config.py +13 -46
  246. wbcore/metadata/configs/display/display.py +2 -2
  247. wbcore/metadata/configs/display/formatting.py +6 -9
  248. wbcore/metadata/configs/display/instance_display/display.py +5 -2
  249. wbcore/metadata/configs/display/instance_display/pages.py +1 -1
  250. wbcore/metadata/configs/display/instance_display/shortcuts.py +1 -1
  251. wbcore/metadata/configs/display/list_display.py +54 -40
  252. wbcore/metadata/configs/display/models.py +6 -0
  253. wbcore/metadata/configs/display/view_config.py +11 -9
  254. wbcore/metadata/configs/endpoints.py +11 -4
  255. wbcore/metadata/configs/fields.py +6 -1
  256. wbcore/metadata/configs/filter_fields.py +12 -13
  257. wbcore/metadata/configs/identifiers.py +3 -1
  258. wbcore/metadata/tests/test_buttons.py +13 -16
  259. wbcore/models/fields.py +2 -2
  260. wbcore/pagination.py +1 -2
  261. wbcore/permissions/permissions.py +2 -2
  262. wbcore/permissions/utils.py +2 -2
  263. wbcore/release_notes/display.py +2 -8
  264. wbcore/release_notes/serializers.py +2 -9
  265. wbcore/release_notes/viewsets.py +8 -2
  266. wbcore/reversion/viewsets/titles.py +4 -3
  267. wbcore/serializers/__init__.py +2 -0
  268. wbcore/serializers/fields/__init__.py +2 -1
  269. wbcore/serializers/fields/boolean.py +1 -1
  270. wbcore/serializers/fields/choice.py +28 -4
  271. wbcore/serializers/fields/datetime.py +45 -36
  272. wbcore/serializers/fields/fields.py +1 -1
  273. wbcore/serializers/fields/fsm.py +1 -1
  274. wbcore/serializers/fields/list.py +2 -5
  275. wbcore/serializers/fields/mixins.py +24 -11
  276. wbcore/serializers/fields/number.py +6 -23
  277. wbcore/serializers/fields/other.py +2 -10
  278. wbcore/serializers/fields/related.py +4 -6
  279. wbcore/serializers/fields/text.py +1 -1
  280. wbcore/serializers/fields/types.py +2 -0
  281. wbcore/serializers/serializers.py +12 -3
  282. wbcore/signals/__init__.py +1 -0
  283. wbcore/signals/clone.py +4 -0
  284. wbcore/signals/models.py +2 -6
  285. wbcore/tasks.py +2 -2
  286. wbcore/templates/wbcore/email_base_template.html +3 -3
  287. wbcore/test/e2e_helpers_methods/e2e_checks.py +10 -4
  288. wbcore/test/e2e_helpers_methods/e2e_helper_methods.py +4 -2
  289. wbcore/test/mixins.py +52 -102
  290. wbcore/test/tests.py +6 -9
  291. wbcore/test/utils.py +3 -4
  292. wbcore/tests/e2e/test_e2e.py +2 -2
  293. wbcore/tests/test_cache/test_decorators.py +4 -7
  294. wbcore/tests/test_configs.py +2 -5
  295. wbcore/tests/test_enums.py +2 -1
  296. wbcore/tests/test_fields/test_choice_fields.py +9 -1
  297. wbcore/tests/test_fields/test_number_fields.py +7 -15
  298. wbcore/tests/test_fields/test_other_fields.py +1 -2
  299. wbcore/tests/test_filters/test_mixins.py +35 -35
  300. wbcore/tests/test_list_display.py +0 -2
  301. wbcore/tests/test_models/test_mixins.py +1 -1
  302. wbcore/tests/test_utils/test_date.py +1 -1
  303. wbcore/tests/test_utils/test_date_builder.py +25 -1
  304. wbcore/tests/test_utils/test_primary.py +1 -1
  305. wbcore/urls.py +4 -0
  306. wbcore/utils/date.py +18 -2
  307. wbcore/utils/figures.py +2 -2
  308. wbcore/utils/models.py +21 -4
  309. wbcore/utils/reportlab.py +7 -0
  310. wbcore/utils/rrules.py +3 -1
  311. wbcore/utils/string_loader.py +1 -1
  312. wbcore/utils/strings.py +3 -3
  313. wbcore/utils/views.py +8 -3
  314. wbcore/viewsets/mixins.py +9 -4
  315. {wbcore-1.46.0.dist-info → wbcore-1.58.2.dist-info}/METADATA +9 -5
  316. {wbcore-1.46.0.dist-info → wbcore-1.58.2.dist-info}/RECORD +317 -271
  317. wbcore/contrib/geography/tests/test_serializers.py +0 -7
  318. wbcore/contrib/geography/tests/tests.py +0 -13
  319. wbcore/contrib/io/tests/tests.py +0 -19
  320. wbcore/contrib/workflow/tests/tests.py +0 -25
  321. {wbcore-1.46.0.dist-info → wbcore-1.58.2.dist-info}/WHEEL +0 -0
@@ -1,5 +1,6 @@
1
1
  from typing import Iterable
2
2
 
3
+ from celery import shared_task
3
4
  from django.conf import settings
4
5
  from django.contrib.auth import get_user_model
5
6
  from django.db import transaction
@@ -43,22 +44,32 @@ def send_notification(
43
44
  if isinstance(users, User):
44
45
  users = [users]
45
46
  for user in users:
46
- notification_user_settings = NotificationTypeSetting.objects.filter(
47
- notification_type__code=code,
48
- user=user,
49
- )
50
- if user.is_active and notification_user_settings.exists():
47
+ notification_type = NotificationType.objects.get(code=code)
48
+ if (
49
+ user.is_active
50
+ and NotificationTypeSetting.objects.filter(notification_type=notification_type, user=user).exists()
51
+ ):
51
52
  if not endpoint:
52
53
  endpoint = reverse(reverse_name, reverse_args, reverse_kwargs) if reverse_name else None
53
54
  notification = Notification.objects.create(
54
55
  title=title,
55
56
  body=body,
56
57
  user=user,
57
- notification_type=NotificationType.objects.get(code=code),
58
+ notification_type=notification_type,
58
59
  endpoint=endpoint,
59
60
  sent=timezone.now(),
60
61
  )
61
- transaction.on_commit(lambda: send_notification_task.delay(notification.pk))
62
+ transaction.on_commit(
63
+ lambda notification_pk=notification.pk: send_notification_task.delay(notification_pk)
64
+ )
65
+
66
+
67
+ @shared_task()
68
+ def send_notification_as_task(code, title, body, user_id, **kwargs):
69
+ if not isinstance(user_id, list):
70
+ user_id = [user_id]
71
+ user = User.objects.filter(id__in=user_id)
72
+ send_notification(code, title, body, user, **kwargs)
62
73
 
63
74
 
64
75
  @receiver(handle_widget_sharing)
@@ -21,3 +21,4 @@ class NotificationTypeSettingModelFactory(factory.django.DjangoModelFactory):
21
21
 
22
22
  class Meta:
23
23
  model = NotificationTypeSetting
24
+ django_get_or_create = ("user", "notification_type")
@@ -1,60 +1,66 @@
1
- # GERMAN TRANSLATIONS FOR NOTIFICATION
1
+ # SOME DESCRIPTIVE TITLE.
2
2
  # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
3
3
  # This file is distributed under the same license as the PACKAGE package.
4
4
  # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
5
5
  #
6
+ # Translators:
7
+ # Kevin Decoster, 2025
8
+ #
9
+ #, fuzzy
6
10
  msgid ""
7
11
  msgstr ""
8
- "Project-Id-Version: \n"
12
+ "Project-Id-Version: PACKAGE VERSION\n"
9
13
  "Report-Msgid-Bugs-To: \n"
10
- "POT-Creation-Date: 2023-04-26 10:52+0200\n"
11
- "PO-Revision-Date: 2023-04-25 17:03+0200\n"
12
- "Last-Translator: \n"
13
- "Language-Team: \n"
14
- "Language: de\n"
14
+ "POT-Creation-Date: 2025-05-30 11:37+0200\n"
15
+ "PO-Revision-Date: 2025-05-30 09:40+0000\n"
16
+ "Last-Translator: Kevin Decoster, 2025\n"
17
+ "Language-Team: German (https://app.transifex.com/stainly/teams/171242/de/)\n"
15
18
  "MIME-Version: 1.0\n"
16
19
  "Content-Type: text/plain; charset=UTF-8\n"
17
20
  "Content-Transfer-Encoding: 8bit\n"
21
+ "Language: de\n"
18
22
  "Plural-Forms: nplurals=2; plural=(n != 1);\n"
19
- "X-Generator: Poedit 3.2.2\n"
20
23
 
21
- #: wbcore/contrib/notifications/templates/notifications/notification_template.html:28
24
+ #: contrib/notifications/dispatch.py:72
25
+ msgid "{} shared a widget with you"
26
+ msgstr ""
27
+
28
+ #: contrib/notifications/templates/notifications/notification_template.html:28
22
29
  msgid "Open"
23
30
  msgstr "Öffnen"
24
31
 
25
- #: wbcore/contrib/notifications/viewsets/configs/notifications.py:19
32
+ #: contrib/notifications/viewsets/configs/notifications.py:27
26
33
  msgid "Reading all notifications"
27
34
  msgstr "Lesen aller Benachrichtigungen"
28
35
 
29
- #: wbcore/contrib/notifications/viewsets/configs/notifications.py:21
36
+ #: contrib/notifications/viewsets/configs/notifications.py:29
30
37
  msgid "Do you want to mark notifications as read?"
31
38
  msgstr "Möchten Sie alle Benachrichtigungen als gelesen markieren?"
32
39
 
33
- #: wbcore/contrib/notifications/viewsets/configs/notifications.py:22
40
+ #: contrib/notifications/viewsets/configs/notifications.py:30
34
41
  msgid "Mark all read"
35
42
  msgstr "Alle als gelesen markieren"
36
43
 
37
- #: wbcore/contrib/notifications/viewsets/configs/notifications.py:28
44
+ #: contrib/notifications/viewsets/configs/notifications.py:37
38
45
  msgid "Deleting all read notifications"
39
46
  msgstr "Löschen aller gelesenen Benachrichtigungen"
40
47
 
41
- #: wbcore/contrib/notifications/viewsets/configs/notifications.py:30
48
+ #: contrib/notifications/viewsets/configs/notifications.py:39
42
49
  msgid "Do you want delete all read notifications?"
43
50
  msgstr "Möchten Sie alle gelesenen Benachrichtigungen löschen?"
44
51
 
45
- #: wbcore/contrib/notifications/viewsets/configs/notifications.py:31
52
+ #: contrib/notifications/viewsets/configs/notifications.py:40
46
53
  msgid "Delete all read"
47
54
  msgstr "Alle gelesenen löschen"
48
55
 
49
- #: wbcore/contrib/notifications/viewsets/configs/notifications.py:40
50
- #: wbcore/contrib/notifications/viewsets/configs/notifications.py:41
56
+ #: contrib/notifications/viewsets/configs/notifications.py:48
51
57
  msgid "Open Resource"
52
58
  msgstr "Ressource Öffnen"
53
59
 
54
- #: wbcore/contrib/notifications/viewsets/menus.py:6
60
+ #: contrib/notifications/viewsets/menus.py:6
55
61
  msgid "Notification Types"
56
62
  msgstr "Benachrichtigungstypen"
57
63
 
58
- #: wbcore/contrib/notifications/viewsets/menus.py:12
64
+ #: contrib/notifications/viewsets/menus.py:12
59
65
  msgid "Notifications"
60
66
  msgstr "Benachrichtigungen"
@@ -0,0 +1,63 @@
1
+ # GERMAN TRANSLATIONS FOR NOTIFICATION
2
+ # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
3
+ # This file is distributed under the same license as the PACKAGE package.
4
+ # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
5
+ #
6
+ msgid ""
7
+ msgstr ""
8
+ "Project-Id-Version: \n"
9
+ "Report-Msgid-Bugs-To: \n"
10
+ "POT-Creation-Date: 2025-05-20 09:30+0200\n"
11
+ "PO-Revision-Date: 2023-04-25 17:03+0200\n"
12
+ "Last-Translator: \n"
13
+ "Language-Team: \n"
14
+ "Language: de\n"
15
+ "MIME-Version: 1.0\n"
16
+ "Content-Type: text/plain; charset=UTF-8\n"
17
+ "Content-Transfer-Encoding: 8bit\n"
18
+ "Plural-Forms: nplurals=2; plural=(n != 1);\n"
19
+ "X-Generator: Poedit 3.2.2\n"
20
+
21
+ #: wbcore/contrib/notifications/dispatch.py:72
22
+ msgid "{} shared a widget with you"
23
+ msgstr ""
24
+
25
+ #: wbcore/contrib/notifications/templates/notifications/notification_template.html:28
26
+ msgid "Open"
27
+ msgstr "Öffnen"
28
+
29
+ #: wbcore/contrib/notifications/viewsets/configs/notifications.py:27
30
+ msgid "Reading all notifications"
31
+ msgstr "Lesen aller Benachrichtigungen"
32
+
33
+ #: wbcore/contrib/notifications/viewsets/configs/notifications.py:29
34
+ msgid "Do you want to mark notifications as read?"
35
+ msgstr "Möchten Sie alle Benachrichtigungen als gelesen markieren?"
36
+
37
+ #: wbcore/contrib/notifications/viewsets/configs/notifications.py:30
38
+ msgid "Mark all read"
39
+ msgstr "Alle als gelesen markieren"
40
+
41
+ #: wbcore/contrib/notifications/viewsets/configs/notifications.py:37
42
+ msgid "Deleting all read notifications"
43
+ msgstr "Löschen aller gelesenen Benachrichtigungen"
44
+
45
+ #: wbcore/contrib/notifications/viewsets/configs/notifications.py:39
46
+ msgid "Do you want delete all read notifications?"
47
+ msgstr "Möchten Sie alle gelesenen Benachrichtigungen löschen?"
48
+
49
+ #: wbcore/contrib/notifications/viewsets/configs/notifications.py:40
50
+ msgid "Delete all read"
51
+ msgstr "Alle gelesenen löschen"
52
+
53
+ #: wbcore/contrib/notifications/viewsets/configs/notifications.py:48
54
+ msgid "Open Resource"
55
+ msgstr "Ressource Öffnen"
56
+
57
+ #: wbcore/contrib/notifications/viewsets/menus.py:6
58
+ msgid "Notification Types"
59
+ msgstr "Benachrichtigungstypen"
60
+
61
+ #: wbcore/contrib/notifications/viewsets/menus.py:12
62
+ msgid "Notifications"
63
+ msgstr "Benachrichtigungen"
@@ -0,0 +1,61 @@
1
+ # SOME DESCRIPTIVE TITLE.
2
+ # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
3
+ # This file is distributed under the same license as the PACKAGE package.
4
+ # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
5
+ #
6
+ msgid ""
7
+ msgstr ""
8
+ "Project-Id-Version: PACKAGE VERSION\n"
9
+ "Report-Msgid-Bugs-To: \n"
10
+ "POT-Creation-Date: 2025-05-30 11:37+0200\n"
11
+ "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
12
+ "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13
+ "Language-Team: LANGUAGE <LL@li.org>\n"
14
+ "Language: \n"
15
+ "MIME-Version: 1.0\n"
16
+ "Content-Type: text/plain; charset=UTF-8\n"
17
+ "Content-Transfer-Encoding: 8bit\n"
18
+
19
+ #: contrib/notifications/dispatch.py:72
20
+ msgid "{} shared a widget with you"
21
+ msgstr ""
22
+
23
+ #: contrib/notifications/templates/notifications/notification_template.html:28
24
+ msgid "Open"
25
+ msgstr ""
26
+
27
+ #: contrib/notifications/viewsets/configs/notifications.py:27
28
+ msgid "Reading all notifications"
29
+ msgstr ""
30
+
31
+ #: contrib/notifications/viewsets/configs/notifications.py:29
32
+ msgid "Do you want to mark notifications as read?"
33
+ msgstr ""
34
+
35
+ #: contrib/notifications/viewsets/configs/notifications.py:30
36
+ msgid "Mark all read"
37
+ msgstr ""
38
+
39
+ #: contrib/notifications/viewsets/configs/notifications.py:37
40
+ msgid "Deleting all read notifications"
41
+ msgstr ""
42
+
43
+ #: contrib/notifications/viewsets/configs/notifications.py:39
44
+ msgid "Do you want delete all read notifications?"
45
+ msgstr ""
46
+
47
+ #: contrib/notifications/viewsets/configs/notifications.py:40
48
+ msgid "Delete all read"
49
+ msgstr ""
50
+
51
+ #: contrib/notifications/viewsets/configs/notifications.py:48
52
+ msgid "Open Resource"
53
+ msgstr ""
54
+
55
+ #: contrib/notifications/viewsets/menus.py:6
56
+ msgid "Notification Types"
57
+ msgstr ""
58
+
59
+ #: contrib/notifications/viewsets/menus.py:12
60
+ msgid "Notifications"
61
+ msgstr ""
@@ -0,0 +1,62 @@
1
+ # SOME DESCRIPTIVE TITLE.
2
+ # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
3
+ # This file is distributed under the same license as the PACKAGE package.
4
+ # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
5
+ #
6
+ #, fuzzy
7
+ msgid ""
8
+ msgstr ""
9
+ "Project-Id-Version: PACKAGE VERSION\n"
10
+ "Report-Msgid-Bugs-To: \n"
11
+ "POT-Creation-Date: 2025-05-30 11:37+0200\n"
12
+ "PO-Revision-Date: 2025-05-30 09:40+0000\n"
13
+ "Language-Team: French (https://app.transifex.com/stainly/teams/171242/fr/)\n"
14
+ "MIME-Version: 1.0\n"
15
+ "Content-Type: text/plain; charset=UTF-8\n"
16
+ "Content-Transfer-Encoding: 8bit\n"
17
+ "Language: fr\n"
18
+ "Plural-Forms: nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n"
19
+
20
+ #: contrib/notifications/dispatch.py:72
21
+ msgid "{} shared a widget with you"
22
+ msgstr ""
23
+
24
+ #: contrib/notifications/templates/notifications/notification_template.html:28
25
+ msgid "Open"
26
+ msgstr ""
27
+
28
+ #: contrib/notifications/viewsets/configs/notifications.py:27
29
+ msgid "Reading all notifications"
30
+ msgstr ""
31
+
32
+ #: contrib/notifications/viewsets/configs/notifications.py:29
33
+ msgid "Do you want to mark notifications as read?"
34
+ msgstr ""
35
+
36
+ #: contrib/notifications/viewsets/configs/notifications.py:30
37
+ msgid "Mark all read"
38
+ msgstr ""
39
+
40
+ #: contrib/notifications/viewsets/configs/notifications.py:37
41
+ msgid "Deleting all read notifications"
42
+ msgstr ""
43
+
44
+ #: contrib/notifications/viewsets/configs/notifications.py:39
45
+ msgid "Do you want delete all read notifications?"
46
+ msgstr ""
47
+
48
+ #: contrib/notifications/viewsets/configs/notifications.py:40
49
+ msgid "Delete all read"
50
+ msgstr ""
51
+
52
+ #: contrib/notifications/viewsets/configs/notifications.py:48
53
+ msgid "Open Resource"
54
+ msgstr ""
55
+
56
+ #: contrib/notifications/viewsets/menus.py:6
57
+ msgid "Notification Types"
58
+ msgstr ""
59
+
60
+ #: contrib/notifications/viewsets/menus.py:12
61
+ msgid "Notifications"
62
+ msgstr ""
@@ -0,0 +1,18 @@
1
+ # Generated by Django 5.0.13 on 2025-03-13 12:50
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ dependencies = [
9
+ ('notifications', '0007_notificationtype_resource_button_label'),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.AddField(
14
+ model_name='notificationtype',
15
+ name='is_lock',
16
+ field=models.BooleanField(default=False),
17
+ ),
18
+ ]
@@ -0,0 +1,32 @@
1
+ # Generated by Django 5.0.14 on 2025-05-21 11:05
2
+
3
+ from django.conf import settings
4
+ from django.db import migrations, models
5
+ from django.db.models import Count
6
+
7
+
8
+ def handle_duplicates(apps, schema_editor):
9
+ NotificationTypeSetting = apps.get_model("notifications", "NotificationTypeSetting")
10
+ for row in NotificationTypeSetting.objects.values('user', 'notification_type').annotate(c=Count("*")).filter(c__gt=1):
11
+ duplicates = NotificationTypeSetting.objects.filter(user=row['user'], notification_type=row['notification_type'])
12
+ for duplicate in duplicates[1:]:
13
+ duplicate.delete()
14
+
15
+ class Migration(migrations.Migration):
16
+
17
+ dependencies = [
18
+ ('notifications', '0008_notificationtype_is_lock'),
19
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
20
+ ]
21
+
22
+ operations = [
23
+ migrations.RunPython(handle_duplicates),
24
+ migrations.AlterModelOptions(
25
+ name='notificationtypesetting',
26
+ options={'verbose_name': 'Notification Type Setting', 'verbose_name_plural': 'Notification Type Settings'},
27
+ ),
28
+ migrations.AddConstraint(
29
+ model_name='notificationtypesetting',
30
+ constraint=models.UniqueConstraint(fields=('user', 'notification_type'), name='unique_notification_setting'),
31
+ ),
32
+ ]
@@ -2,6 +2,7 @@ from contextlib import suppress
2
2
 
3
3
  from django.contrib.auth import get_user_model
4
4
  from django.db import models
5
+ from django.db.models import UniqueConstraint
5
6
  from django.db.models.signals import post_save
6
7
  from django.dispatch import receiver
7
8
  from guardian.utils import get_anonymous_user
@@ -23,6 +24,7 @@ class NotificationType(WBModel):
23
24
  default_enable_web = models.BooleanField(default=False)
24
25
  default_enable_mobile = models.BooleanField(default=False)
25
26
  default_enable_email = models.BooleanField(default=False)
27
+ is_lock = models.BooleanField(default=False)
26
28
 
27
29
  def __str__(self) -> str:
28
30
  return f"{self.title}"
@@ -71,6 +73,24 @@ class NotificationTypeSetting(WBModel):
71
73
  def __str__(self) -> str:
72
74
  return f"{self.user}: {self.notification_type} ({self.enable_web}/{self.enable_mobile}/{self.enable_email})"
73
75
 
76
+ class Meta:
77
+ verbose_name = "Notification Type Setting"
78
+ verbose_name_plural = "Notification Type Settings"
79
+ constraints = [
80
+ UniqueConstraint(
81
+ name="unique_notification_setting",
82
+ fields=["user", "notification_type"],
83
+ )
84
+ ]
85
+
86
+ def save(self, *args, **kwargs):
87
+ if self.notification_type.is_lock:
88
+ self.enable_web = self.notification_type.default_enable_web
89
+ self.enable_mobile = self.notification_type.default_enable_mobile
90
+ self.enable_email = self.notification_type.default_enable_email
91
+
92
+ super().save(*args, **kwargs)
93
+
74
94
  @classmethod
75
95
  def get_endpoint_basename(cls):
76
96
  return "wbcore:notifications:notification_type_setting"
@@ -91,32 +111,55 @@ class NotificationTypeSetting(WBModel):
91
111
  @receiver(post_save, sender="notifications.NotificationType")
92
112
  def post_save_notification_type(instance, **kwargs):
93
113
  anonymous_user = get_anonymous_user()
114
+
115
+ objs = []
94
116
  for user in get_user_model().objects.filter(~models.Q(pk=anonymous_user.pk)):
95
- NotificationTypeSetting.objects.get_or_create(
96
- notification_type=instance,
97
- user=user,
98
- defaults={
99
- "enable_web": instance.default_enable_web,
100
- "enable_mobile": instance.default_enable_mobile,
101
- "enable_email": instance.default_enable_email,
102
- },
117
+ try:
118
+ existing_setting = NotificationTypeSetting.objects.get(user=user, notification_type=instance)
119
+ enable_web = existing_setting.enable_web if not instance.is_lock else instance.default_enable_web
120
+ enable_mobile = existing_setting.enable_mobile if not instance.is_lock else instance.default_enable_mobile
121
+ enable_email = existing_setting.enable_email if not instance.is_lock else instance.default_enable_email
122
+ except NotificationTypeSetting.DoesNotExist:
123
+ enable_web = instance.default_enable_web
124
+ enable_mobile = instance.default_enable_mobile
125
+ enable_email = instance.default_enable_email
126
+
127
+ objs.append(
128
+ NotificationTypeSetting(
129
+ notification_type=instance,
130
+ user=user,
131
+ enable_web=enable_web,
132
+ enable_mobile=enable_mobile,
133
+ enable_email=enable_email,
134
+ )
135
+ )
136
+ if objs:
137
+ NotificationTypeSetting.objects.bulk_create(
138
+ objs,
139
+ unique_fields=["notification_type", "user"],
140
+ update_fields=["enable_web", "enable_mobile", "enable_email"],
141
+ update_conflicts=True,
103
142
  )
104
143
 
105
144
 
106
145
  @receiver(post_save, sender=get_user_model())
107
- def post_save_user(instance, **kwargs):
108
- with suppress(get_user_model().DoesNotExist):
109
- anonymous_user = get_anonymous_user()
110
- if instance.pk == anonymous_user.pk:
111
- return
112
-
113
- for notification_type in NotificationType.objects.all():
114
- NotificationTypeSetting.objects.get_or_create(
115
- notification_type=notification_type,
116
- user=instance,
117
- defaults={
118
- "enable_web": notification_type.default_enable_web,
119
- "enable_mobile": notification_type.default_enable_mobile,
120
- "enable_email": notification_type.default_enable_email,
121
- },
122
- )
146
+ def post_save_user(sender, instance, created, raw, **kwargs):
147
+ if created:
148
+ with suppress(get_user_model().DoesNotExist):
149
+ anonymous_user = get_anonymous_user()
150
+ if instance.pk == anonymous_user.pk:
151
+ return
152
+
153
+ objs = []
154
+ for notification_type in NotificationType.objects.all():
155
+ objs.append(
156
+ NotificationTypeSetting(
157
+ notification_type=notification_type,
158
+ user=instance,
159
+ enable_web=notification_type.default_enable_web,
160
+ enable_mobile=notification_type.default_enable_mobile,
161
+ enable_email=notification_type.default_enable_email,
162
+ )
163
+ )
164
+ if objs:
165
+ NotificationTypeSetting.objects.bulk_create(objs, unique_fields=["notification_type", "user"])
@@ -1,3 +1,5 @@
1
+ from rest_framework.reverse import reverse
2
+
1
3
  from wbcore import serializers
2
4
  from wbcore.contrib.notifications.models import (
3
5
  NotificationType,
@@ -19,9 +21,19 @@ class NotificationTypeRepresentationSerializer(serializers.RepresentationSeriali
19
21
  class NotificationTypeSettingModelSerializer(serializers.ModelSerializer):
20
22
  _notification_type = NotificationTypeRepresentationSerializer(source="notification_type")
21
23
  help_text = serializers.CharField()
24
+ locked_icon = serializers.IconSelectField(read_only=True)
25
+ locked = serializers.BooleanField(read_only=True)
26
+ _update_url = serializers.SerializerMethodField()
27
+
28
+ def get__update_url(self, obj):
29
+ if not obj.notification_type.is_lock:
30
+ return reverse(
31
+ "wbcore:notifications:notification_type_setting-list", args=[], request=self.context["request"]
32
+ )
33
+ return None
22
34
 
23
35
  class Meta:
24
- read_only_fields = ("user", "notification_type")
36
+ read_only_fields = ("user", "notification_type", "help_text")
25
37
  model = NotificationTypeSetting
26
38
  fields = (
27
39
  "id",
@@ -32,5 +44,8 @@ class NotificationTypeSettingModelSerializer(serializers.ModelSerializer):
32
44
  "enable_web",
33
45
  "enable_mobile",
34
46
  "enable_email",
47
+ "locked",
48
+ "locked_icon",
35
49
  "_additional_resources",
50
+ "_update_url",
36
51
  )
@@ -19,4 +19,12 @@ class TestNotificationUserToken:
19
19
  setting = NotificationTypeSettingModelFactory(
20
20
  user=notification_user_token.user, enable_web=True, enable_mobile=True
21
21
  )
22
+ assert setting.enable_web is False
23
+ assert setting.enable_mobile is False
24
+ assert not NotificationUserToken.objects.filter_for_user_settings(setting).exists()
25
+
26
+ setting.enable_web = True
27
+ setting.enable_mobile = True
28
+ setting.save()
29
+
22
30
  assert NotificationUserToken.objects.filter_for_user_settings(setting).first() == notification_user_token # type: ignore
@@ -1,4 +1,5 @@
1
1
  import pytest
2
+ from rest_framework.reverse import reverse
2
3
 
3
4
  from wbcore.contrib.notifications.serializers import (
4
5
  NotificationTypeRepresentationSerializer,
@@ -40,6 +41,10 @@ class TestNotificationTypeSettingModelSerializer:
40
41
  "enable_mobile": notification_type_setting.enable_mobile,
41
42
  "enable_email": notification_type_setting.enable_email,
42
43
  "_additional_resources": {},
44
+ "_update_url": reverse(
45
+ "wbcore:notifications:notification_type_setting-list", args=[], request=request_with_user
46
+ ),
47
+ "locked_icon": None,
43
48
  }
44
49
 
45
50
  def test_deserialize_partial_instance(self, notification_type_setting, request_with_user):
@@ -1,7 +1,7 @@
1
1
  import pytest
2
2
  from rest_framework.reverse import reverse
3
3
 
4
- from wbcore.contrib.authentication.factories import UserFactory
4
+ from wbcore.contrib.authentication.factories import InternalUserFactory, UserFactory
5
5
  from wbcore.contrib.notifications.factories.notification_types import (
6
6
  NotificationTypeModelFactory,
7
7
  )
@@ -110,11 +110,9 @@ class TestNotificationTypeSettingModelViewSet:
110
110
  )
111
111
  def test_change_setting_from_other_user(self, notification_type_setting, client, user, status_code):
112
112
  """Regardless of permission, no user should ever be able to change a setting from a different user"""
113
- user2 = UserFactory()
114
- notification_type_setting.user = user2
115
- notification_type_setting.save()
113
+ other_user = InternalUserFactory(is_superuser=True)
116
114
 
117
- client.force_authenticate(user)
115
+ client.force_authenticate(other_user)
118
116
  response = client.patch(
119
117
  reverse("wbcore:notifications:notification_type_setting-detail", args=[notification_type_setting.id]),
120
118
  {"enable_web": True},
@@ -22,5 +22,6 @@ def create_notification_type(
22
22
  mobile: bool = True,
23
23
  email: bool = False,
24
24
  resource_button_label: str = "",
25
- ) -> tuple[str, str, str, bool, bool, bool, str]:
26
- return (code, title, help_text, web, mobile, email, resource_button_label)
25
+ is_lock: bool = False, # set to true if user cannot modified the preference for this notification type
26
+ ) -> tuple[str, str, str, bool, bool, bool, str, bool]:
27
+ return (code, title, help_text, web, mobile, email, resource_button_label, is_lock)
@@ -1,20 +1,34 @@
1
- from wbcore.metadata.configs.display import Field, ListDisplay
1
+ from wbcore.contrib.icons import WBIcon
2
+ from wbcore.enums import Unit
3
+ from wbcore.metadata.configs.display import Field, Legend, LegendItem, ListDisplay
2
4
  from wbcore.metadata.configs.display.instance_display import Display
3
5
  from wbcore.metadata.configs.display.instance_display.shortcuts import (
4
6
  create_simple_display,
5
7
  )
6
8
  from wbcore.metadata.configs.display.view_config import DisplayViewConfig
9
+ from wbcore.metadata.configs.endpoints import EndpointViewConfig
7
10
 
8
11
 
9
12
  class NotificationTypeSettingDisplayConfig(DisplayViewConfig):
10
13
  def get_list_display(self) -> ListDisplay:
11
14
  return ListDisplay(
12
15
  fields=[
13
- Field(key="notification_type", label="Notification"),
14
- Field(key="help_text", label="Help Text"),
15
- Field(key="enable_web", label="Web"),
16
- Field(key="enable_mobile", label="Mobile"),
17
- Field(key="enable_email", label="E-Mail"),
16
+ Field(key="locked_icon", label=" ", width=Unit.PIXEL(40)),
17
+ Field(key="notification_type", label="Notification", width=Unit.PIXEL(250)),
18
+ Field(key="help_text", label="Help Text", width=Unit.PIXEL(500)),
19
+ Field(key="enable_web", label="Web", width=Unit.PIXEL(100)),
20
+ Field(key="enable_mobile", label="Mobile", width=Unit.PIXEL(100)),
21
+ Field(key="enable_email", label="E-Mail", width=Unit.PIXEL(100)),
22
+ ],
23
+ legends=[
24
+ Legend(
25
+ items=[
26
+ LegendItem(
27
+ icon=WBIcon.LOCK.icon,
28
+ label="Locked",
29
+ ),
30
+ ],
31
+ )
18
32
  ],
19
33
  )
20
34
 
@@ -25,3 +39,11 @@ class NotificationTypeSettingDisplayConfig(DisplayViewConfig):
25
39
  ["enable_web", "enable_mobile", "enable_email"],
26
40
  ]
27
41
  )
42
+
43
+
44
+ class NotificationTypeSettingEndpointConfig(EndpointViewConfig):
45
+ def get_endpoint(self, **kwargs):
46
+ return None
47
+
48
+ def get_update_endpoint(self):
49
+ return "{{_update_url}}"
@@ -1,4 +1,4 @@
1
- from django.utils.translation import gettext as _
1
+ from django.utils.translation import gettext_lazy as _
2
2
 
3
3
  from wbcore.menus import ItemPermission, MenuItem
4
4