django-whiteneuron 0.4.0.0__tar.gz → 0.4.0.2__tar.gz

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 (231) hide show
  1. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/PKG-INFO +20 -5
  2. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/README.md +18 -3
  3. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/django_whiteneuron.egg-info/PKG-INFO +20 -5
  4. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/django_whiteneuron.egg-info/requires.txt +1 -1
  5. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/pyproject.toml +2 -2
  6. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/base/admin.py +13 -8
  7. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/base/models.py +16 -11
  8. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/base/tests_regressions.py +92 -0
  9. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/base/views.py +12 -4
  10. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/dashboard/views.py +16 -5
  11. django_whiteneuron-0.4.0.2/whiteneuron/locale/vi/LC_MESSAGES/django.mo +0 -0
  12. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/locale/vi/LC_MESSAGES/django.po +319 -286
  13. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/static/base/css/styles.css +1 -1
  14. django_whiteneuron-0.4.0.0/whiteneuron/locale/vi/LC_MESSAGES/django.mo +0 -0
  15. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/LICENSE +0 -0
  16. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/django_whiteneuron.egg-info/SOURCES.txt +0 -0
  17. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/django_whiteneuron.egg-info/dependency_links.txt +0 -0
  18. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/django_whiteneuron.egg-info/top_level.txt +0 -0
  19. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/setup.cfg +0 -0
  20. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/__init__.py +0 -0
  21. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/base/__init__.py +0 -0
  22. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/base/apps.py +0 -0
  23. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/base/asgi.py +0 -0
  24. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/base/ckeditor.py +0 -0
  25. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/base/filters.py +0 -0
  26. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/base/forms.py +0 -0
  27. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/base/html_sanitize.py +0 -0
  28. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/base/management/__init__.py +0 -0
  29. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/base/management/commands/__init__.py +0 -0
  30. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/base/management/commands/cleanup_old_activities.py +0 -0
  31. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/base/management/commands/init_admin.py +0 -0
  32. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/base/management/commands/init_groupuser.py +0 -0
  33. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/base/management/commands/init_guest.py +0 -0
  34. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/base/management/commands/update_visit_profiles.py +0 -0
  35. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/base/middleware.py +0 -0
  36. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/base/migrations/0001_initial.py +0 -0
  37. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/base/migrations/0002_imagemodel_userprofile_user_avatar_user_biography_and_more.py +0 -0
  38. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/base/migrations/0003_notification_obj_link.py +0 -0
  39. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/base/migrations/0004_delete_notification.py +0 -0
  40. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/base/migrations/0005_user_show_softdelete.py +0 -0
  41. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/base/migrations/0006_user_is_bot.py +0 -0
  42. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/base/migrations/0007_app.py +0 -0
  43. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/base/migrations/0008_app_created_at_app_created_by_app_deleted_at_and_more.py +0 -0
  44. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/base/migrations/0009_alter_app_table.py +0 -0
  45. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/base/migrations/0010_app_category.py +0 -0
  46. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/base/migrations/0011_alter_app_options.py +0 -0
  47. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/base/migrations/0012_app_permission.py +0 -0
  48. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/base/migrations/0013_app_thumbnail_url.py +0 -0
  49. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/base/migrations/0014_alter_app_created_at_alter_app_updated_at_and_more.py +0 -0
  50. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/base/migrations/0015_ipblacklist.py +0 -0
  51. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/base/migrations/0016_uablacklist.py +0 -0
  52. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/base/migrations/0017_alter_uablacklist_pattern.py +0 -0
  53. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/base/migrations/0018_user_uuid.py +0 -0
  54. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/base/migrations/0019_alter_user_uuid.py +0 -0
  55. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/base/migrations/0020_add_visit_profile_and_fk_fields.py +0 -0
  56. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/base/migrations/0021_populate_visit_profiles.py +0 -0
  57. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/base/migrations/0022_remove_duplicate_visit_profiles.py +0 -0
  58. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/base/migrations/0023_remove_useractivity_ip_address_and_more.py +0 -0
  59. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/base/migrations/0024_alter_visitprofile_user_agent_to_charfield.py +0 -0
  60. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/base/migrations/0025_alter_anonymousactivity_options_and_more.py +0 -0
  61. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/base/migrations/0026_alter_anonymousactivity_profile_and_more.py +0 -0
  62. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/base/migrations/0027_alter_anonymousactivity_method_and_more.py +0 -0
  63. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/base/migrations/0028_perf_indexes.py +0 -0
  64. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/base/migrations/__init__.py +0 -0
  65. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/base/modeladmin.py +0 -0
  66. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/base/queue.py +0 -0
  67. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/base/resources.py +0 -0
  68. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/base/settings.py +0 -0
  69. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/base/sites.py +0 -0
  70. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/base/tasks.py +0 -0
  71. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/base/templatetags/__init__.py +0 -0
  72. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/base/templatetags/unfold_list_custom.py +0 -0
  73. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/base/templatetags/wn_filters.py +0 -0
  74. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/base/templatetags/wn_theme.py +0 -0
  75. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/base/testing.py +0 -0
  76. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/base/tests.py +0 -0
  77. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/base/themes.py +0 -0
  78. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/base/thread_local.py +0 -0
  79. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/base/urls.py +0 -0
  80. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/base/urls_testing.py +0 -0
  81. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/base/utils.py +0 -0
  82. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/base/widgets.py +0 -0
  83. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/contrib/__init__.py +0 -0
  84. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/contrib/wn_import_export/__init__.py +0 -0
  85. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/contrib/wn_import_export/admin.py +0 -0
  86. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/contrib/wn_import_export/constants.py +0 -0
  87. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/contrib/wn_import_export/mixins.py +0 -0
  88. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/contrib/wn_import_export/tasks.py +0 -0
  89. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/contrib/wn_import_export/utils.py +0 -0
  90. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/contrib/wn_modeltranslation/__init__.py +0 -0
  91. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/contrib/wn_modeltranslation/admin.py +0 -0
  92. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/contrib/wn_modeltranslation/utils.py +0 -0
  93. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/dashboard/__init__.py +0 -0
  94. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/dashboard/admin.py +0 -0
  95. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/dashboard/apps.py +0 -0
  96. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/dashboard/migrations/__init__.py +0 -0
  97. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/dashboard/models.py +0 -0
  98. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/dashboard/tests.py +0 -0
  99. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/feedbacks/__init__.py +0 -0
  100. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/feedbacks/admin.py +0 -0
  101. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/feedbacks/apps.py +0 -0
  102. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/feedbacks/migrations/0001_initial.py +0 -0
  103. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/feedbacks/migrations/0002_feedbackdata_created_by_feedbackdata_deleted_at_and_more.py +0 -0
  104. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/feedbacks/migrations/0003_feedbackdata_note.py +0 -0
  105. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/feedbacks/migrations/0004_alter_feedbackdata_object_id.py +0 -0
  106. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/feedbacks/migrations/0005_feedbackdata_field.py +0 -0
  107. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/feedbacks/migrations/0006_alter_feedbackdata_created_at_and_more.py +0 -0
  108. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/feedbacks/migrations/__init__.py +0 -0
  109. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/feedbacks/models.py +0 -0
  110. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/feedbacks/tests.py +0 -0
  111. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/feedbacks/urls.py +0 -0
  112. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/feedbacks/utils.py +0 -0
  113. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/feedbacks/views.py +0 -0
  114. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/file_management/__init__.py +0 -0
  115. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/file_management/admin.py +0 -0
  116. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/file_management/apps.py +0 -0
  117. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/file_management/migrations/0001_initial.py +0 -0
  118. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/file_management/migrations/0002_alter_excelfile_options_alter_pdffile_options_and_more.py +0 -0
  119. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/file_management/migrations/0003_alter_excelfile_created_at_and_more.py +0 -0
  120. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/file_management/migrations/0004_excelfile_hash_pdffile_hash.py +0 -0
  121. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/file_management/migrations/0005_alter_excelfile_file_alter_excelfile_title_and_more.py +0 -0
  122. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/file_management/migrations/0006_excelfile_is_public_pdffile_is_public.py +0 -0
  123. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/file_management/migrations/0007_htmlfile.py +0 -0
  124. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/file_management/migrations/0008_excelfile_allowed_groups_excelfile_allowed_users_and_more.py +0 -0
  125. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/file_management/migrations/__init__.py +0 -0
  126. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/file_management/models.py +0 -0
  127. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/file_management/tests.py +0 -0
  128. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/file_management/urls.py +0 -0
  129. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/file_management/utils.py +0 -0
  130. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/file_management/views.py +0 -0
  131. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/manage.py +0 -0
  132. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/monitoring/__init__.py +0 -0
  133. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/monitoring/admin.py +0 -0
  134. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/monitoring/apps.py +0 -0
  135. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/monitoring/collector.py +0 -0
  136. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/monitoring/management/__init__.py +0 -0
  137. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/monitoring/management/commands/__init__.py +0 -0
  138. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/monitoring/management/commands/monitoring_collector.py +0 -0
  139. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/monitoring/migrations/0001_initial.py +0 -0
  140. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/monitoring/migrations/__init__.py +0 -0
  141. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/monitoring/models.py +0 -0
  142. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/monitoring/permissions.py +0 -0
  143. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/monitoring/stats.py +0 -0
  144. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/monitoring/tasks.py +0 -0
  145. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/monitoring/tests.py +0 -0
  146. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/notification/__init__.py +0 -0
  147. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/notification/admin.py +0 -0
  148. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/notification/apps.py +0 -0
  149. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/notification/consumers.py +0 -0
  150. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/notification/management/__init__.py +0 -0
  151. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/notification/management/commands/__init__.py +0 -0
  152. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/notification/management/commands/call_demo_noti.py +0 -0
  153. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/notification/migrations/0001_initial.py +0 -0
  154. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/notification/migrations/0002_alter_notificationconfig_event_type.py +0 -0
  155. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/notification/migrations/0003_notification.py +0 -0
  156. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/notification/migrations/0004_alter_notification_flag.py +0 -0
  157. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/notification/migrations/0005_notification_keywords.py +0 -0
  158. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/notification/migrations/0006_remove_notification_keywords.py +0 -0
  159. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/notification/migrations/0007_notification_changed_data.py +0 -0
  160. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/notification/migrations/0008_alter_notification_changed_data.py +0 -0
  161. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/notification/migrations/0009_notification_action_by_alter_notification_user.py +0 -0
  162. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/notification/migrations/0010_alter_notification_user.py +0 -0
  163. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/notification/migrations/0011_alter_notification_user.py +0 -0
  164. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/notification/migrations/0012_alter_notification_action_and_more.py +0 -0
  165. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/notification/migrations/0013_perf_indexes.py +0 -0
  166. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/notification/migrations/__init__.py +0 -0
  167. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/notification/models.py +0 -0
  168. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/notification/routing.py +0 -0
  169. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/notification/tests.py +0 -0
  170. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/notification/utils.py +0 -0
  171. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/notification/views.py +0 -0
  172. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/static/base/css/btn-styles.css +0 -0
  173. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/static/base/css/ckeditor5.css +0 -0
  174. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/static/base/css/loading.css +0 -0
  175. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/static/base/forms/js/mdeditor.js +0 -0
  176. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/static/base/images/delete.svg +0 -0
  177. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/static/base/images/edit.svg +0 -0
  178. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/static/base/images/icon-viewlink.svg +0 -0
  179. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/static/base/images/loading.gif +0 -0
  180. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/static/base/images/login-bg.jpg +0 -0
  181. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/static/base/images/login_bg.jpg +0 -0
  182. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/static/base/images/logo/LOGO.jpg +0 -0
  183. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/static/base/images/logo/WhiteNeuron-128.png +0 -0
  184. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/static/base/images/logo/WhiteNeuron-32.png +0 -0
  185. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/static/base/images/logo/WhiteNeuron.png +0 -0
  186. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/static/base/js/chart.min.js +0 -0
  187. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/static/base/js/d3.v7.min.js +0 -0
  188. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/static/base/js/loading.js +0 -0
  189. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/static/base/js/whiteneuron.js +0 -0
  190. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/static/monitoring/css/dashboard.css +0 -0
  191. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/static/monitoring/js/dashboard.js +0 -0
  192. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/templates/400.html +0 -0
  193. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/templates/403.html +0 -0
  194. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/templates/404.html +0 -0
  195. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/templates/429.html +0 -0
  196. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/templates/500.html +0 -0
  197. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/templates/admin/auth/user/add_form.html +0 -0
  198. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/templates/admin/base/app_change_list.html +0 -0
  199. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/templates/admin/base/image_change_form.html +0 -0
  200. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/templates/admin/base/userprofile_change_form.html +0 -0
  201. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/templates/admin/base.html +0 -0
  202. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/templates/admin/change_list.html +0 -0
  203. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/templates/admin/feedbacks/change_form_feedback_list.html +0 -0
  204. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/templates/admin/feedbacks/feedback_change_form.html +0 -0
  205. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/templates/admin/grid_view_results.html +0 -0
  206. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/templates/admin/header.html +0 -0
  207. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/templates/admin/index.html +0 -0
  208. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/templates/admin/login.html +0 -0
  209. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/templates/admin/login_after.html +0 -0
  210. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/templates/admin/monitoring/dashboard.html +0 -0
  211. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/templates/admin/monitoring/stat.html +0 -0
  212. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/templates/admin/monitoring/timeseries.html +0 -0
  213. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/templates/admin/search_form.html +0 -0
  214. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/templates/admin/signature.html +0 -0
  215. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/templates/admin/submit_line.html +0 -0
  216. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/templates/admin/wn_change_form.html +0 -0
  217. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/templates/admin/wn_history_panel.html +0 -0
  218. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/templates/base/announcement.html +0 -0
  219. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/templates/base/forms/helpers/toolbar.html +0 -0
  220. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/templates/base/forms/mdeditor.html +0 -0
  221. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/templates/base/forms/wysiwyg.html +0 -0
  222. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/templates/base.html +0 -0
  223. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/templates/file_management/widgets/clearable_file_input_no_download.html +0 -0
  224. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/templates/unfold/helpers/account_links.html +0 -0
  225. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/templates/unfold/helpers/app_list_badge.html +0 -0
  226. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/templates/unfold/helpers/field_password.html +0 -0
  227. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/templates/unfold/helpers/language_switch.html +0 -0
  228. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/templates/unfold/helpers/pagination.html +0 -0
  229. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/templates/unfold/helpers/theme_switch.html +0 -0
  230. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/templates/unfold/helpers/userlinks.html +0 -0
  231. {django_whiteneuron-0.4.0.0 → django_whiteneuron-0.4.0.2}/whiteneuron/templates/unfold/helpers/wn_page_button.html +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: django-whiteneuron
