django-whiteneuron 0.2.42__tar.gz → 0.2.47__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 (188) hide show
  1. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/PKG-INFO +42 -9
  2. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/README.md +35 -3
  3. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/django_whiteneuron.egg-info/PKG-INFO +42 -9
  4. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/django_whiteneuron.egg-info/requires.txt +3 -2
  5. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/pyproject.toml +8 -21
  6. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/admin.py +37 -150
  7. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/modeladmin.py +23 -16
  8. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/models.py +103 -153
  9. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/utils.py +33 -13
  10. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/feedbacks/admin.py +39 -26
  11. django_whiteneuron-0.2.47/whiteneuron/feedbacks/urls.py +81 -0
  12. django_whiteneuron-0.2.47/whiteneuron/feedbacks/utils.py +10 -0
  13. django_whiteneuron-0.2.47/whiteneuron/locale/vi/LC_MESSAGES/django.mo +0 -0
  14. django_whiteneuron-0.2.47/whiteneuron/locale/vi/LC_MESSAGES/django.po +1423 -0
  15. django_whiteneuron-0.2.47/whiteneuron/static/base/css/styles.css +2 -0
  16. django_whiteneuron-0.2.47/whiteneuron/templates/admin/base/app_change_list.html +202 -0
  17. django_whiteneuron-0.2.47/whiteneuron/templates/admin/feedbacks/feedback_change_form.html +132 -0
  18. django_whiteneuron-0.2.47/whiteneuron/templates/admin/submit_line.html +157 -0
  19. django_whiteneuron-0.2.42/whiteneuron/feedbacks/urls.py +0 -69
  20. django_whiteneuron-0.2.42/whiteneuron/feedbacks/utils.py +0 -5
  21. django_whiteneuron-0.2.42/whiteneuron/locale/vi/LC_MESSAGES/django.mo +0 -0
  22. django_whiteneuron-0.2.42/whiteneuron/locale/vi/LC_MESSAGES/django.po +0 -1182
  23. django_whiteneuron-0.2.42/whiteneuron/static/base/css/styles.css +0 -2
  24. django_whiteneuron-0.2.42/whiteneuron/templates/admin/base/app_change_list.html +0 -170
  25. django_whiteneuron-0.2.42/whiteneuron/templates/admin/feedbacks/feedback_change_form.html +0 -41
  26. django_whiteneuron-0.2.42/whiteneuron/templates/admin/submit_line.html +0 -67
  27. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/LICENSE +0 -0
  28. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/django_whiteneuron.egg-info/SOURCES.txt +0 -0
  29. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/django_whiteneuron.egg-info/dependency_links.txt +0 -0
  30. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/django_whiteneuron.egg-info/top_level.txt +0 -0
  31. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/setup.cfg +0 -0
  32. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/__init__.py +0 -0
  33. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/__init__.py +0 -0
  34. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/apps.py +0 -0
  35. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/asgi.py +0 -0
  36. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/ckeditor.py +0 -0
  37. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/filters.py +0 -0
  38. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/forms.py +0 -0
  39. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/management/__init__.py +0 -0
  40. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/management/commands/__init__.py +0 -0
  41. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/management/commands/init_admin.py +0 -0
  42. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/management/commands/init_groupuser.py +0 -0
  43. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/management/commands/init_guest.py +0 -0
  44. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/middleware.py +0 -0
  45. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/migrations/0001_initial.py +0 -0
  46. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/migrations/0002_imagemodel_userprofile_user_avatar_user_biography_and_more.py +0 -0
  47. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/migrations/0003_notification_obj_link.py +0 -0
  48. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/migrations/0004_delete_notification.py +0 -0
  49. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/migrations/0005_user_show_softdelete.py +0 -0
  50. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/migrations/0006_user_is_bot.py +0 -0
  51. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/migrations/0007_app.py +0 -0
  52. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/migrations/0008_app_created_at_app_created_by_app_deleted_at_and_more.py +0 -0
  53. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/migrations/0009_alter_app_table.py +0 -0
  54. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/migrations/0010_app_category.py +0 -0
  55. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/migrations/0011_alter_app_options.py +0 -0
  56. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/migrations/0012_app_permission.py +0 -0
  57. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/migrations/0013_app_thumbnail_url.py +0 -0
  58. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/migrations/0014_alter_app_created_at_alter_app_updated_at_and_more.py +0 -0
  59. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/migrations/0015_ipblacklist.py +0 -0
  60. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/migrations/0016_uablacklist.py +0 -0
  61. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/migrations/0017_alter_uablacklist_pattern.py +0 -0
  62. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/migrations/__init__.py +0 -0
  63. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/resources.py +0 -0
  64. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/settings.py +0 -0
  65. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/sites.py +0 -0
  66. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/tasks.py +0 -0
  67. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/templatetags/__init__.py +0 -0
  68. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/templatetags/unfold_list_custom.py +0 -0
  69. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/templatetags/wn_filters.py +0 -0
  70. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/tests.py +0 -0
  71. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/thread_local.py +0 -0
  72. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/urls.py +0 -0
  73. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/views.py +0 -0
  74. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/widgets.py +0 -0
  75. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/contrib/__init__.py +0 -0
  76. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/contrib/wn_import_export/__init__.py +0 -0
  77. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/contrib/wn_import_export/admin.py +0 -0
  78. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/contrib/wn_import_export/constants.py +0 -0
  79. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/contrib/wn_import_export/mixins.py +0 -0
  80. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/contrib/wn_import_export/tasks.py +0 -0
  81. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/contrib/wn_import_export/utils.py +0 -0
  82. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/contrib/wn_modeltranslation/__init__.py +0 -0
  83. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/contrib/wn_modeltranslation/admin.py +0 -0
  84. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/contrib/wn_modeltranslation/utils.py +0 -0
  85. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/dashboard/__init__.py +0 -0
  86. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/dashboard/admin.py +0 -0
  87. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/dashboard/apps.py +0 -0
  88. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/dashboard/migrations/__init__.py +0 -0
  89. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/dashboard/models.py +0 -0
  90. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/dashboard/tests.py +0 -0
  91. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/dashboard/views.py +0 -0
  92. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/feedbacks/__init__.py +0 -0
  93. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/feedbacks/apps.py +0 -0
  94. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/feedbacks/migrations/0001_initial.py +0 -0
  95. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/feedbacks/migrations/0002_feedbackdata_created_by_feedbackdata_deleted_at_and_more.py +0 -0
  96. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/feedbacks/migrations/0003_feedbackdata_note.py +0 -0
  97. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/feedbacks/migrations/0004_alter_feedbackdata_object_id.py +0 -0
  98. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/feedbacks/migrations/0005_feedbackdata_field.py +0 -0
  99. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/feedbacks/migrations/0006_alter_feedbackdata_created_at_and_more.py +0 -0
  100. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/feedbacks/migrations/__init__.py +0 -0
  101. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/feedbacks/models.py +0 -0
  102. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/feedbacks/tests.py +0 -0
  103. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/feedbacks/views.py +0 -0
  104. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/file_management/__init__.py +0 -0
  105. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/file_management/admin.py +0 -0
  106. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/file_management/apps.py +0 -0
  107. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/file_management/migrations/0001_initial.py +0 -0
  108. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/file_management/migrations/0002_alter_excelfile_options_alter_pdffile_options_and_more.py +0 -0
  109. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/file_management/migrations/0003_alter_excelfile_created_at_and_more.py +0 -0
  110. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/file_management/migrations/__init__.py +0 -0
  111. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/file_management/models.py +0 -0
  112. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/file_management/tests.py +0 -0
  113. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/file_management/utils.py +0 -0
  114. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/file_management/views.py +0 -0
  115. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/manage.py +0 -0
  116. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/notification/__init__.py +0 -0
  117. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/notification/admin.py +0 -0
  118. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/notification/apps.py +0 -0
  119. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/notification/consumers.py +0 -0
  120. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/notification/management/__init__.py +0 -0
  121. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/notification/management/commands/__init__.py +0 -0
  122. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/notification/management/commands/call_demo_noti.py +0 -0
  123. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/notification/migrations/0001_initial.py +0 -0
  124. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/notification/migrations/0002_alter_notificationconfig_event_type.py +0 -0
  125. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/notification/migrations/0003_notification.py +0 -0
  126. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/notification/migrations/0004_alter_notification_flag.py +0 -0
  127. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/notification/migrations/0005_notification_keywords.py +0 -0
  128. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/notification/migrations/0006_remove_notification_keywords.py +0 -0
  129. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/notification/migrations/0007_notification_changed_data.py +0 -0
  130. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/notification/migrations/0008_alter_notification_changed_data.py +0 -0
  131. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/notification/migrations/0009_notification_action_by_alter_notification_user.py +0 -0
  132. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/notification/migrations/0010_alter_notification_user.py +0 -0
  133. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/notification/migrations/0011_alter_notification_user.py +0 -0
  134. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/notification/migrations/0012_alter_notification_action_and_more.py +0 -0
  135. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/notification/migrations/__init__.py +0 -0
  136. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/notification/models.py +0 -0
  137. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/notification/routing.py +0 -0
  138. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/notification/tests.py +0 -0
  139. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/notification/utils.py +0 -0
  140. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/notification/views.py +0 -0
  141. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/static/base/css/btn-styles.css +0 -0
  142. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/static/base/css/ckeditor5.css +0 -0
  143. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/static/base/css/loading.css +0 -0
  144. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/static/base/forms/css/html-editor.css +0 -0
  145. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/static/base/forms/js/format.html.js +0 -0
  146. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/static/base/forms/js/trix.config.js +0 -0
  147. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/static/base/images/delete.svg +0 -0
  148. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/static/base/images/edit.svg +0 -0
  149. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/static/base/images/icon-viewlink.svg +0 -0
  150. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/static/base/images/loading.gif +0 -0
  151. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/static/base/images/login-bg.jpg +0 -0
  152. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/static/base/images/login_bg.jpg +0 -0
  153. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/static/base/images/logo/WhiteNeuron.png +0 -0
  154. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/static/base/js/chart.min.js +0 -0
  155. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/static/base/js/d3.v7.min.js +0 -0
  156. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/static/base/js/loading.js +0 -0
  157. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/static/base/js/whiteneuron.js +0 -0
  158. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/synchronization/__init__.py +0 -0
  159. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/synchronization/admin.py +0 -0
  160. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/synchronization/apps.py +0 -0
  161. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/synchronization/migrations/__init__.py +0 -0
  162. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/synchronization/models.py +0 -0
  163. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/synchronization/tests.py +0 -0
  164. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/synchronization/views.py +0 -0
  165. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/templates/400.html +0 -0
  166. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/templates/403.html +0 -0
  167. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/templates/404.html +0 -0
  168. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/templates/429.html +0 -0
  169. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/templates/500.html +0 -0
  170. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/templates/admin/auth/user/add_form.html +0 -0
  171. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/templates/admin/base/image_change_form.html +0 -0
  172. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/templates/admin/base/userprofile_change_form.html +0 -0
  173. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/templates/admin/base.html +0 -0
  174. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/templates/admin/change_list.html +0 -0
  175. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/templates/admin/grid_view_results.html +0 -0
  176. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/templates/admin/header.html +0 -0
  177. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/templates/admin/index.html +0 -0
  178. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/templates/admin/login.html +0 -0
  179. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/templates/admin/login_after.html +0 -0
  180. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/templates/admin/search_form.html +0 -0
  181. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/templates/base/forms/helpers/toolbar.html +0 -0
  182. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/templates/base/forms/wysiwyg.html +0 -0
  183. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/templates/base.html +0 -0
  184. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/templates/unfold/helpers/account_links.html +0 -0
  185. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/templates/unfold/helpers/app_list_badge.html +0 -0
  186. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/templates/unfold/helpers/field_password.html +0 -0
  187. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/templates/unfold/helpers/navigation_user.html +0 -0
  188. {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/templates/unfold/helpers/pagination.html +0 -0
@@ -1,8 +1,8 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: django-whiteneuron
3
- Version: 0.2.42
3
+ Version: 0.2.47
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
- Author-email: White Neuron <anhnt@whiteneuron.com>
5
+ Author-email: White Neuron <anhnt@whiteneuron.ai>
6
6
  License: MIT License
7
7
 
8
8
  Copyright (c) 2025 Nguyễn Tú Anh
@@ -25,7 +25,7 @@ License: MIT License
25
25
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
26
  SOFTWARE.
27
27
 
28
- Project-URL: homepage, https://github.com/White-Neuron/django-whiteneuron
28
+ Project-URL: homepage, https://whiteneuron.ai
29
29
  Project-URL: repository, https://github.com/White-Neuron/django-whiteneuron
30
30
  Project-URL: changelog, https://github.com/White-Neuron/django-whiteneuron/releases
31
31
  Project-URL: Bug Tracker, https://github.com/White-Neuron/django-whiteneuron/issues
@@ -34,7 +34,7 @@ Keywords: django,plugin,admin,dashboard,whiteneuron
34
34
  Classifier: Development Status :: 4 - Beta
35
35
  Classifier: Intended Audience :: Developers
36
36
  Classifier: Framework :: Django
37
- Classifier: Framework :: Django :: 5.0
37
+ Classifier: Framework :: Django :: 5.2
38
38
  Classifier: Programming Language :: Python :: 3
39
39
  Classifier: Programming Language :: Python :: 3.11
40
40
  Classifier: Programming Language :: Python :: 3.12
@@ -44,7 +44,7 @@ Requires-Python: >=3.11
44
44
  Description-Content-Type: text/markdown
45
45
  License-File: LICENSE
46
46
  Requires-Dist: django-debug-toolbar>=5.0.1
47
- Requires-Dist: django>=5.1.6
47
+ Requires-Dist: django>=5.2.12
48
48
  Requires-Dist: django-celery-beat>=2.7.0
49
49
  Requires-Dist: django-guardian>=2.4.0
50
50
  Requires-Dist: django-imagekit>=5.0.0
@@ -58,7 +58,7 @@ Requires-Dist: markdown>=3.7
58
58
  Requires-Dist: openai>=1.58.1
59
59
  Requires-Dist: openpyxl>=3.1.5
60
60
  Requires-Dist: pandas>=2.2.3
61
- Requires-Dist: pillow>=11.0.0
61
+ Requires-Dist: pillow>=12.1.1
62
62
  Requires-Dist: python-dotenv>=1.0.1
63
63
  Requires-Dist: redis>=5.2.1
64
64
  Requires-Dist: sentry-sdk==2.28.0
@@ -74,6 +74,7 @@ Requires-Dist: django-ckeditor-5>=0.2.18
74
74
  Requires-Dist: django-cors-headers>=4.3.0
75
75
  Requires-Dist: django-unfold>=0.85.0
76
76
  Requires-Dist: djangoql>=0.18.1
77
+ Requires-Dist: cryptography>=46.0.5
77
78
  Provides-Extra: dev
78
79
  Requires-Dist: pytest; extra == "dev"
79
80
  Requires-Dist: black; extra == "dev"
@@ -94,18 +95,50 @@ A modern Django Admin extension focused on UI/UX, dashboard, feedback, file mana
94
95
 
95
96
  ## Current Version
96
97
 
97
- - 0.2.42
98
+ - 0.2.45
98
99
 
99
100
  ## Compatibility
100
101
 
101
102
  - Python >= 3.11
102
- - Django >= 5.1.6
103
+ - Django >= 5.2.12
103
104
  - django-unfold >= 0.85.0
104
105
  - Tailwind CSS 4.x + daisyUI 5.x (for the bundled frontend styles)
105
106
 
106
107
  ## Changelog
107
108
 
108
- ### v0.2.42 (2026-03-31) — latest
109
+ ### v0.2.45 (2026-04-03) — latest
110
+ **Security: 11 CVE fixes across django, pillow, cryptography, pyasn1**
111
+ - **Security**: `django` lower bound raised from `>=5.1.6` to `>=5.2.12` — patches 7 CVEs including SQL Injection (CVE-2026-1207), DoS ×5, race condition in file-system storage (CVE-2026-25674), and URLField vulnerability (CVE-2026-25673).
112
+ - **Security**: `pillow` lower bound raised from `>=11.0.0` to `>=12.1.1` — patches heap buffer overflow (CVE-2026-25990).
113
+ - **Security**: `cryptography>=46.0.5` added as explicit dependency — patches Improper Input Validation (CVE-2026-26007); previously uncontrolled transitive dependency.
114
+ - **Security**: `pyasn1>=0.6.2` added as explicit dependency — patches CVE-2026-23490; `==0.6.1` was the sole affected version.
115
+ - **Validation**: `safety` scan confirms 0 known vulnerabilities after upgrade (down from 11).
116
+
117
+ ### v0.2.44 (2026-04-02)
118
+ **Fixes & Improvements: Feedback System — Error handling, input limits, i18n lazy evaluation**
119
+ - **Fixed**: `changeform_view` now returns `JsonResponse({"success": True})` after a successful resolve instead of falling through to `super()` with a POST request — prevents a spurious HTTP 403 caused by `has_change_permission = False`.
120
+ - **Fixed**: `note` is read from `request.POST` instead of `request.GET` — prevents HTTP 414 (URI Too Long) when the note is lengthy.
121
+ - **Fixed**: JS `fetch` upgraded to POST + `FormData` with CSRF token; error handling now maps status codes (403/404/500/network) to specific user-facing messages.
122
+ - **Added**: Two-sided character limits — `maxlength` attribute + live counter on client; server-side validation: `note ≤ 500`, `feedback_message ≤ 2000`.
123
+ - **Added**: `get_short_message()` in `FeedbackDataAdmin.list_display` — truncates to 200 words via Django's `Truncator`.
124
+ - **Fixed**: `search_help_text` moved from `__init__` to a `@property` using `format_lazy` — now re-evaluates per request and responds correctly to language switches instead of being frozen at server startup.
125
+ - **Improved**: `base_badge_callback()` accepts an optional `filter_kwargs` parameter; `feedback_data_badge_callback()` now scopes the badge count to the current user for non-superusers.
126
+
127
+ ### v0.2.43 (2026-04-01)
128
+ **Feature: Feedback System — DaisyUI modals, anti-spam cooldown, i18n, security hardening**
129
+ - **Added**: DaisyUI `<dialog>` modals replace native `prompt()`/`alert()` in both `feedback_change_form.html` (Mark as Resolved flow) and `submit_line.html` (Feedback submission) — smooth animations, no browser dialogs.
130
+ - **Added**: Server-side anti-spam cooldown — 60s per user globally; HTTP 429 with dynamically calculated remaining seconds (`remaining = 60 - elapsed`).
131
+ - **Security**: Replaced `@csrf_exempt` with `@require_POST` in feedback endpoint — CSRF protection fully enforced via `X-CSRFToken` header.
132
+ - **Security**: Auth check moved before body parse; removed redundant `User.objects.get()` in favor of `request.user`.
133
+ - **Fixed**: `FeedbackDataAdmin.changeform_view()` — null guard added after `get_object()` to prevent `AttributeError` when object does not exist.
134
+ - **Fixed**: OK button in result modal now wrapped in `<form method="dialog">` — dialog closes correctly, `close` event fires `location.reload()`.
135
+ - **Fixed**: Buttons missing `type="button"` were inadvertently submitting the admin form — fixed across all modals.
136
+ - **Fixed**: Modals moved to `document.body` via JS — eliminates content flash at page bottom during closing animation.
137
+ - **Fixed**: Import cleanup in `feedbacks/admin.py` — removed unused `display` import, moved `FEEDBACK_COOLDOWN_SECONDS` import to top.
138
+ - **Added**: Full i18n (`{% trans %}`, `{% blocktrans %}`, `gettext_lazy`) across feedback templates and `modeladmin.py` (12 strings); Vietnamese translations updated in `locale/vi`.
139
+ - **Added**: `feedback_cooldown_ms` passed from server via `FeedbackBaseAdmin.render_change_form()` — no more hardcoded 60000ms in template.
140
+
141
+ ### v0.2.42 (2026-03-31)
109
142
  **Improve: persist filter/search state in admin changelist + fix Django duplicate filter bug**
110
143
  - **Added**: Filter/search state is now persisted via `sessionStorage` across navigation — selecting a filter, navigating to a detail page, then returning restores the exact filter state automatically.
111
144
  - **Fixed**: Django bug — `add_preserved_filters()` uses `dict(parse_qsl())` which drops duplicate filter params (e.g. `?chapter__id__exact=29&chapter__id__exact=31` → only `=31` kept after save). Fixed server-side in `ModelAdmin._fix_preserved_filters()` — rebuilds the redirect URL with `parse_qsl()` (preserves all values) when duplicates are detected.
@@ -11,18 +11,50 @@ A modern Django Admin extension focused on UI/UX, dashboard, feedback, file mana
11
11
 
12
12
  ## Current Version
13
13
 
14
- - 0.2.42
14
+ - 0.2.45
15
15
 
16
16
  ## Compatibility
17
17
 
18
18
  - Python >= 3.11
19
- - Django >= 5.1.6
19
+ - Django >= 5.2.12
20
20
  - django-unfold >= 0.85.0
21
21
  - Tailwind CSS 4.x + daisyUI 5.x (for the bundled frontend styles)
22
22
 
23
23
  ## Changelog
24
24
 
25
- ### v0.2.42 (2026-03-31) — latest
25
+ ### v0.2.45 (2026-04-03) — latest
26
+ **Security: 11 CVE fixes across django, pillow, cryptography, pyasn1**
27
+ - **Security**: `django` lower bound raised from `>=5.1.6` to `>=5.2.12` — patches 7 CVEs including SQL Injection (CVE-2026-1207), DoS ×5, race condition in file-system storage (CVE-2026-25674), and URLField vulnerability (CVE-2026-25673).
28
+ - **Security**: `pillow` lower bound raised from `>=11.0.0` to `>=12.1.1` — patches heap buffer overflow (CVE-2026-25990).
29
+ - **Security**: `cryptography>=46.0.5` added as explicit dependency — patches Improper Input Validation (CVE-2026-26007); previously uncontrolled transitive dependency.
30
+ - **Security**: `pyasn1>=0.6.2` added as explicit dependency — patches CVE-2026-23490; `==0.6.1` was the sole affected version.
31
+ - **Validation**: `safety` scan confirms 0 known vulnerabilities after upgrade (down from 11).
32
+
33
+ ### v0.2.44 (2026-04-02)
34
+ **Fixes & Improvements: Feedback System — Error handling, input limits, i18n lazy evaluation**
35
+ - **Fixed**: `changeform_view` now returns `JsonResponse({"success": True})` after a successful resolve instead of falling through to `super()` with a POST request — prevents a spurious HTTP 403 caused by `has_change_permission = False`.
36
+ - **Fixed**: `note` is read from `request.POST` instead of `request.GET` — prevents HTTP 414 (URI Too Long) when the note is lengthy.
37
+ - **Fixed**: JS `fetch` upgraded to POST + `FormData` with CSRF token; error handling now maps status codes (403/404/500/network) to specific user-facing messages.
38
+ - **Added**: Two-sided character limits — `maxlength` attribute + live counter on client; server-side validation: `note ≤ 500`, `feedback_message ≤ 2000`.
39
+ - **Added**: `get_short_message()` in `FeedbackDataAdmin.list_display` — truncates to 200 words via Django's `Truncator`.
40
+ - **Fixed**: `search_help_text` moved from `__init__` to a `@property` using `format_lazy` — now re-evaluates per request and responds correctly to language switches instead of being frozen at server startup.
41
+ - **Improved**: `base_badge_callback()` accepts an optional `filter_kwargs` parameter; `feedback_data_badge_callback()` now scopes the badge count to the current user for non-superusers.
42
+
43
+ ### v0.2.43 (2026-04-01)
44
+ **Feature: Feedback System — DaisyUI modals, anti-spam cooldown, i18n, security hardening**
45
+ - **Added**: DaisyUI `<dialog>` modals replace native `prompt()`/`alert()` in both `feedback_change_form.html` (Mark as Resolved flow) and `submit_line.html` (Feedback submission) — smooth animations, no browser dialogs.
46
+ - **Added**: Server-side anti-spam cooldown — 60s per user globally; HTTP 429 with dynamically calculated remaining seconds (`remaining = 60 - elapsed`).
47
+ - **Security**: Replaced `@csrf_exempt` with `@require_POST` in feedback endpoint — CSRF protection fully enforced via `X-CSRFToken` header.
48
+ - **Security**: Auth check moved before body parse; removed redundant `User.objects.get()` in favor of `request.user`.
49
+ - **Fixed**: `FeedbackDataAdmin.changeform_view()` — null guard added after `get_object()` to prevent `AttributeError` when object does not exist.
50
+ - **Fixed**: OK button in result modal now wrapped in `<form method="dialog">` — dialog closes correctly, `close` event fires `location.reload()`.
51
+ - **Fixed**: Buttons missing `type="button"` were inadvertently submitting the admin form — fixed across all modals.
52
+ - **Fixed**: Modals moved to `document.body` via JS — eliminates content flash at page bottom during closing animation.
53
+ - **Fixed**: Import cleanup in `feedbacks/admin.py` — removed unused `display` import, moved `FEEDBACK_COOLDOWN_SECONDS` import to top.
54
+ - **Added**: Full i18n (`{% trans %}`, `{% blocktrans %}`, `gettext_lazy`) across feedback templates and `modeladmin.py` (12 strings); Vietnamese translations updated in `locale/vi`.
55
+ - **Added**: `feedback_cooldown_ms` passed from server via `FeedbackBaseAdmin.render_change_form()` — no more hardcoded 60000ms in template.
56
+
57
+ ### v0.2.42 (2026-03-31)
26
58
  **Improve: persist filter/search state in admin changelist + fix Django duplicate filter bug**
27
59
  - **Added**: Filter/search state is now persisted via `sessionStorage` across navigation — selecting a filter, navigating to a detail page, then returning restores the exact filter state automatically.
28
60
  - **Fixed**: Django bug — `add_preserved_filters()` uses `dict(parse_qsl())` which drops duplicate filter params (e.g. `?chapter__id__exact=29&chapter__id__exact=31` → only `=31` kept after save). Fixed server-side in `ModelAdmin._fix_preserved_filters()` — rebuilds the redirect URL with `parse_qsl()` (preserves all values) when duplicates are detected.
@@ -1,8 +1,8 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: django-whiteneuron
3
- Version: 0.2.42
3
+ Version: 0.2.47
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
- Author-email: White Neuron <anhnt@whiteneuron.com>
5
+ Author-email: White Neuron <anhnt@whiteneuron.ai>
6
6
  License: MIT License
7
7
 
8
8
  Copyright (c) 2025 Nguyễn Tú Anh
@@ -25,7 +25,7 @@ License: MIT License
25
25
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
26
  SOFTWARE.
27
27
 
28
- Project-URL: homepage, https://github.com/White-Neuron/django-whiteneuron
28
+ Project-URL: homepage, https://whiteneuron.ai
29
29
  Project-URL: repository, https://github.com/White-Neuron/django-whiteneuron
30
30
  Project-URL: changelog, https://github.com/White-Neuron/django-whiteneuron/releases
31
31
  Project-URL: Bug Tracker, https://github.com/White-Neuron/django-whiteneuron/issues
@@ -34,7 +34,7 @@ Keywords: django,plugin,admin,dashboard,whiteneuron
34
34
  Classifier: Development Status :: 4 - Beta
35
35
  Classifier: Intended Audience :: Developers
36
36
  Classifier: Framework :: Django
37
- Classifier: Framework :: Django :: 5.0
37
+ Classifier: Framework :: Django :: 5.2
38
38
  Classifier: Programming Language :: Python :: 3
39
39
  Classifier: Programming Language :: Python :: 3.11
40
40
  Classifier: Programming Language :: Python :: 3.12
@@ -44,7 +44,7 @@ Requires-Python: >=3.11
44
44
  Description-Content-Type: text/markdown
45
45
  License-File: LICENSE
46
46
  Requires-Dist: django-debug-toolbar>=5.0.1
47
- Requires-Dist: django>=5.1.6
47
+ Requires-Dist: django>=5.2.12
48
48
  Requires-Dist: django-celery-beat>=2.7.0
49
49
  Requires-Dist: django-guardian>=2.4.0
50
50
  Requires-Dist: django-imagekit>=5.0.0
@@ -58,7 +58,7 @@ Requires-Dist: markdown>=3.7
58
58
  Requires-Dist: openai>=1.58.1
59
59
  Requires-Dist: openpyxl>=3.1.5
60
60
  Requires-Dist: pandas>=2.2.3
61
- Requires-Dist: pillow>=11.0.0
61
+ Requires-Dist: pillow>=12.1.1
62
62
  Requires-Dist: python-dotenv>=1.0.1
63
63
  Requires-Dist: redis>=5.2.1
64
64
  Requires-Dist: sentry-sdk==2.28.0
@@ -74,6 +74,7 @@ Requires-Dist: django-ckeditor-5>=0.2.18
74
74
  Requires-Dist: django-cors-headers>=4.3.0
75
75
  Requires-Dist: django-unfold>=0.85.0
76
76
  Requires-Dist: djangoql>=0.18.1
77
+ Requires-Dist: cryptography>=46.0.5
77
78
  Provides-Extra: dev
78
79
  Requires-Dist: pytest; extra == "dev"
79
80
  Requires-Dist: black; extra == "dev"
@@ -94,18 +95,50 @@ A modern Django Admin extension focused on UI/UX, dashboard, feedback, file mana
94
95
 
95
96
  ## Current Version
96
97
 
97
- - 0.2.42
98
+ - 0.2.45
98
99
 
99
100
  ## Compatibility
100
101
 
101
102
  - Python >= 3.11
102
- - Django >= 5.1.6
103
+ - Django >= 5.2.12
103
104
  - django-unfold >= 0.85.0
104
105
  - Tailwind CSS 4.x + daisyUI 5.x (for the bundled frontend styles)
105
106
 
106
107
  ## Changelog
107
108
 
108
- ### v0.2.42 (2026-03-31) — latest
109
+ ### v0.2.45 (2026-04-03) — latest
110
+ **Security: 11 CVE fixes across django, pillow, cryptography, pyasn1**
111
+ - **Security**: `django` lower bound raised from `>=5.1.6` to `>=5.2.12` — patches 7 CVEs including SQL Injection (CVE-2026-1207), DoS ×5, race condition in file-system storage (CVE-2026-25674), and URLField vulnerability (CVE-2026-25673).
112
+ - **Security**: `pillow` lower bound raised from `>=11.0.0` to `>=12.1.1` — patches heap buffer overflow (CVE-2026-25990).
113
+ - **Security**: `cryptography>=46.0.5` added as explicit dependency — patches Improper Input Validation (CVE-2026-26007); previously uncontrolled transitive dependency.
114
+ - **Security**: `pyasn1>=0.6.2` added as explicit dependency — patches CVE-2026-23490; `==0.6.1` was the sole affected version.
115
+ - **Validation**: `safety` scan confirms 0 known vulnerabilities after upgrade (down from 11).
116
+
117
+ ### v0.2.44 (2026-04-02)
118
+ **Fixes & Improvements: Feedback System — Error handling, input limits, i18n lazy evaluation**
119
+ - **Fixed**: `changeform_view` now returns `JsonResponse({"success": True})` after a successful resolve instead of falling through to `super()` with a POST request — prevents a spurious HTTP 403 caused by `has_change_permission = False`.
120
+ - **Fixed**: `note` is read from `request.POST` instead of `request.GET` — prevents HTTP 414 (URI Too Long) when the note is lengthy.
121
+ - **Fixed**: JS `fetch` upgraded to POST + `FormData` with CSRF token; error handling now maps status codes (403/404/500/network) to specific user-facing messages.
122
+ - **Added**: Two-sided character limits — `maxlength` attribute + live counter on client; server-side validation: `note ≤ 500`, `feedback_message ≤ 2000`.
123
+ - **Added**: `get_short_message()` in `FeedbackDataAdmin.list_display` — truncates to 200 words via Django's `Truncator`.
124
+ - **Fixed**: `search_help_text` moved from `__init__` to a `@property` using `format_lazy` — now re-evaluates per request and responds correctly to language switches instead of being frozen at server startup.
125
+ - **Improved**: `base_badge_callback()` accepts an optional `filter_kwargs` parameter; `feedback_data_badge_callback()` now scopes the badge count to the current user for non-superusers.
126
+
127
+ ### v0.2.43 (2026-04-01)
128
+ **Feature: Feedback System — DaisyUI modals, anti-spam cooldown, i18n, security hardening**
129
+ - **Added**: DaisyUI `<dialog>` modals replace native `prompt()`/`alert()` in both `feedback_change_form.html` (Mark as Resolved flow) and `submit_line.html` (Feedback submission) — smooth animations, no browser dialogs.
130
+ - **Added**: Server-side anti-spam cooldown — 60s per user globally; HTTP 429 with dynamically calculated remaining seconds (`remaining = 60 - elapsed`).
131
+ - **Security**: Replaced `@csrf_exempt` with `@require_POST` in feedback endpoint — CSRF protection fully enforced via `X-CSRFToken` header.
132
+ - **Security**: Auth check moved before body parse; removed redundant `User.objects.get()` in favor of `request.user`.
133
+ - **Fixed**: `FeedbackDataAdmin.changeform_view()` — null guard added after `get_object()` to prevent `AttributeError` when object does not exist.
134
+ - **Fixed**: OK button in result modal now wrapped in `<form method="dialog">` — dialog closes correctly, `close` event fires `location.reload()`.
135
+ - **Fixed**: Buttons missing `type="button"` were inadvertently submitting the admin form — fixed across all modals.
136
+ - **Fixed**: Modals moved to `document.body` via JS — eliminates content flash at page bottom during closing animation.
137
+ - **Fixed**: Import cleanup in `feedbacks/admin.py` — removed unused `display` import, moved `FEEDBACK_COOLDOWN_SECONDS` import to top.
138
+ - **Added**: Full i18n (`{% trans %}`, `{% blocktrans %}`, `gettext_lazy`) across feedback templates and `modeladmin.py` (12 strings); Vietnamese translations updated in `locale/vi`.
139
+ - **Added**: `feedback_cooldown_ms` passed from server via `FeedbackBaseAdmin.render_change_form()` — no more hardcoded 60000ms in template.
140
+
141
+ ### v0.2.42 (2026-03-31)
109
142
  **Improve: persist filter/search state in admin changelist + fix Django duplicate filter bug**
110
143
  - **Added**: Filter/search state is now persisted via `sessionStorage` across navigation — selecting a filter, navigating to a detail page, then returning restores the exact filter state automatically.
111
144
  - **Fixed**: Django bug — `add_preserved_filters()` uses `dict(parse_qsl())` which drops duplicate filter params (e.g. `?chapter__id__exact=29&chapter__id__exact=31` → only `=31` kept after save). Fixed server-side in `ModelAdmin._fix_preserved_filters()` — rebuilds the redirect URL with `parse_qsl()` (preserves all values) when duplicates are detected.
@@ -1,5 +1,5 @@
1
1
  django-debug-toolbar>=5.0.1
2
- django>=5.1.6
2
+ django>=5.2.12
3
3
  django-celery-beat>=2.7.0
4
4
  django-guardian>=2.4.0
5
5
  django-imagekit>=5.0.0
@@ -13,7 +13,7 @@ markdown>=3.7
13
13
  openai>=1.58.1
14
14
  openpyxl>=3.1.5
15
15
  pandas>=2.2.3
16
- pillow>=11.0.0
16
+ pillow>=12.1.1
17
17
  python-dotenv>=1.0.1
18
18
  redis>=5.2.1
19
19
  sentry-sdk==2.28.0
@@ -29,6 +29,7 @@ django-ckeditor-5>=0.2.18
29
29
  django-cors-headers>=4.3.0
30
30
  django-unfold>=0.85.0
31
31
  djangoql>=0.18.1
32
+ cryptography>=46.0.5
32
33
 
33
34
  [dev]
34
35
  pytest
@@ -1,16 +1,16 @@
1
1
  [project]
2
2
  name = "django-whiteneuron"
3
- version = "0.2.42"
3
+ version = "0.2.47"
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
- {name = "White Neuron", email = "anhnt@whiteneuron.com"}
6
+ {name = "White Neuron", email = "anhnt@whiteneuron.ai"}
7
7
  ]
