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.
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/PKG-INFO +42 -9
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/README.md +35 -3
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/django_whiteneuron.egg-info/PKG-INFO +42 -9
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/django_whiteneuron.egg-info/requires.txt +3 -2
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/pyproject.toml +8 -21
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/admin.py +37 -150
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/modeladmin.py +23 -16
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/models.py +103 -153
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/utils.py +33 -13
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/feedbacks/admin.py +39 -26
- django_whiteneuron-0.2.47/whiteneuron/feedbacks/urls.py +81 -0
- django_whiteneuron-0.2.47/whiteneuron/feedbacks/utils.py +10 -0
- django_whiteneuron-0.2.47/whiteneuron/locale/vi/LC_MESSAGES/django.mo +0 -0
- django_whiteneuron-0.2.47/whiteneuron/locale/vi/LC_MESSAGES/django.po +1423 -0
- django_whiteneuron-0.2.47/whiteneuron/static/base/css/styles.css +2 -0
- django_whiteneuron-0.2.47/whiteneuron/templates/admin/base/app_change_list.html +202 -0
- django_whiteneuron-0.2.47/whiteneuron/templates/admin/feedbacks/feedback_change_form.html +132 -0
- django_whiteneuron-0.2.47/whiteneuron/templates/admin/submit_line.html +157 -0
- django_whiteneuron-0.2.42/whiteneuron/feedbacks/urls.py +0 -69
- django_whiteneuron-0.2.42/whiteneuron/feedbacks/utils.py +0 -5
- django_whiteneuron-0.2.42/whiteneuron/locale/vi/LC_MESSAGES/django.mo +0 -0
- django_whiteneuron-0.2.42/whiteneuron/locale/vi/LC_MESSAGES/django.po +0 -1182
- django_whiteneuron-0.2.42/whiteneuron/static/base/css/styles.css +0 -2
- django_whiteneuron-0.2.42/whiteneuron/templates/admin/base/app_change_list.html +0 -170
- django_whiteneuron-0.2.42/whiteneuron/templates/admin/feedbacks/feedback_change_form.html +0 -41
- django_whiteneuron-0.2.42/whiteneuron/templates/admin/submit_line.html +0 -67
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/LICENSE +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/django_whiteneuron.egg-info/SOURCES.txt +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/django_whiteneuron.egg-info/dependency_links.txt +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/django_whiteneuron.egg-info/top_level.txt +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/setup.cfg +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/__init__.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/__init__.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/apps.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/asgi.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/ckeditor.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/filters.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/forms.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/management/__init__.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/management/commands/__init__.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/management/commands/init_admin.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/management/commands/init_groupuser.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/management/commands/init_guest.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/middleware.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/migrations/0001_initial.py +0 -0
- {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
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/migrations/0003_notification_obj_link.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/migrations/0004_delete_notification.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/migrations/0005_user_show_softdelete.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/migrations/0006_user_is_bot.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/migrations/0007_app.py +0 -0
- {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
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/migrations/0009_alter_app_table.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/migrations/0010_app_category.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/migrations/0011_alter_app_options.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/migrations/0012_app_permission.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/migrations/0013_app_thumbnail_url.py +0 -0
- {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
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/migrations/0015_ipblacklist.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/migrations/0016_uablacklist.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/migrations/0017_alter_uablacklist_pattern.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/migrations/__init__.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/resources.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/settings.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/sites.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/tasks.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/templatetags/__init__.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/templatetags/unfold_list_custom.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/templatetags/wn_filters.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/tests.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/thread_local.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/urls.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/views.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/base/widgets.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/contrib/__init__.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/contrib/wn_import_export/__init__.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/contrib/wn_import_export/admin.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/contrib/wn_import_export/constants.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/contrib/wn_import_export/mixins.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/contrib/wn_import_export/tasks.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/contrib/wn_import_export/utils.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/contrib/wn_modeltranslation/__init__.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/contrib/wn_modeltranslation/admin.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/contrib/wn_modeltranslation/utils.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/dashboard/__init__.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/dashboard/admin.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/dashboard/apps.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/dashboard/migrations/__init__.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/dashboard/models.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/dashboard/tests.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/dashboard/views.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/feedbacks/__init__.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/feedbacks/apps.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/feedbacks/migrations/0001_initial.py +0 -0
- {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
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/feedbacks/migrations/0003_feedbackdata_note.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/feedbacks/migrations/0004_alter_feedbackdata_object_id.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/feedbacks/migrations/0005_feedbackdata_field.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/feedbacks/migrations/0006_alter_feedbackdata_created_at_and_more.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/feedbacks/migrations/__init__.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/feedbacks/models.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/feedbacks/tests.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/feedbacks/views.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/file_management/__init__.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/file_management/admin.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/file_management/apps.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/file_management/migrations/0001_initial.py +0 -0
- {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
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/file_management/migrations/0003_alter_excelfile_created_at_and_more.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/file_management/migrations/__init__.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/file_management/models.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/file_management/tests.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/file_management/utils.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/file_management/views.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/manage.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/notification/__init__.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/notification/admin.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/notification/apps.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/notification/consumers.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/notification/management/__init__.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/notification/management/commands/__init__.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/notification/management/commands/call_demo_noti.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/notification/migrations/0001_initial.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/notification/migrations/0002_alter_notificationconfig_event_type.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/notification/migrations/0003_notification.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/notification/migrations/0004_alter_notification_flag.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/notification/migrations/0005_notification_keywords.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/notification/migrations/0006_remove_notification_keywords.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/notification/migrations/0007_notification_changed_data.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/notification/migrations/0008_alter_notification_changed_data.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/notification/migrations/0009_notification_action_by_alter_notification_user.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/notification/migrations/0010_alter_notification_user.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/notification/migrations/0011_alter_notification_user.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/notification/migrations/0012_alter_notification_action_and_more.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/notification/migrations/__init__.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/notification/models.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/notification/routing.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/notification/tests.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/notification/utils.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/notification/views.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/static/base/css/btn-styles.css +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/static/base/css/ckeditor5.css +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/static/base/css/loading.css +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/static/base/forms/css/html-editor.css +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/static/base/forms/js/format.html.js +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/static/base/forms/js/trix.config.js +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/static/base/images/delete.svg +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/static/base/images/edit.svg +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/static/base/images/icon-viewlink.svg +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/static/base/images/loading.gif +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/static/base/images/login-bg.jpg +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/static/base/images/login_bg.jpg +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/static/base/images/logo/WhiteNeuron.png +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/static/base/js/chart.min.js +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/static/base/js/d3.v7.min.js +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/static/base/js/loading.js +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/static/base/js/whiteneuron.js +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/synchronization/__init__.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/synchronization/admin.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/synchronization/apps.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/synchronization/migrations/__init__.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/synchronization/models.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/synchronization/tests.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/synchronization/views.py +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/templates/400.html +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/templates/403.html +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/templates/404.html +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/templates/429.html +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/templates/500.html +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/templates/admin/auth/user/add_form.html +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/templates/admin/base/image_change_form.html +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/templates/admin/base/userprofile_change_form.html +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/templates/admin/base.html +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/templates/admin/change_list.html +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/templates/admin/grid_view_results.html +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/templates/admin/header.html +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/templates/admin/index.html +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/templates/admin/login.html +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/templates/admin/login_after.html +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/templates/admin/search_form.html +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/templates/base/forms/helpers/toolbar.html +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/templates/base/forms/wysiwyg.html +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/templates/base.html +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/templates/unfold/helpers/account_links.html +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/templates/unfold/helpers/app_list_badge.html +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/templates/unfold/helpers/field_password.html +0 -0
- {django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/whiteneuron/templates/unfold/helpers/navigation_user.html +0 -0
- {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.
|
|
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.
|
|
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://
|
|
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.
|
|
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.
|
|
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>=
|
|
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.
|
|
98
|
+
- 0.2.45
|
|
98
99
|
|
|
99
100
|
## Compatibility
|
|
100
101
|
|
|
101
102
|
- Python >= 3.11
|
|
102
|
-
- Django >= 5.
|
|
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.
|
|
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.
|
|
14
|
+
- 0.2.45
|
|
15
15
|
|
|
16
16
|
## Compatibility
|
|
17
17
|
|
|
18
18
|
- Python >= 3.11
|
|
19
|
-
- Django >= 5.
|
|
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.
|
|
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.
|
{django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/django_whiteneuron.egg-info/PKG-INFO
RENAMED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: django-whiteneuron
|
|
3
|
-
Version: 0.2.
|
|
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.
|
|
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://
|
|
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.
|
|
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.
|
|
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>=
|
|
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.
|
|
98
|
+
- 0.2.45
|
|
98
99
|
|
|
99
100
|
## Compatibility
|
|
100
101
|
|
|
101
102
|
- Python >= 3.11
|
|
102
|
-
- Django >= 5.
|
|
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.
|
|
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.
|
{django_whiteneuron-0.2.42 → django_whiteneuron-0.2.47}/django_whiteneuron.egg-info/requires.txt
RENAMED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
django-debug-toolbar>=5.0.1
|
|
2
|
-
django>=5.
|
|
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>=
|
|
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.
|
|
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.
|
|
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.
|
|
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>=
|
|
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.
|
|
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://
|
|
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
|
-
"
|
|
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
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
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
|
-
|
|
640
|
-
navigation= sidebar.get('navigation', [])
|
|
632
|
+
navigation = settings.UNFOLD.get('SIDEBAR', {}).get('navigation', [])
|
|
641
633
|
for section in navigation:
|
|
642
|
-
category= section.get('title'
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
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
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
677
|
+
permission = obj.permission if obj else None
|
|
766
678
|
if permission:
|
|
767
|
-
perm_func= import_string(permission)
|
|
768
|
-
|
|
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
|
-
|
|
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
|
|
697
|
+
for app in apps:
|
|
805
698
|
cat = app.category or ''
|
|
806
699
|
app_by_category.setdefault(cat, []).append(app)
|
|
807
|
-
|
|
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
|
-
|
|
815
|
-
return ['category']
|
|
702
|
+
return super().changelist_view(request, extra_context)
|