3
- Version: 0.4.0.0
3
+ Version: 0.4.0.2
4
4
  Summary: A modern Django Admin extension with enhanced UI/UX, dashboard, feedback, file management, rate limiting, IP blacklisting, and advanced admin integrations built on django-unfold.
5
5
  Author-email: White Neuron <anhnt@whiteneuron.ai>
6
6
  License: MIT License
@@ -43,7 +43,7 @@ Requires-Python: >=3.13
43
43
  Description-Content-Type: text/markdown
44
44
  License-File: LICENSE
45
45
  Requires-Dist: django-debug-toolbar>=5.0.1
46
- Requires-Dist: django<7.0.0,>=6.0.8
46
+ Requires-Dist: django<7.0.0,>=6.0.9
47
47
  Requires-Dist: django-celery-beat>=2.7.0
48
48
  Requires-Dist: django-guardian>=2.4.0
49
49
  Requires-Dist: django-imagekit>=5.0.0
@@ -96,11 +96,26 @@ A modern Django Admin extension focused on UI/UX, dashboard, feedback, file mana
96
96
  ![django-whiteneuron](https://raw.githubusercontent.com/White-Neuron/django-whiteneuron/main2.0/docs/images/main.png)
97
97
 
98
98
  ## Current Version
99
- v0.4.0.0
99
+ v0.4.0.2
100
100
 
101
101
  ## Changelog
102
102
 
103
- ### Latest: v0.4.0.0 (2026-10-13)
103
+ ### Latest: v0.4.0.2 (2026-10-15)
104
+ **Patch release: dependency security updates in the locked set (and the Docker base images)**
105
+ - **Security (dependencies)**: locked django 6.0.9, gunicorn 26.2.0, cryptography 50.0.2, hiredis 3.4.2, msgpack 1.2.3, Markdown 3.11.1 and ujson 6.0.0 (all patched releases). The Docker base images install exactly this lock, so projects building `FROM django-whiteneuron-*:v0.4.0.2` no longer need to pin these packages above the base. `django>=6.0.9` is now the minimum.
106
+ - **Upgrade**: `pip install -U django-whiteneuron==0.4.0.2`. No code, template or migration changes. Docker base images `v0.4.0.2` are published by the release workflow.
107
+
108
+ ### v0.4.0.1 (2026-10-14)
109
+ **Patch release: Vietnamese translations lost in 0.4.0.0, daisyUI modals under Unfold, announcement endpoint**
110
+ - **Fixed**: translations that 0.4.0.0 dropped from the Vietnamese catalog. Strings translated with `_()` *inside f-strings* are invisible to `makemessages`, so regenerating the catalog for 0.4.0.0 marked them obsolete and left them out of `django.mo`, so 0.4.0.0 showed them in English: user badges (Inactive / Admin / Bot), every "… has been updated by user …" notification, M2M change notes (added / removed) and the dashboard KPI cards ("progress from last", "in"). The `_()` calls are moved out of the f-strings and the translations restored.
111
+ - **Fixed**: dashboard KPI cards are whole translatable sentences (`%(object)s in %(period)s`, `%(count)s successful (%(rate)s%%)`); "day" and "successful" were hard-coded English; no change now shows as a green +0 instead of a red -0; the color classes are literal so Tailwind compiles them.
112
+ - **Fixed**: notification bodies ("… has been updated by user … with the following changes") were hard-coded English.
113
+ - **Fixed**: an open daisyUI `ui-modal` (announcement, feedback resolve, markdown preview) rendered as a narrow white column without overlay: Unfold's unlayered `dialog { … }` rule beats daisyUI's layered `.ui-modal`. `dialog.ui-modal` now restores the full-screen modal.
114
+ - **Fixed**: `/announcement/` returned a 500 when the project-provided `ANNOUNCEMENT_CONTENT_HTML_FILE` template is missing or the setting is empty; it now returns empty content and logs a warning.
115
+ - **Tests**: no `_()` inside f-strings; every literal msgid in the package's Python code must be in the compiled Vietnamese catalog (catches strings dropped by `makemessages`); dashboard KPI translated; `dialog.ui-modal` override present; announcement endpoint without a template.
116
+ - **Upgrade**: `pip install -U django-whiteneuron==0.4.0.1`, `collectstatic`. No migrations. Docker base images `v0.4.0.1` are published by the release workflow.
117
+
118
+ ### v0.4.0.0 (2026-10-13)
104
119
  **Feature release: daisyUI multi-theme admin, server monitoring dashboard, django-unfold 0.109, security hardening and performance work**
105
120
  - **Added**: 35 daisyUI themes selectable from the user menu (**Themes**); each theme also re-colors Unfold and drives its dark mode. `WN_ADMIN_THEMES` limits the picker (`[]` hides it); `WN_THEMES="nord,dracula" bash scripts/tailwind.sh` builds a smaller CSS bundle.
106
121
  - **Added**: `whiteneuron.monitoring` — Grafana-style **Server monitoring** dashboard (CPU, memory, disk, load, network, disk I/O, request throughput, p50/p95/p99 latency, error rate, Apdex, slowest endpoints, DB/cache latency) with time ranges, auto-refresh, host selector and shared crosshair. Collect with `manage.py monitoring_collector` or the Celery task `whiteneuron.monitoring.tasks.collect_metrics`; configure with `WN_MONITORING`. New dependency: `psutil`.
@@ -529,7 +544,7 @@ Release is published.
529
544
  ```bash
530
545
  bash docker/build-base.sh # HEAD, load into local docker
531
546
  GIT_REF=v0.3.8.0 bash docker/build-base.sh # a tag / branch / commit
532
- GIT_REF=v0.4.0.0 bash docker/push-base.sh # push a release, with provenance and SBOM
547
+ GIT_REF=v0.4.0.2 bash docker/push-base.sh # push a release, with provenance and SBOM
533
548
  VARIANTS=alpine IMAGE_NAMESPACE=myorg WN_PYTHON_VERSION=3.13 bash docker/build-base.sh
534
549
  ```
535
550
 
@@ -10,11 +10,26 @@ A modern Django Admin extension focused on UI/UX, dashboard, feedback, file mana
10
10
  ![django-whiteneuron](https://raw.githubusercontent.com/White-Neuron/django-whiteneuron/main2.0/docs/images/main.png)
11
11
 
12
12
  ## Current Version
13
- v0.4.0.0
13
+ v0.4.0.2
14
14
 
15
15
  ## Changelog
16
16
 
17
- ### Latest: v0.4.0.0 (2026-10-13)
17
+ ### Latest: v0.4.0.2 (2026-10-15)
18
+ **Patch release: dependency security updates in the locked set (and the Docker base images)**
19
+ - **Security (dependencies)**: locked django 6.0.9, gunicorn 26.2.0, cryptography 50.0.2, hiredis 3.4.2, msgpack 1.2.3, Markdown 3.11.1 and ujson 6.0.0 (all patched releases). The Docker base images install exactly this lock, so projects building `FROM django-whiteneuron-*:v0.4.0.2` no longer need to pin these packages above the base. `django>=6.0.9` is now the minimum.
20
+ - **Upgrade**: `pip install -U django-whiteneuron==0.4.0.2`. No code, template or migration changes. Docker base images `v0.4.0.2` are published by the release workflow.
21
+
22
+ ### v0.4.0.1 (2026-10-14)
23
+ **Patch release: Vietnamese translations lost in 0.4.0.0, daisyUI modals under Unfold, announcement endpoint**
24
+ - **Fixed**: translations that 0.4.0.0 dropped from the Vietnamese catalog. Strings translated with `_()` *inside f-strings* are invisible to `makemessages`, so regenerating the catalog for 0.4.0.0 marked them obsolete and left them out of `django.mo`, so 0.4.0.0 showed them in English: user badges (Inactive / Admin / Bot), every "… has been updated by user …" notification, M2M change notes (added / removed) and the dashboard KPI cards ("progress from last", "in"). The `_()` calls are moved out of the f-strings and the translations restored.
25
+ - **Fixed**: dashboard KPI cards are whole translatable sentences (`%(object)s in %(period)s`, `%(count)s successful (%(rate)s%%)`); "day" and "successful" were hard-coded English; no change now shows as a green +0 instead of a red -0; the color classes are literal so Tailwind compiles them.
26
+ - **Fixed**: notification bodies ("… has been updated by user … with the following changes") were hard-coded English.
27
+ - **Fixed**: an open daisyUI `ui-modal` (announcement, feedback resolve, markdown preview) rendered as a narrow white column without overlay: Unfold's unlayered `dialog { … }` rule beats daisyUI's layered `.ui-modal`. `dialog.ui-modal` now restores the full-screen modal.
28
+ - **Fixed**: `/announcement/` returned a 500 when the project-provided `ANNOUNCEMENT_CONTENT_HTML_FILE` template is missing or the setting is empty; it now returns empty content and logs a warning.
29
+ - **Tests**: no `_()` inside f-strings; every literal msgid in the package's Python code must be in the compiled Vietnamese catalog (catches strings dropped by `makemessages`); dashboard KPI translated; `dialog.ui-modal` override present; announcement endpoint without a template.
30
+ - **Upgrade**: `pip install -U django-whiteneuron==0.4.0.1`, `collectstatic`. No migrations. Docker base images `v0.4.0.1` are published by the release workflow.
31
+
32
+ ### v0.4.0.0 (2026-10-13)
18
33
  **Feature release: daisyUI multi-theme admin, server monitoring dashboard, django-unfold 0.109, security hardening and performance work**
19
34
  - **Added**: 35 daisyUI themes selectable from the user menu (**Themes**); each theme also re-colors Unfold and drives its dark mode. `WN_ADMIN_THEMES` limits the picker (`[]` hides it); `WN_THEMES="nord,dracula" bash scripts/tailwind.sh` builds a smaller CSS bundle.
20
35
  - **Added**: `whiteneuron.monitoring` — Grafana-style **Server monitoring** dashboard (CPU, memory, disk, load, network, disk I/O, request throughput, p50/p95/p99 latency, error rate, Apdex, slowest endpoints, DB/cache latency) with time ranges, auto-refresh, host selector and shared crosshair. Collect with `manage.py monitoring_collector` or the Celery task `whiteneuron.monitoring.tasks.collect_metrics`; configure with `WN_MONITORING`. New dependency: `psutil`.
@@ -443,7 +458,7 @@ Release is published.
443
458
  ```bash
444
459
  bash docker/build-base.sh # HEAD, load into local docker
445
460
  GIT_REF=v0.3.8.0 bash docker/build-base.sh # a tag / branch / commit
446
- GIT_REF=v0.4.0.0 bash docker/push-base.sh # push a release, with provenance and SBOM
461
+ GIT_REF=v0.4.0.2 bash docker/push-base.sh # push a release, with provenance and SBOM
447
462
  VARIANTS=alpine IMAGE_NAMESPACE=myorg WN_PYTHON_VERSION=3.13 bash docker/build-base.sh
448
463
  ```
449
464
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: django-whiteneuron
3
- Version: 0.4.0.0
3
+ Version: 0.4.0.2
4
4
  Summary: A modern Django Admin extension with enhanced UI/UX, dashboard, feedback, file management, rate limiting, IP blacklisting, and advanced admin integrations built on django-unfold.
5
5
  Author-email: White Neuron <anhnt@whiteneuron.ai>
6
6
  License: MIT License
@@ -43,7 +43,7 @@ Requires-Python: >=3.13
43
43
  Description-Content-Type: text/markdown
44
44
  License-File: LICENSE
45
45
  Requires-Dist: django-debug-toolbar>=5.0.1
46
- Requires-Dist: django<7.0.0,>=6.0.8
46
+ Requires-Dist: django<7.0.0,>=6.0.9
47
47
  Requires-Dist: django-celery-beat>=2.7.0
48
48
  Requires-Dist: django-guardian>=2.4.0
49
49
  Requires-Dist: django-imagekit>=5.0.0
@@ -96,11 +96,26 @@ A modern Django Admin extension focused on UI/UX, dashboard, feedback, file mana
96
96
  ![django-whiteneuron](https://raw.githubusercontent.com/White-Neuron/django-whiteneuron/main2.0/docs/images/main.png)
97
97
 
98
98
  ## Current Version
99
- v0.4.0.0
99
+ v0.4.0.2
100
100
 
101
101
  ## Changelog
102
102
 
103
- ### Latest: v0.4.0.0 (2026-10-13)
103
+ ### Latest: v0.4.0.2 (2026-10-15)
104
+ **Patch release: dependency security updates in the locked set (and the Docker base images)**
105
+ - **Security (dependencies)**: locked django 6.0.9, gunicorn 26.2.0, cryptography 50.0.2, hiredis 3.4.2, msgpack 1.2.3, Markdown 3.11.1 and ujson 6.0.0 (all patched releases). The Docker base images install exactly this lock, so projects building `FROM django-whiteneuron-*:v0.4.0.2` no longer need to pin these packages above the base. `django>=6.0.9` is now the minimum.
106
+ - **Upgrade**: `pip install -U django-whiteneuron==0.4.0.2`. No code, template or migration changes. Docker base images `v0.4.0.2` are published by the release workflow.
107
+
108
+ ### v0.4.0.1 (2026-10-14)
109
+ **Patch release: Vietnamese translations lost in 0.4.0.0, daisyUI modals under Unfold, announcement endpoint**
110
+ - **Fixed**: translations that 0.4.0.0 dropped from the Vietnamese catalog. Strings translated with `_()` *inside f-strings* are invisible to `makemessages`, so regenerating the catalog for 0.4.0.0 marked them obsolete and left them out of `django.mo`, so 0.4.0.0 showed them in English: user badges (Inactive / Admin / Bot), every "… has been updated by user …" notification, M2M change notes (added / removed) and the dashboard KPI cards ("progress from last", "in"). The `_()` calls are moved out of the f-strings and the translations restored.
111
+ - **Fixed**: dashboard KPI cards are whole translatable sentences (`%(object)s in %(period)s`, `%(count)s successful (%(rate)s%%)`); "day" and "successful" were hard-coded English; no change now shows as a green +0 instead of a red -0; the color classes are literal so Tailwind compiles them.
112
+ - **Fixed**: notification bodies ("… has been updated by user … with the following changes") were hard-coded English.
113
+ - **Fixed**: an open daisyUI `ui-modal` (announcement, feedback resolve, markdown preview) rendered as a narrow white column without overlay: Unfold's unlayered `dialog { … }` rule beats daisyUI's layered `.ui-modal`. `dialog.ui-modal` now restores the full-screen modal.
114
+ - **Fixed**: `/announcement/` returned a 500 when the project-provided `ANNOUNCEMENT_CONTENT_HTML_FILE` template is missing or the setting is empty; it now returns empty content and logs a warning.
115
+ - **Tests**: no `_()` inside f-strings; every literal msgid in the package's Python code must be in the compiled Vietnamese catalog (catches strings dropped by `makemessages`); dashboard KPI translated; `dialog.ui-modal` override present; announcement endpoint without a template.
116
+ - **Upgrade**: `pip install -U django-whiteneuron==0.4.0.1`, `collectstatic`. No migrations. Docker base images `v0.4.0.1` are published by the release workflow.
117
+
118
+ ### v0.4.0.0 (2026-10-13)
104
119
  **Feature release: daisyUI multi-theme admin, server monitoring dashboard, django-unfold 0.109, security hardening and performance work**
105
120
  - **Added**: 35 daisyUI themes selectable from the user menu (**Themes**); each theme also re-colors Unfold and drives its dark mode. `WN_ADMIN_THEMES` limits the picker (`[]` hides it); `WN_THEMES="nord,dracula" bash scripts/tailwind.sh` builds a smaller CSS bundle.
106
121
  - **Added**: `whiteneuron.monitoring` — Grafana-style **Server monitoring** dashboard (CPU, memory, disk, load, network, disk I/O, request throughput, p50/p95/p99 latency, error rate, Apdex, slowest endpoints, DB/cache latency) with time ranges, auto-refresh, host selector and shared crosshair. Collect with `manage.py monitoring_collector` or the Celery task `whiteneuron.monitoring.tasks.collect_metrics`; configure with `WN_MONITORING`. New dependency: `psutil`.
@@ -529,7 +544,7 @@ Release is published.
529
544
  ```bash
530
545
  bash docker/build-base.sh # HEAD, load into local docker
531
546
  GIT_REF=v0.3.8.0 bash docker/build-base.sh # a tag / branch / commit
532
- GIT_REF=v0.4.0.0 bash docker/push-base.sh # push a release, with provenance and SBOM
547
+ GIT_REF=v0.4.0.2 bash docker/push-base.sh # push a release, with provenance and SBOM
533
548
  VARIANTS=alpine IMAGE_NAMESPACE=myorg WN_PYTHON_VERSION=3.13 bash docker/build-base.sh
534
549
  ```
535
550
 
@@ -1,5 +1,5 @@
1
1
  django-debug-toolbar>=5.0.1
2
- django<7.0.0,>=6.0.8
2
+ django<7.0.0,>=6.0.9
3
3
  django-celery-beat>=2.7.0
4
4
  django-guardian>=2.4.0
5
5
  django-imagekit>=5.0.0
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "django-whiteneuron"
3
- version = "0.4.0.0"
3
+ version = "0.4.0.2"
4
4
  description = "A modern Django Admin extension with enhanced UI/UX, dashboard, feedback, file management, rate limiting, IP blacklisting, and advanced admin integrations built on django-unfold."
5
5
  authors = [
6
6
  {name = "White Neuron", email = "anhnt@whiteneuron.ai"}
@@ -10,7 +10,7 @@ readme = "README.md"
10
10
  keywords = ["django", "plugin", "admin", "dashboard", "whiteneuron"]
11
11
  dependencies = [
12
12
  "django-debug-toolbar>=5.0.1",
13
- "django>=6.0.8,<7.0.0",
13
+ "django>=6.0.9,<7.0.0",
14
14
  "django-celery-beat>=2.7.0",
15
15
  "django-guardian>=2.4.0",
16
16
  "django-imagekit>=5.0.0",
@@ -184,18 +184,23 @@ class UserAdmin(BaseUserAdmin, ModelAdmin):
184
184
  </div>
185
185
  '''
186
186
 
187
+ # Labels are looked up outside the f-strings: makemessages can't read _() inside them
188
+ active_label, inactive_label = _("Active"), _("Inactive")
189
+ staff_label, admin_label = _("Staff"), _("Admin")
190
+ superuser_label, bot_label = _("Superuser"), _("Bot")
191
+
187
192
  # Status badges với dark mode support
188
193
  badges = []
189
194
  if obj.is_active:
190
- badges.append(f'<span class="ui-badge ui-badge-success ui-badge-xs gap-1"><span class="w-1.5 h-1.5 bg-success dark:bg-success-300 rounded-full animate-pulse"></span>{_("Active")}</span>')
195
+ badges.append(f'<span class="ui-badge ui-badge-success ui-badge-xs gap-1"><span class="w-1.5 h-1.5 bg-success dark:bg-success-300 rounded-full animate-pulse"></span>{active_label}</span>')
191
196
  else:
192
- badges.append(f'<span class="ui-badge ui-badge-error ui-badge-xs gap-1"><span class="w-1.5 h-1.5 bg-error dark:bg-error-300 rounded-full"></span>{_("Inactive")}</span>')
197
+ badges.append(f'<span class="ui-badge ui-badge-error ui-badge-xs gap-1"><span class="w-1.5 h-1.5 bg-error dark:bg-error-300 rounded-full"></span>{inactive_label}</span>')
193
198
 
194
199
  if obj.is_staff:
195
- badges.append(f'<span class="ui-badge ui-badge-info ui-badge-xs">{_("Staff")}</span>')
200
+ badges.append(f'<span class="ui-badge ui-badge-info ui-badge-xs">{staff_label}</span>')
196
201
 
197
202
  if obj.is_superuser:
198
- badges.append(f'<span class="ui-badge ui-badge-warning ui-badge-xs">{_("Admin")}</span>')
203
+ badges.append(f'<span class="ui-badge ui-badge-warning ui-badge-xs">{admin_label}</span>')
199
204
 
200
205
  badges_html = ' '.join(badges)
201
206
 
@@ -205,11 +210,11 @@ class UserAdmin(BaseUserAdmin, ModelAdmin):
205
210
 
206
211
  role= ""
207
212
  if obj.is_superuser:
208
- role= f'<span class="material-symbols-outlined text-warning" style="font-size:18px" title="{_("Superuser")}">admin_panel_settings</span>'
213
+ role= f'<span class="material-symbols-outlined text-warning" style="font-size:18px" title="{superuser_label}">admin_panel_settings</span>'
209
214
  elif obj.is_staff:
210
- role= f'<span class="material-symbols-outlined text-info" style="font-size:18px" title="{_("Staff")}">badge</span>'
215
+ role= f'<span class="material-symbols-outlined text-info" style="font-size:18px" title="{staff_label}">badge</span>'
211
216
  elif obj.is_bot:
212
- role= f'<span class="material-symbols-outlined text-secondary" style="font-size:18px" title="{_("Bot")}">smart_toy</span>'
217
+ role= f'<span class="material-symbols-outlined text-secondary" style="font-size:18px" title="{bot_label}">smart_toy</span>'
213
218
 
214
219
  # Compact vertical card layout với dark mode support
215
220
  string = f"""
@@ -229,7 +234,7 @@ class UserAdmin(BaseUserAdmin, ModelAdmin):
229
234
  <h5>{display_name}</h5>
230
235
  <p class="ui-card-title justify-center text-xs">{username_display}</p>
231
236
  <div class="flex flex-wrap items-center justify-center gap-2 mt-3">
232
- {f'<span class="material-symbols-outlined text-success" style="font-size:18px" title="{_("Active")}">check_circle</span>' if obj.is_active else f'<span class="material-symbols-outlined text-error" style="font-size:18px" title="{_("Inactive")}">cancel</span>'}
237
+ {f'<span class="material-symbols-outlined text-success" style="font-size:18px" title="{active_label}">check_circle</span>' if obj.is_active else f'<span class="material-symbols-outlined text-error" style="font-size:18px" title="{inactive_label}">cancel</span>'}
233
238
  {role}
234
239
  </div>
235
240
 
@@ -529,7 +529,8 @@ class BaseModel(SoftDeleteModel):
529
529
  verbose_name = self._meta.verbose_name
530
530
  obj_link = self.path()
531
531
  if action == 'create':
532
- title = f'{_("New")} {verbose_name} "{self}" {_("has been created by user")} "{self.created_by}"'
532
+ new_label, created_by_label = _("New"), _("has been created by user")
533
+ title = f'{new_label} {verbose_name} "{self}" {created_by_label} "{self.created_by}"'
533
534
  content_html = format_html(
534
535
  '<p>{} {} <strong>{} ({})</strong> {} <strong>{}</strong></p>',
535
536
  _('New'), verbose_name, self, self.pk, _('has been created by user'), self.created_by,
@@ -538,14 +539,15 @@ class BaseModel(SoftDeleteModel):
538
539
  content_html += format_html('<p>{} <a href="{}">{}</a>.</p>', _('You can view it at'), obj_link, _('this link'))
539
540
  changed_data = []
540
541
  else:
541
- title = f'{_("Update")} {verbose_name} "{self}" ({self.pk}) {_("has been updated by user")} "{self.updated_by}"'
542
+ update_label, updated_by_label = _("Update"), _("has been updated by user")
543
+ title = f'{update_label} {verbose_name} "{self}" ({self.pk}) {updated_by_label} "{self.updated_by}"'
542
544
  changed_data = [
543
545
  {'field_name': name, 'old_value': old_value, 'new_value': new_value}
544
546
  for name, old_value, new_value in fields_changed
545
547
  ]
546
548
  content_html = format_html(
547
- '{} <strong>{}</strong> ({}) has been updated by user "{}" with the following changes: <ul>{}</ul>',
548
- verbose_name, self, self.pk, self.updated_by,
549
+ '{} <strong>{}</strong> ({}) {} "{}" {}: <ul>{}</ul>',
550
+ verbose_name, self, self.pk, updated_by_label, self.updated_by, _('with the following changes'),
549
551
  format_html_join('', '<li>{}: {} -> {}</li>', fields_changed),
550
552
  )
551
553
  Notification.notify_superusers(
@@ -621,12 +623,14 @@ def _base_model_m2m_changed(sender, instance, action, pk_set, model, **kwargs):
621
623
  added_set = set(pk_list)
622
624
  old_value = [lbl for lbl in current_labels if not any(f"({pk})" in lbl for pk in added_set)]
623
625
  new_value = current_labels
624
- desc = f"{_('added')}: {added_labels}"
626
+ added_label = _('added')
627
+ desc = f"{added_label}: {added_labels}"
625
628
  elif action == 'post_remove':
626
629
  removed_labels = _resolve_labels(pk_list)
627
630
  old_value = current_labels + removed_labels
628
631
  new_value = current_labels
629
- desc = f"{_('removed')}: {removed_labels}"
632
+ removed_label = _('removed')
633
+ desc = f"{removed_label}: {removed_labels}"
630
634
  else: # post_clear
631
635
  old_value = '(all)'
632
636
  new_value = []
@@ -634,14 +638,15 @@ def _base_model_m2m_changed(sender, instance, action, pk_set, model, **kwargs):
634
638
 
635
639
  changed_data = [{'field_name': field_name, 'old_value': old_value, 'new_value': new_value}]
636
640
 
641
+ update_label, updated_by_label = _("Update"), _("has been updated by user")
637
642
  title = (
638
- f'{_("Update")} {instance._meta.verbose_name} "{instance}" ({instance.pk}) '
639
- f'{_("has been updated by user")} "{user}"'
643
+ f'{update_label} {instance._meta.verbose_name} "{instance}" ({instance.pk}) '
644
+ f'{updated_by_label} "{user}"'
640
645
  )
641
646
  content_html = format_html(
642
- '{} <strong>{}</strong> ({}) has been updated by user "{}" with the following changes: '
643
- '<ul><li>{}: {}</li></ul>',
644
- instance._meta.verbose_name, instance, instance.pk, user, field_verbose, desc,
647
+ '{} <strong>{}</strong> ({}) {} "{}" {}: <ul><li>{}: {}</li></ul>',
648
+ instance._meta.verbose_name, instance, instance.pk, updated_by_label, user,
649
+ _('with the following changes'), field_verbose, desc,
645
650
  )
646
651
  try:
647
652
  Notification.notify_superusers(
@@ -6,6 +6,7 @@ from django.contrib.admin.sites import AdminSite
6
6
  from django.contrib.auth import get_user_model
7
7
  from django.test import RequestFactory, TestCase, override_settings
8
8
  from django.urls import reverse
9
+ from django.utils import translation
9
10
 
10
11
  from whiteneuron.base.middleware import get_client_ip
11
12
  from whiteneuron.base.testing import package_test_settings
@@ -369,3 +370,94 @@ class AdminTemplateRegressionTests(TestCase):
369
370
  self.assertEqual(page.context["cl"].page_num, 1)
370
371
  self.assertContains(page, 'aria-disabled="true">Previous', count=1)
371
372
  self.assertContains(page, 'p=2" class="ui-btn')
373
+
374
+
375
+ class I18nCatalogRegressionTests(TestCase):
376
+ """makemessages can't read _() inside f-strings: such strings silently dropped out of
377
+ the Vietnamese catalog in 0.4.0.0 (marked obsolete, so missing from django.mo)."""
378
+
379
+ GETTEXT_FUNCS = {"_", "gettext", "gettext_lazy", "pgettext", "pgettext_lazy", "ngettext", "ngettext_lazy"}
380
+
381
+ @staticmethod
382
+ def _python_files():
383
+ import pathlib
384
+ root = pathlib.Path(__file__).resolve().parents[1]
385
+ for path in root.rglob("*.py"):
386
+ if "migrations" in path.parts or path.name.startswith("tests"):
387
+ continue
388
+ yield root, path
389
+
390
+ def _func_name(self, call):
391
+ return getattr(call.func, "id", None) or getattr(call.func, "attr", None)
392
+
393
+ def test_no_gettext_inside_fstrings(self):
394
+ import ast
395
+ found = []
396
+ for root, path in self._python_files():
397
+ for node in ast.walk(ast.parse(path.read_text())):
398
+ if isinstance(node, ast.JoinedStr):
399
+ for call in ast.walk(node):
400
+ if isinstance(call, ast.Call) and self._func_name(call) in self.GETTEXT_FUNCS:
401
+ found.append(f"{path.relative_to(root)}:{call.lineno}")
402
+ self.assertEqual(found, [], "move these _() calls out of the f-strings so makemessages sees them")
403
+
404
+ def test_python_msgids_are_in_the_vietnamese_catalog(self):
405
+ import ast
406
+ import gettext
407
+ import pathlib
408
+
409
+ mo = pathlib.Path(__file__).resolve().parents[1] / "locale" / "vi" / "LC_MESSAGES" / "django.mo"
410
+ with mo.open("rb") as fh:
411
+ catalog = gettext.GNUTranslations(fh)._catalog
412
+ missing = {}
413
+ for root, path in self._python_files():
414
+ for call in ast.walk(ast.parse(path.read_text())):
415
+ if (isinstance(call, ast.Call) and self._func_name(call) in {"_", "gettext", "gettext_lazy"}
416
+ and call.args and isinstance(call.args[0], ast.Constant)
417
+ and isinstance(call.args[0].value, str) and call.args[0].value):
418
+ msgid = call.args[0].value
419
+ if msgid not in catalog:
420
+ with translation.override("vi"):
421
+ if str(translation.gettext(msgid)) == msgid: # not covered by Django/Unfold either
422
+ missing.setdefault(msgid, f"{path.relative_to(root)}:{call.lineno}")
423
+ self.assertEqual(missing, {}, "run scripts/makemessages.sh, translate, then scripts/compilemessages.sh")
424
+
425
+ def test_dashboard_kpi_is_fully_translated(self):
426
+ from whiteneuron.dashboard.views import dashboard_callback
427
+
428
+ request = RequestFactory().get("/admin/", {"time": "today"})
429
+ with translation.override("vi"):
430
+ kpi = dashboard_callback(request, {})["kpi"][0]
431
+ self.assertIn("trong", kpi["title"])
432
+ self.assertIn("tiến trình từ lần cuối", str(kpi["footer"]))
433
+ self.assertIn("ngày", str(kpi["footer"]))
434
+
435
+
436
+ class UiModalCssRegressionTests(TestCase):
437
+ def test_ui_modal_overrides_unfold_dialog_rule(self):
438
+ # Unfold's unlayered `dialog {...}` beat daisyUI's layered .ui-modal (narrow white
439
+ # column, no overlay); styles.css must carry the dialog.ui-modal override.
440
+ import pathlib
441
+
442
+ css = (pathlib.Path(__file__).resolve().parents[1] / "static" / "base" / "css" / "styles.css").read_text()
443
+ rule = css[css.index("dialog.ui-modal{"):]
444
+ rule = rule[: rule.index("}")]
445
+ for decl in ("position:fixed", "inset:0", "translate:none", "width:100%", "padding:0"):
446
+ self.assertIn(decl, rule)
447
+
448
+
449
+ class AnnouncementContentTests(TestCase):
450
+ def _get(self):
451
+ from whiteneuron.base.views import get_announcement_content
452
+ return get_announcement_content(RequestFactory().get("/announcement/"))
453
+
454
+ @override_settings(ANNOUNCEMENT_CONTENT_HTML_FILE="does-not-exist/announcement.html")
455
+ def test_missing_template_returns_empty_content(self):
456
+ with self.assertLogs("whiteneuron.base.views", level="WARNING"):
457
+ response = self._get()
458
+ self.assertEqual((response.status_code, response.content), (200, b""))
459
+
460
+ @override_settings(ANNOUNCEMENT_CONTENT_HTML_FILE=None)
461
+ def test_unset_setting_returns_empty_content(self):
462
+ response = self._get() # used to return None (500)
463
+ self.assertEqual((response.status_code, response.content), (200, b""))
@@ -43,12 +43,20 @@ class GuestLoginView(View):
43
43
  return redirect('admin:index')
44
44
 
45
45
  def get_announcement_content(request):
46
+ """HTML for the announcement dialog. The template is project-provided
47
+ (ANNOUNCEMENT_CONTENT_HTML_FILE): without it the dialog stays empty instead of a 500."""
46
48
  from django.conf import settings
47
- content_file= getattr(settings, 'ANNOUNCEMENT_CONTENT_HTML_FILE', None)
48
- if not content_file:
49
- return None
50
- content = render_to_string(content_file, request=request)
51
49
  from django.http import HttpResponse
50
+ from django.template import TemplateDoesNotExist
51
+
52
+ content_file = getattr(settings, 'ANNOUNCEMENT_CONTENT_HTML_FILE', None)
53
+ if not content_file:
54
+ return HttpResponse("")
55
+ try:
56
+ content = render_to_string(str(content_file), request=request)
57
+ except TemplateDoesNotExist:
58
+ logger.warning("Announcement template %r not found (ANNOUNCEMENT_CONTENT_HTML_FILE)", content_file)
59
+ return HttpResponse("")
52
60
  return HttpResponse(content)
53
61
 
54
62
 
@@ -2,6 +2,7 @@ from django.shortcuts import render
2
2
  from django.utils.translation import gettext as _
3
3
  from django.contrib import messages
4
4
  from django.template.loader import render_to_string
5
+ from django.utils.html import format_html
5
6
  from django.utils.safestring import mark_safe
6
7
  import random
7
8
  import json
@@ -138,17 +139,27 @@ def dashboard_callback(request, context):
138
139
  _28_days = [_(WEEKDAYS[d]) for d in stats["weekdays"]]
139
140
 
140
141
  def render_kpi(label, object_name, value, last_value, time, success=None):
141
- is_increase = value > last_value
142
+ # Whole translatable sentences with named placeholders (word order differs per
143
+ # language; makemessages can't see _() inside f-strings)
142
144
  diff = value - last_value
143
145
  diff_percent = diff / last_value * 100 if last_value else 0
144
- footer = f'<strong class="text-{"green" if is_increase else "red"}-600 font-medium">{"+" if is_increase else "-"}{abs(diff)} ({diff_percent:.2f}%)</strong>&nbsp;{_("progress from last")} - {MAPTIME[time] if time != "today" else "day"}'
146
+ period = MAPTIME[time] if time != "today" else _("day")
147
+ footer = format_html(
148
+ '<strong class="{} font-medium">{}{} ({}%)</strong>&nbsp;{} - {}',
149
+ "text-green-600" if diff >= 0 else "text-red-600",
150
+ "+" if diff >= 0 else "-", abs(diff), f"{diff_percent:.2f}",
151
+ _("progress from last"), period,
152
+ )
145
153
  rate = round(success / value * 100, 1) if value and success else None
146
154
  return {
147
155
  "label": label,
148
- "title": f"{_(object_name)} {_('in')} {_(MAPTIME[time])}",
156
+ "title": _("%(object)s in %(period)s") % {"object": object_name, "period": MAPTIME[time]},
149
157
  "metric_total": value,
150
- "metric_success": f"{success} successful ({rate}%)" if rate is not None else None,
151
- "footer": mark_safe(footer),
158
+ "metric_success": (
159
+ _("%(count)s successful (%(rate)s%%)") % {"count": success, "rate": rate}
160
+ if rate is not None else None
161
+ ),
162
+ "footer": footer,
152
163
  }
153
164
 
154
165