8
8
  license = {file = "LICENSE"}
9
9
  readme = "README.md"
10
10
  keywords = ["django", "plugin", "admin", "dashboard", "whiteneuron"]
11
11
  dependencies = [
12
12
  "django-debug-toolbar>=5.0.1",
13
- "django>=5.1.6",
13
+ "django>=5.2.12",
14
14
  "django-celery-beat>=2.7.0",
15
15
  "django-guardian>=2.4.0",
16
16
  "django-imagekit>=5.0.0",
@@ -24,7 +24,7 @@ dependencies = [
24
24
  "openai>=1.58.1",
25
25
  "openpyxl>=3.1.5",
26
26
  "pandas>=2.2.3",
27
- "pillow>=11.0.0",
27
+ "pillow>=12.1.1",
28
28
  "python-dotenv>=1.0.1",
29
29
  "redis>=5.2.1",
30
30
  "sentry-sdk==2.28.0",
@@ -40,6 +40,7 @@ dependencies = [
40
40
  'django-cors-headers>=4.3.0',
41
41
  "django-unfold>=0.85.0",
42
42
  "djangoql>=0.18.1",
43
+ "cryptography>=46.0.5",
43
44
  ]
44
45
  requires-python = ">=3.11"
45
46
 
@@ -47,7 +48,7 @@ classifiers = [
47
48
  "Development Status :: 4 - Beta",
48
49
  "Intended Audience :: Developers",
49
50
  "Framework :: Django",
50
- "Framework :: Django :: 5.0",
51
+ "Framework :: Django :: 5.2",
51
52
  "Programming Language :: Python :: 3",
52
53
  "Programming Language :: Python :: 3.11",
53
54
  "Programming Language :: Python :: 3.12",
@@ -56,37 +57,23 @@ classifiers = [
56
57
  ]
57
58
 
58
59
  [project.urls]
59
- homepage = "https://github.com/White-Neuron/django-whiteneuron"
60
+ homepage = "https://whiteneuron.ai"
60
61
  repository = "https://github.com/White-Neuron/django-whiteneuron"
61
62
  changelog = "https://github.com/White-Neuron/django-whiteneuron/releases"
62
63
  "Bug Tracker" = "https://github.com/White-Neuron/django-whiteneuron/issues"
63
64
  "PyPI" = "https://pypi.org/project/django-whiteneuron/"
64
65
 
65
- [tool.setuptools]
66
66
  [tool.setuptools.packages.find]
67
67
  where = ["."]
68
68
  include = ["whiteneuron*"]
69
69
 
70
70
  [tool.setuptools.package-data]
71
71
  "whiteneuron" = [
72
- "base/templates/**/*",
73
- "base/static/**/*",
74
- "feedbacks/templates/**/*",
75
- "feedbacks/static/**/*",
76
72
  "templates/**/*",
77
- "templates/**/**/*",
78
- "templates/**/**/**/*",
79
73
  "static/**/*",
80
- "static/**/**/*",
81
- "static/**/**/**/*",
82
- 'locale/**/*',
83
- 'locale/**/**/*',
84
- 'locale/**/**/**/*',
74
+ "locale/**/*",
85
75
  ]
86
76
 
87
- [tool.uv.workspace]
88
- members = ["demo"]
89
-
90
77
  [project.optional-dependencies]
91
78
  dev = ["pytest", "black", "flake8", "pre-commit"]
92
79
 
@@ -1,3 +1,4 @@
1
+ import types as _types
1
2
  from typing import Sequence
2
3
  from django import forms
3
4
  from django.contrib import admin, messages
@@ -623,67 +624,32 @@ class MailAdmin(ModelAdmin):
623
624
 
624
625
 
625
626
  from django.conf import settings
626
- from django.core.cache import cache
627
-
628
- def init_app_db():
629
- # Kiểm tra xem hàm đã được chạy chưa
630
- cache_key = 'init_app_db_executed'
631
- cached_value = cache.get(cache_key)
632
- # Đã chạy rồi, bỏ qua
633
- if cached_value is not None:
634
- return
635
-
636
- # Get apps from SIDEBAR in UNFOLD settings
637
- sidebar_apps= []
627
+
628
+ def _parse_sidebar_apps():
629
+ """Parse UNFOLD SIDEBAR settings into app-like objects. No DB access."""
630
+ apps = []
638
631
  try:
639
- sidebar= settings.UNFOLD.get('SIDEBAR', {})
640
- navigation= sidebar.get('navigation', [])
632
+ navigation = settings.UNFOLD.get('SIDEBAR', {}).get('navigation', [])
641
633
  for section in navigation:
642
- category= section.get('title', None)
643
- items= section.get('items', [])
644
- for item in items:
645
- app_name= item.get('title', None)
646
- link= item.get('link', None)
647
- if '/base/app/' in str(link):
648
- # bỏ qua link quản trị app
634
+ category = section.get('title')
635
+ for item in section.get('items', []):
636
+ name = item.get('title')
637
+ link = item.get('link')
638
+ if not (name and link) or '/base/app/' in str(link):
649
639
  continue
650
- icon= item.get('icon', None)
651
- permission= item.get('permission', None)
652
- thumbnail_url= item.get('thumbnail', None)
653
- if app_name and link:
654
- sidebar_apps.append({
655
- 'name': app_name,
656
- 'url': link,
657
- 'icon': icon,
658
- 'category': category,
659
- 'permission': permission,
660
- 'thumbnail_url': thumbnail_url,
661
- })
662
- except:
640
+ apps.append(_types.SimpleNamespace(
641
+ pk=None,
642
+ name=name,
643
+ url=link,
644
+ icon=item.get('icon'),
645
+ category=category,
646
+ permission=item.get('permission'),
647
+ thumbnail_url=item.get('thumbnail'),
648
+ is_active=True,
649
+ ))
650
+ except Exception:
663
651
  pass
664
- # Cập nhật hoặc tạo mới các app từ sidebar_apps
665
- for app in sidebar_apps:
666
- obj= App.objects.filter(url= app['url']).first()
667
- if obj is None:
668
- obj= App()
669
- obj.name= app['name']
670
- obj.url= app['url']
671
- obj.icon= app['icon']
672
- obj.category= app['category']
673
- obj.permission= app['permission']
674
- obj.thumbnail_url= app['thumbnail_url']
675
- obj.is_active= True
676
- obj.save(notification= False)
677
-
678
- # set active= False cho các app không có trong sidebar_apps
679
- existing_apps= App.objects.all()
680
- for obj in existing_apps:
681
- if not any(app['url'] == obj.url for app in sidebar_apps):
682
- obj.is_active= False
683
- obj.save(notification= False)
684
-
685
- # Re-sync every 5 minutes so SIDEBAR changes are picked up without manual cache clear
686
- cache.set(cache_key, True, timeout=300)
652
+ return apps
687
653
 
688
654
  # App Model Admin
689
655
  from django.utils.module_loading import import_string
@@ -694,29 +660,8 @@ class AppAdmin(ModelAdmin):
694
660
  list_before_template= 'admin/header.html'
695
661
  change_list_template= 'admin/base/app_change_list.html'
696
662
 
697
- list_display = ['icon_display', 'name', 'is_active', 'category', 'permission']
698
- search_fields = ['name']
699
- list_filter = ['is_active', 'category']
700
- readonly_fields = ['icon_display']
701
- fieldsets = (
702
- (_('App info'), {
703
- 'fields': ('name', 'is_active', 'icon', 'url')
704
- }),
705
- )
706
-
707
- list_filter_submit= False
708
-
709
663
  default_toggle_sidebar= False
710
664
 
711
- def icon_display(self, instance: App):
712
- if instance.icon:
713
- if instance.icon.startswith('http://') or instance.icon.startswith('https://'):
714
- return format_html(f'<img src="{instance.icon}" height="48"/>')
715
- else:
716
- return format_html(f'<span class="material-symbols-outlined" style="font-size: 48px;">{instance.icon}</span>')
717
- return None
718
- icon_display.short_description = _('Icon')
719
-
720
665
  def has_add_permission(self, request):
721
666
  return False
722
667
 
@@ -726,90 +671,32 @@ class AppAdmin(ModelAdmin):
726
671
  def has_delete_permission(self, request, obj = ...):
727
672
  return False
728
673
 
729
- grid_view= True
730
- grid_exclude_fields_list_display= ['icon_display', 'is_active', 'name', 'category', 'permission']
731
- action_buttons= False
732
-
733
- def grid_item_header(self, obj):
734
- if obj.thumbnail_url:
735
- icon_html = f'<img src="{static(obj.thumbnail_url)}" class="w-14 h-14 object-contain rounded-xl"/>'
736
- elif obj.icon and (obj.icon.startswith('http://') or obj.icon.startswith('https://')):
737
- icon_html = f'<img src="{obj.icon}" class="w-14 h-14 object-contain rounded-xl"/>'
738
- elif obj.icon:
739
- icon_html = f'<span class="material-symbols-outlined" style="font-size:3.5rem;line-height:1">{obj.icon}</span>'
740
- else:
741
- icon_html = '<span class="material-symbols-outlined text-base-content/30" style="font-size:3.5rem;line-height:1">apps</span>'
742
-
743
- category_html = (
744
- f'<span class="text-xs text-base-content/40 mt-1 truncate max-w-full">{_(obj.category)}</span>'
745
- if obj.category else ''
746
- )
747
-
748
- string = f"""
749
- <div class="ui-card h-full flex flex-col items-center justify-center gap-3 p-5 bg-base-100 dark:bg-base-800 border border-base-200 dark:border-base-700 hover:border-primary/40 dark:hover:border-primary/50 hover:shadow-lg dark:hover:shadow-base-content/10 transition-all duration-200 cursor-pointer group rounded-2xl">
750
- <div class="flex items-center justify-center w-16 h-16 rounded-2xl bg-base-200/60 dark:bg-base-700/40 group-hover:bg-primary/10 dark:group-hover:bg-primary/15 transition-colors duration-200 text-primary">
751
- {icon_html}
752
- </div>
753
- <div class="flex flex-col items-center text-center gap-0.5 w-full">
754
- <span class="font-semibold text-sm leading-snug line-clamp-2">{_(obj.name)}</span>
755
- {category_html}
756
- </div>
757
- </div>
758
- """
759
- return mark_safe(string)
760
- grid_item_header.short_description = "Header"
761
-
762
- def has_permission(self, request, obj= None):
674
+ def has_permission(self, request, obj=None):
763
675
  if request.user.is_superuser:
764
676
  return True
765
- permission= obj.permission if obj else None # string like: 'whiteneuron.base.utils.permission_admin_callback'
677
+ permission = obj.permission if obj else None
766
678
  if permission:
767
- perm_func= import_string(permission)
768
- if perm_func:
769
- return perm_func(request)
679
+ perm_func = permission if callable(permission) else import_string(permission)
680
+ return perm_func(request)
770
681
  return True
771
682
 
772
683
  def get_queryset(self, request):
773
- init_app_db()
774
- queryset = super(AppAdmin, self).get_queryset(request).filter(is_active=True)
775
-
776
- if request.user.is_superuser:
777
- return queryset
778
-
779
- allowed_ids = [obj.pk for obj in queryset if self.has_permission(request, obj)]
780
- return queryset.filter(pk__in=allowed_ids)
781
-
782
- def changeform_view(self, request, object_id=None, form_url="", extra_context=None):
783
- ## Điều hướng tới url của app
784
- app= get_object_or_404(App, pk= object_id)
785
- if not app.is_active:
786
- messages.error(request, _("This app is not active."))
787
- return redirect(reverse_lazy('admin:base_app_changelist'))
788
- if not self.has_permission(request, app):
789
- messages.error(request, _("You do not have permission to access this app."))
790
- return redirect(reverse_lazy('admin:base_app_changelist'))
791
- if app.url:
792
- return redirect(app.url)
793
- return super().changeform_view(request, object_id, form_url, extra_context)
684
+ return super().get_queryset(request).none()
794
685
 
795
686
  def changelist_view(self, request, extra_context=None):
796
687
  if extra_context is None:
797
- extra_context= {}
688
+ extra_context = {}
798
689
  extra_context.update(dashboard_callback(request, extra_context))
799
- extra_context['title']= _('Dashboard')
690
+ extra_context['title'] = _('Dashboard')
691
+
692
+ apps = _parse_sidebar_apps()
693
+ if not request.user.is_superuser:
694
+ apps = [app for app in apps if self.has_permission(request, app)]
800
695
 
801
- # Group apps by category for two-level grid UI
802
- qs = self.get_queryset(request)
803
696
  app_by_category = {}
804
- for app in qs:
697
+ for app in apps:
805
698
  cat = app.category or ''
806
699
  app_by_category.setdefault(cat, []).append(app)
807
- # Sort: named categories alphabetically, unnamed ("Other") last
808
- extra_context['app_by_category'] = dict(
809
- sorted(app_by_category.items(), key=lambda x: (x[0] == '', x[0].lower()))
810
- )
811
-
812
- return super().changelist_view(request, extra_context)
700
+ extra_context['app_by_category'] = app_by_category
813
701
 
814
- def get_list_filter(self, request):
815
- return ['category']
702
+ return super().changelist_view(request, extra_context)