django-whiteneuron 0.2.47__tar.gz → 0.2.48__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.47 → django_whiteneuron-0.2.48}/PKG-INFO +36 -3
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/README.md +35 -2
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/django_whiteneuron.egg-info/PKG-INFO +36 -3
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/django_whiteneuron.egg-info/SOURCES.txt +2 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/pyproject.toml +1 -1
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/base/modeladmin.py +36 -59
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/base/models.py +29 -6
- django_whiteneuron-0.2.48/whiteneuron/locale/vi/LC_MESSAGES/django.mo +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/locale/vi/LC_MESSAGES/django.po +647 -673
- django_whiteneuron-0.2.48/whiteneuron/static/base/css/styles.css +2 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/templates/admin/base/image_change_form.html +1 -1
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/templates/admin/base/userprofile_change_form.html +1 -1
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/templates/admin/change_list.html +0 -1
- django_whiteneuron-0.2.48/whiteneuron/templates/admin/wn_change_form.html +12 -0
- django_whiteneuron-0.2.48/whiteneuron/templates/admin/wn_history_panel.html +113 -0
- django_whiteneuron-0.2.47/whiteneuron/locale/vi/LC_MESSAGES/django.mo +0 -0
- django_whiteneuron-0.2.47/whiteneuron/static/base/css/styles.css +0 -2
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/LICENSE +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/django_whiteneuron.egg-info/dependency_links.txt +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/django_whiteneuron.egg-info/requires.txt +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/django_whiteneuron.egg-info/top_level.txt +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/setup.cfg +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/__init__.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/base/__init__.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/base/admin.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/base/apps.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/base/asgi.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/base/ckeditor.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/base/filters.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/base/forms.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/base/management/__init__.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/base/management/commands/__init__.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/base/management/commands/init_admin.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/base/management/commands/init_groupuser.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/base/management/commands/init_guest.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/base/middleware.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/base/migrations/0001_initial.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/base/migrations/0002_imagemodel_userprofile_user_avatar_user_biography_and_more.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/base/migrations/0003_notification_obj_link.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/base/migrations/0004_delete_notification.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/base/migrations/0005_user_show_softdelete.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/base/migrations/0006_user_is_bot.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/base/migrations/0007_app.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/base/migrations/0008_app_created_at_app_created_by_app_deleted_at_and_more.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/base/migrations/0009_alter_app_table.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/base/migrations/0010_app_category.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/base/migrations/0011_alter_app_options.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/base/migrations/0012_app_permission.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/base/migrations/0013_app_thumbnail_url.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/base/migrations/0014_alter_app_created_at_alter_app_updated_at_and_more.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/base/migrations/0015_ipblacklist.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/base/migrations/0016_uablacklist.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/base/migrations/0017_alter_uablacklist_pattern.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/base/migrations/__init__.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/base/resources.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/base/settings.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/base/sites.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/base/tasks.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/base/templatetags/__init__.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/base/templatetags/unfold_list_custom.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/base/templatetags/wn_filters.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/base/tests.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/base/thread_local.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/base/urls.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/base/utils.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/base/views.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/base/widgets.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/contrib/__init__.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/contrib/wn_import_export/__init__.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/contrib/wn_import_export/admin.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/contrib/wn_import_export/constants.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/contrib/wn_import_export/mixins.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/contrib/wn_import_export/tasks.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/contrib/wn_import_export/utils.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/contrib/wn_modeltranslation/__init__.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/contrib/wn_modeltranslation/admin.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/contrib/wn_modeltranslation/utils.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/dashboard/__init__.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/dashboard/admin.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/dashboard/apps.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/dashboard/migrations/__init__.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/dashboard/models.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/dashboard/tests.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/dashboard/views.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/feedbacks/__init__.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/feedbacks/admin.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/feedbacks/apps.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/feedbacks/migrations/0001_initial.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/feedbacks/migrations/0002_feedbackdata_created_by_feedbackdata_deleted_at_and_more.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/feedbacks/migrations/0003_feedbackdata_note.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/feedbacks/migrations/0004_alter_feedbackdata_object_id.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/feedbacks/migrations/0005_feedbackdata_field.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/feedbacks/migrations/0006_alter_feedbackdata_created_at_and_more.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/feedbacks/migrations/__init__.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/feedbacks/models.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/feedbacks/tests.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/feedbacks/urls.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/feedbacks/utils.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/feedbacks/views.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/file_management/__init__.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/file_management/admin.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/file_management/apps.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/file_management/migrations/0001_initial.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/file_management/migrations/0002_alter_excelfile_options_alter_pdffile_options_and_more.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/file_management/migrations/0003_alter_excelfile_created_at_and_more.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/file_management/migrations/__init__.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/file_management/models.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/file_management/tests.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/file_management/utils.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/file_management/views.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/manage.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/notification/__init__.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/notification/admin.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/notification/apps.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/notification/consumers.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/notification/management/__init__.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/notification/management/commands/__init__.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/notification/management/commands/call_demo_noti.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/notification/migrations/0001_initial.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/notification/migrations/0002_alter_notificationconfig_event_type.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/notification/migrations/0003_notification.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/notification/migrations/0004_alter_notification_flag.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/notification/migrations/0005_notification_keywords.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/notification/migrations/0006_remove_notification_keywords.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/notification/migrations/0007_notification_changed_data.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/notification/migrations/0008_alter_notification_changed_data.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/notification/migrations/0009_notification_action_by_alter_notification_user.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/notification/migrations/0010_alter_notification_user.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/notification/migrations/0011_alter_notification_user.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/notification/migrations/0012_alter_notification_action_and_more.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/notification/migrations/__init__.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/notification/models.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/notification/routing.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/notification/tests.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/notification/utils.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/notification/views.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/static/base/css/btn-styles.css +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/static/base/css/ckeditor5.css +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/static/base/css/loading.css +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/static/base/forms/css/html-editor.css +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/static/base/forms/js/format.html.js +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/static/base/forms/js/trix.config.js +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/static/base/images/delete.svg +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/static/base/images/edit.svg +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/static/base/images/icon-viewlink.svg +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/static/base/images/loading.gif +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/static/base/images/login-bg.jpg +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/static/base/images/login_bg.jpg +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/static/base/images/logo/WhiteNeuron.png +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/static/base/js/chart.min.js +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/static/base/js/d3.v7.min.js +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/static/base/js/loading.js +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/static/base/js/whiteneuron.js +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/synchronization/__init__.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/synchronization/admin.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/synchronization/apps.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/synchronization/migrations/__init__.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/synchronization/models.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/synchronization/tests.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/synchronization/views.py +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/templates/400.html +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/templates/403.html +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/templates/404.html +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/templates/429.html +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/templates/500.html +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/templates/admin/auth/user/add_form.html +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/templates/admin/base/app_change_list.html +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/templates/admin/base.html +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/templates/admin/feedbacks/feedback_change_form.html +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/templates/admin/grid_view_results.html +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/templates/admin/header.html +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/templates/admin/index.html +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/templates/admin/login.html +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/templates/admin/login_after.html +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/templates/admin/search_form.html +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/templates/admin/submit_line.html +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/templates/base/forms/helpers/toolbar.html +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/templates/base/forms/wysiwyg.html +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/templates/base.html +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/templates/unfold/helpers/account_links.html +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/templates/unfold/helpers/app_list_badge.html +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/templates/unfold/helpers/field_password.html +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/templates/unfold/helpers/navigation_user.html +0 -0
- {django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/whiteneuron/templates/unfold/helpers/pagination.html +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: django-whiteneuron
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.48
|
|
4
4
|
Summary: A modern Django Admin extension with enhanced UI/UX, dashboard, feedback, file management, rate limiting, IP blacklisting, and advanced admin integrations built on django-unfold.
|
|
5
5
|
Author-email: White Neuron <anhnt@whiteneuron.ai>
|
|
6
6
|
License: MIT License
|
|
@@ -95,7 +95,7 @@ A modern Django Admin extension focused on UI/UX, dashboard, feedback, file mana
|
|
|
95
95
|
|
|
96
96
|
## Current Version
|
|
97
97
|
|
|
98
|
-
- 0.2.
|
|
98
|
+
- 0.2.48
|
|
99
99
|
|
|
100
100
|
## Compatibility
|
|
101
101
|
|
|
@@ -106,7 +106,40 @@ A modern Django Admin extension focused on UI/UX, dashboard, feedback, file mana
|
|
|
106
106
|
|
|
107
107
|
## Changelog
|
|
108
108
|
|
|
109
|
-
### v0.2.
|
|
109
|
+
### v0.2.48 (2026-04-06) — latest
|
|
110
|
+
**Feature: Change history panel — modal UI, M2M diff, permission gate**
|
|
111
|
+
- **Added**: Change history panel shown on every change-form — floating "History" button (bottom-right, `fixed position`) opens a scrollable modal listing the 50 most recent `Notification` records for the object.
|
|
112
|
+
- **Added**: Each history entry displays: action badge (Create/Update/Delete/Restore), actor avatar + username, timestamp, and an **old → new diff** for every changed field using `changed_data` from the `Notification` model.
|
|
113
|
+
- **Added**: `has_view_history_permission()` on `ModelAdmin` — panel is shown only to superusers or users with `change` permission; read-only staff cannot see history.
|
|
114
|
+
- **Added**: `show_history = True` class attribute on `ModelAdmin` — subclasses can opt out per model by setting `show_history = False`.
|
|
115
|
+
- **Fixed**: DaisyUI v5 `ui-` prefix applied correctly to `ui-modal`, `ui-modal-box`, `ui-modal-backdrop`, `ui-btn` — resolves invisible/broken modal.
|
|
116
|
+
- **Fixed**: Trigger `<button type="button">` — prevents accidental form submission when clicking the History button.
|
|
117
|
+
- **Fixed**: Close button and backdrop use `dialog.close()` via JS — avoids nested `<form method="dialog">` inside the admin form (HTML spec violation that caused the button to escape DOM).
|
|
118
|
+
- **Fixed**: UNFOLD's built-in "Lịch sử" button hidden via `{% block object-tools-items %}{% endblock %}` — eliminates duplicate history link in the toolbar.
|
|
119
|
+
- **Improved**: `import ast` and `reverse` moved to module-level imports in `modeladmin.py`.
|
|
120
|
+
- **Improved**: All panel strings wrapped in `{% trans %}` for full i18n support.
|
|
121
|
+
- **Improved**: `change_list.html` — pagination state (`p=...`) now preserved in `sessionStorage` alongside filters; navigating back from a change form restores the correct page instead of jumping to page 1.
|
|
122
|
+
|
|
123
|
+
### v0.2.47 (2026-04-05)
|
|
124
|
+
**Feature: M2M change tracking, zero-DB App dashboard, dual-view UI**
|
|
125
|
+
- **Added**: `m2m_changed` signal handler for all concrete `BaseModel` subclasses — `.add()`, `.remove()`, `.set()`, `.clear()` on any ManyToMany field now correctly updates `updated_at`/`updated_by` and sends a notification to superusers with the same `changed_data` format as `save()`.
|
|
126
|
+
- **Added**: App dashboard — two toggle-able view modes: **grid** (2-level category → app cards) and **list** (flat grouped), persisted via `localStorage`.
|
|
127
|
+
- **Removed**: `init_app_db()` DB-write mechanism — App dashboard now reads directly from `settings.UNFOLD['SIDEBAR']` via a new `_parse_sidebar_apps()` helper; zero DB writes on page load.
|
|
128
|
+
- **Fixed**: `app_badge_callback()` counted from stale DB; now counts from `settings.UNFOLD['SIDEBAR']` — badge is always in sync with config.
|
|
129
|
+
- **Fixed**: `has_permission()` in `AppAdmin` now handles callable UNFOLD permissions (lambdas) in addition to dotted-string imports.
|
|
130
|
+
- **Fixed**: Protocol-relative URLs (`//cdn…`) in app icons/thumbnails were incorrectly routed to `{% static %}`; added `|slice:":2" == "//"` check across all render paths.
|
|
131
|
+
- **Fixed**: Mosaic `+N` counter (categories with >4 apps) correctly shows 3 icons + overflow count.
|
|
132
|
+
- **Improved**: Pagination and filter bars hidden on App dashboard via `{% block pagination %}` and `{% block filters %}` override.
|
|
133
|
+
- **Improved**: `pyproject.toml` — `package-data` reduced from 14 lines to 3; removed unused `pyasn1` dep, dead `[tool.uv.workspace]`, empty `[tool.setuptools]`; fixed `Framework :: Django :: 5.2` classifier.
|
|
134
|
+
|
|
135
|
+
### v0.2.46 (2026-04-04)
|
|
136
|
+
**Fix: Email template — logo URL, company name, and contact details**
|
|
137
|
+
- **Fixed**: Logo URL updated from `api.logo.com` to stable Google-hosted URL.
|
|
138
|
+
- **Fixed**: Company name updated to `White Neuron Co., Ltd` (official name).
|
|
139
|
+
- **Fixed**: Title updated to `Founder & CTO`.
|
|
140
|
+
- **Fixed**: Website URL in signature changed to full `https://whiteneuron.ai`.
|
|
141
|
+
|
|
142
|
+
### v0.2.45 (2026-04-03)
|
|
110
143
|
**Security: 11 CVE fixes across django, pillow, cryptography, pyasn1**
|
|
111
144
|
- **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
145
|
- **Security**: `pillow` lower bound raised from `>=11.0.0` to `>=12.1.1` — patches heap buffer overflow (CVE-2026-25990).
|
|
@@ -11,7 +11,7 @@ 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.48
|
|
15
15
|
|
|
16
16
|
## Compatibility
|
|
17
17
|
|
|
@@ -22,7 +22,40 @@ A modern Django Admin extension focused on UI/UX, dashboard, feedback, file mana
|
|
|
22
22
|
|
|
23
23
|
## Changelog
|
|
24
24
|
|
|
25
|
-
### v0.2.
|
|
25
|
+
### v0.2.48 (2026-04-06) — latest
|
|
26
|
+
**Feature: Change history panel — modal UI, M2M diff, permission gate**
|
|
27
|
+
- **Added**: Change history panel shown on every change-form — floating "History" button (bottom-right, `fixed position`) opens a scrollable modal listing the 50 most recent `Notification` records for the object.
|
|
28
|
+
- **Added**: Each history entry displays: action badge (Create/Update/Delete/Restore), actor avatar + username, timestamp, and an **old → new diff** for every changed field using `changed_data` from the `Notification` model.
|
|
29
|
+
- **Added**: `has_view_history_permission()` on `ModelAdmin` — panel is shown only to superusers or users with `change` permission; read-only staff cannot see history.
|
|
30
|
+
- **Added**: `show_history = True` class attribute on `ModelAdmin` — subclasses can opt out per model by setting `show_history = False`.
|
|
31
|
+
- **Fixed**: DaisyUI v5 `ui-` prefix applied correctly to `ui-modal`, `ui-modal-box`, `ui-modal-backdrop`, `ui-btn` — resolves invisible/broken modal.
|
|
32
|
+
- **Fixed**: Trigger `<button type="button">` — prevents accidental form submission when clicking the History button.
|
|
33
|
+
- **Fixed**: Close button and backdrop use `dialog.close()` via JS — avoids nested `<form method="dialog">` inside the admin form (HTML spec violation that caused the button to escape DOM).
|
|
34
|
+
- **Fixed**: UNFOLD's built-in "Lịch sử" button hidden via `{% block object-tools-items %}{% endblock %}` — eliminates duplicate history link in the toolbar.
|
|
35
|
+
- **Improved**: `import ast` and `reverse` moved to module-level imports in `modeladmin.py`.
|
|
36
|
+
- **Improved**: All panel strings wrapped in `{% trans %}` for full i18n support.
|
|
37
|
+
- **Improved**: `change_list.html` — pagination state (`p=...`) now preserved in `sessionStorage` alongside filters; navigating back from a change form restores the correct page instead of jumping to page 1.
|
|
38
|
+
|
|
39
|
+
### v0.2.47 (2026-04-05)
|
|
40
|
+
**Feature: M2M change tracking, zero-DB App dashboard, dual-view UI**
|
|
41
|
+
- **Added**: `m2m_changed` signal handler for all concrete `BaseModel` subclasses — `.add()`, `.remove()`, `.set()`, `.clear()` on any ManyToMany field now correctly updates `updated_at`/`updated_by` and sends a notification to superusers with the same `changed_data` format as `save()`.
|
|
42
|
+
- **Added**: App dashboard — two toggle-able view modes: **grid** (2-level category → app cards) and **list** (flat grouped), persisted via `localStorage`.
|
|
43
|
+
- **Removed**: `init_app_db()` DB-write mechanism — App dashboard now reads directly from `settings.UNFOLD['SIDEBAR']` via a new `_parse_sidebar_apps()` helper; zero DB writes on page load.
|
|
44
|
+
- **Fixed**: `app_badge_callback()` counted from stale DB; now counts from `settings.UNFOLD['SIDEBAR']` — badge is always in sync with config.
|
|
45
|
+
- **Fixed**: `has_permission()` in `AppAdmin` now handles callable UNFOLD permissions (lambdas) in addition to dotted-string imports.
|
|
46
|
+
- **Fixed**: Protocol-relative URLs (`//cdn…`) in app icons/thumbnails were incorrectly routed to `{% static %}`; added `|slice:":2" == "//"` check across all render paths.
|
|
47
|
+
- **Fixed**: Mosaic `+N` counter (categories with >4 apps) correctly shows 3 icons + overflow count.
|
|
48
|
+
- **Improved**: Pagination and filter bars hidden on App dashboard via `{% block pagination %}` and `{% block filters %}` override.
|
|
49
|
+
- **Improved**: `pyproject.toml` — `package-data` reduced from 14 lines to 3; removed unused `pyasn1` dep, dead `[tool.uv.workspace]`, empty `[tool.setuptools]`; fixed `Framework :: Django :: 5.2` classifier.
|
|
50
|
+
|
|
51
|
+
### v0.2.46 (2026-04-04)
|
|
52
|
+
**Fix: Email template — logo URL, company name, and contact details**
|
|
53
|
+
- **Fixed**: Logo URL updated from `api.logo.com` to stable Google-hosted URL.
|
|
54
|
+
- **Fixed**: Company name updated to `White Neuron Co., Ltd` (official name).
|
|
55
|
+
- **Fixed**: Title updated to `Founder & CTO`.
|
|
56
|
+
- **Fixed**: Website URL in signature changed to full `https://whiteneuron.ai`.
|
|
57
|
+
|
|
58
|
+
### v0.2.45 (2026-04-03)
|
|
26
59
|
**Security: 11 CVE fixes across django, pillow, cryptography, pyasn1**
|
|
27
60
|
- **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
61
|
- **Security**: `pillow` lower bound raised from `>=11.0.0` to `>=12.1.1` — patches heap buffer overflow (CVE-2026-25990).
|
{django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/django_whiteneuron.egg-info/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: django-whiteneuron
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.48
|
|
4
4
|
Summary: A modern Django Admin extension with enhanced UI/UX, dashboard, feedback, file management, rate limiting, IP blacklisting, and advanced admin integrations built on django-unfold.
|
|
5
5
|
Author-email: White Neuron <anhnt@whiteneuron.ai>
|
|
6
6
|
License: MIT License
|
|
@@ -95,7 +95,7 @@ A modern Django Admin extension focused on UI/UX, dashboard, feedback, file mana
|
|
|
95
95
|
|
|
96
96
|
## Current Version
|
|
97
97
|
|
|
98
|
-
- 0.2.
|
|
98
|
+
- 0.2.48
|
|
99
99
|
|
|
100
100
|
## Compatibility
|
|
101
101
|
|
|
@@ -106,7 +106,40 @@ A modern Django Admin extension focused on UI/UX, dashboard, feedback, file mana
|
|
|
106
106
|
|
|
107
107
|
## Changelog
|
|
108
108
|
|
|
109
|
-
### v0.2.
|
|
109
|
+
### v0.2.48 (2026-04-06) — latest
|
|
110
|
+
**Feature: Change history panel — modal UI, M2M diff, permission gate**
|
|
111
|
+
- **Added**: Change history panel shown on every change-form — floating "History" button (bottom-right, `fixed position`) opens a scrollable modal listing the 50 most recent `Notification` records for the object.
|
|
112
|
+
- **Added**: Each history entry displays: action badge (Create/Update/Delete/Restore), actor avatar + username, timestamp, and an **old → new diff** for every changed field using `changed_data` from the `Notification` model.
|
|
113
|
+
- **Added**: `has_view_history_permission()` on `ModelAdmin` — panel is shown only to superusers or users with `change` permission; read-only staff cannot see history.
|
|
114
|
+
- **Added**: `show_history = True` class attribute on `ModelAdmin` — subclasses can opt out per model by setting `show_history = False`.
|
|
115
|
+
- **Fixed**: DaisyUI v5 `ui-` prefix applied correctly to `ui-modal`, `ui-modal-box`, `ui-modal-backdrop`, `ui-btn` — resolves invisible/broken modal.
|
|
116
|
+
- **Fixed**: Trigger `<button type="button">` — prevents accidental form submission when clicking the History button.
|
|
117
|
+
- **Fixed**: Close button and backdrop use `dialog.close()` via JS — avoids nested `<form method="dialog">` inside the admin form (HTML spec violation that caused the button to escape DOM).
|
|
118
|
+
- **Fixed**: UNFOLD's built-in "Lịch sử" button hidden via `{% block object-tools-items %}{% endblock %}` — eliminates duplicate history link in the toolbar.
|
|
119
|
+
- **Improved**: `import ast` and `reverse` moved to module-level imports in `modeladmin.py`.
|
|
120
|
+
- **Improved**: All panel strings wrapped in `{% trans %}` for full i18n support.
|
|
121
|
+
- **Improved**: `change_list.html` — pagination state (`p=...`) now preserved in `sessionStorage` alongside filters; navigating back from a change form restores the correct page instead of jumping to page 1.
|
|
122
|
+
|
|
123
|
+
### v0.2.47 (2026-04-05)
|
|
124
|
+
**Feature: M2M change tracking, zero-DB App dashboard, dual-view UI**
|
|
125
|
+
- **Added**: `m2m_changed` signal handler for all concrete `BaseModel` subclasses — `.add()`, `.remove()`, `.set()`, `.clear()` on any ManyToMany field now correctly updates `updated_at`/`updated_by` and sends a notification to superusers with the same `changed_data` format as `save()`.
|
|
126
|
+
- **Added**: App dashboard — two toggle-able view modes: **grid** (2-level category → app cards) and **list** (flat grouped), persisted via `localStorage`.
|
|
127
|
+
- **Removed**: `init_app_db()` DB-write mechanism — App dashboard now reads directly from `settings.UNFOLD['SIDEBAR']` via a new `_parse_sidebar_apps()` helper; zero DB writes on page load.
|
|
128
|
+
- **Fixed**: `app_badge_callback()` counted from stale DB; now counts from `settings.UNFOLD['SIDEBAR']` — badge is always in sync with config.
|
|
129
|
+
- **Fixed**: `has_permission()` in `AppAdmin` now handles callable UNFOLD permissions (lambdas) in addition to dotted-string imports.
|
|
130
|
+
- **Fixed**: Protocol-relative URLs (`//cdn…`) in app icons/thumbnails were incorrectly routed to `{% static %}`; added `|slice:":2" == "//"` check across all render paths.
|
|
131
|
+
- **Fixed**: Mosaic `+N` counter (categories with >4 apps) correctly shows 3 icons + overflow count.
|
|
132
|
+
- **Improved**: Pagination and filter bars hidden on App dashboard via `{% block pagination %}` and `{% block filters %}` override.
|
|
133
|
+
- **Improved**: `pyproject.toml` — `package-data` reduced from 14 lines to 3; removed unused `pyasn1` dep, dead `[tool.uv.workspace]`, empty `[tool.setuptools]`; fixed `Framework :: Django :: 5.2` classifier.
|
|
134
|
+
|
|
135
|
+
### v0.2.46 (2026-04-04)
|
|
136
|
+
**Fix: Email template — logo URL, company name, and contact details**
|
|
137
|
+
- **Fixed**: Logo URL updated from `api.logo.com` to stable Google-hosted URL.
|
|
138
|
+
- **Fixed**: Company name updated to `White Neuron Co., Ltd` (official name).
|
|
139
|
+
- **Fixed**: Title updated to `Founder & CTO`.
|
|
140
|
+
- **Fixed**: Website URL in signature changed to full `https://whiteneuron.ai`.
|
|
141
|
+
|
|
142
|
+
### v0.2.45 (2026-04-03)
|
|
110
143
|
**Security: 11 CVE fixes across django, pillow, cryptography, pyasn1**
|
|
111
144
|
- **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
145
|
- **Security**: `pillow` lower bound raised from `>=11.0.0` to `>=12.1.1` — patches heap buffer overflow (CVE-2026-25990).
|
{django_whiteneuron-0.2.47 → django_whiteneuron-0.2.48}/django_whiteneuron.egg-info/SOURCES.txt
RENAMED
|
@@ -164,6 +164,8 @@ whiteneuron/templates/admin/login.html
|
|
|
164
164
|
whiteneuron/templates/admin/login_after.html
|
|
165
165
|
whiteneuron/templates/admin/search_form.html
|
|
166
166
|
whiteneuron/templates/admin/submit_line.html
|
|
167
|
+
whiteneuron/templates/admin/wn_change_form.html
|
|
168
|
+
whiteneuron/templates/admin/wn_history_panel.html
|
|
167
169
|
whiteneuron/templates/admin/auth/user/add_form.html
|
|
168
170
|
whiteneuron/templates/admin/base/app_change_list.html
|
|
169
171
|
whiteneuron/templates/admin/base/image_change_form.html
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "django-whiteneuron"
|
|
3
|
-
version = "0.2.
|
|
3
|
+
version = "0.2.48"
|
|
4
4
|
description = "A modern Django Admin extension with enhanced UI/UX, dashboard, feedback, file management, rate limiting, IP blacklisting, and advanced admin integrations built on django-unfold."
|
|
5
5
|
authors = [
|
|
6
6
|
{name = "White Neuron", email = "anhnt@whiteneuron.ai"}
|
|
@@ -9,7 +9,8 @@ from django.contrib.admin.views.main import ChangeList
|
|
|
9
9
|
from collections import OrderedDict
|
|
10
10
|
from typing import Any, Sequence
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
import ast
|
|
13
|
+
from django.urls import reverse, reverse_lazy
|
|
13
14
|
|
|
14
15
|
from unfold.contrib.forms.widgets import WysiwygWidget as UnfoldWysiwygWidget
|
|
15
16
|
from whiteneuron.base.widgets import WysiwygWidget, CKEditor5Widget
|
|
@@ -49,6 +50,9 @@ def get_verbose_name_field(model, field):
|
|
|
49
50
|
|
|
50
51
|
class ModelAdmin(UnfoldAdmin):
|
|
51
52
|
|
|
53
|
+
show_history = True # Show history panel in change form, data from Notification model
|
|
54
|
+
change_form_template = 'admin/wn_change_form.html'
|
|
55
|
+
|
|
52
56
|
# MAX OBJECTS PER PAGE
|
|
53
57
|
list_per_page = 50
|
|
54
58
|
|
|
@@ -205,63 +209,6 @@ class ModelAdmin(UnfoldAdmin):
|
|
|
205
209
|
flag= 'danger',
|
|
206
210
|
action= 'delete',
|
|
207
211
|
content= content_html)
|
|
208
|
-
|
|
209
|
-
# def save_model(self, request, obj, form, change):
|
|
210
|
-
# save created_by and updated_by when create or update
|
|
211
|
-
# title= ''
|
|
212
|
-
# content_html= ''
|
|
213
|
-
# action= ''
|
|
214
|
-
# user= request.user
|
|
215
|
-
# obj_link= f"/admin/{obj._meta.app_label}/{obj._meta.model_name}/{obj.id}/change/"
|
|
216
|
-
# if not change: # check if the object is being created
|
|
217
|
-
# obj.updated_by= user
|
|
218
|
-
# obj.created_by= user
|
|
219
|
-
# action= 'create'
|
|
220
|
-
# title= f"New {obj._meta.verbose_name} \"{obj}\" has been created by user \"{obj.created_by}\""
|
|
221
|
-
# content_html= f"New {obj._meta.verbose_name} <a href='{obj_link}'>{obj}</a> has been created by user \"{obj.created_by}\""
|
|
222
|
-
|
|
223
|
-
# # check if the object is being updated with a new version
|
|
224
|
-
# else:
|
|
225
|
-
# fields_changed= []
|
|
226
|
-
# isUpdate= False
|
|
227
|
-
# if hasattr(obj.__class__, 'objects_all'):
|
|
228
|
-
# old= obj.__class__.objects_all.get(pk= obj.pk)
|
|
229
|
-
# else:
|
|
230
|
-
# old= obj.__class__.objects.get(pk= obj.pk)
|
|
231
|
-
|
|
232
|
-
# for attr in obj.__dict__:
|
|
233
|
-
# if attr in ['_state', 'created_at', 'created_by', 'updated_at', 'updated_by']:
|
|
234
|
-
# continue
|
|
235
|
-
# # kiểm tra attr có phải là field không
|
|
236
|
-
# if not hasattr(obj.__class__, attr):
|
|
237
|
-
# continue
|
|
238
|
-
# # print(attr)
|
|
239
|
-
# try:
|
|
240
|
-
# if getattr(obj, attr) != getattr(old, attr):
|
|
241
|
-
# isUpdate= True
|
|
242
|
-
# fields_changed.append((attr, getattr(old, attr), getattr(obj, attr)))
|
|
243
|
-
# except Exception as e:
|
|
244
|
-
# print(e)
|
|
245
|
-
# pass
|
|
246
|
-
# if isUpdate:
|
|
247
|
-
# obj.updated_by= user
|
|
248
|
-
# action= 'update'
|
|
249
|
-
# title= f"{obj._meta.verbose_name} \"{obj}\" has been updated by user \"{obj.updated_by}\""
|
|
250
|
-
# content_html= f"{obj._meta.verbose_name} \"{obj}\" has been updated by user \"{obj.updated_by}\" with the following changes: <ul>"
|
|
251
|
-
# for field in fields_changed:
|
|
252
|
-
# content_html+= f"<li>{field[0].verbose_name if hasattr(field[0], 'verbose_name') else field[0]}: {field[1]} -> {field[2]}</li>"
|
|
253
|
-
# content_html+= "</ul>"
|
|
254
|
-
# super().save_model(request, obj, form, change)
|
|
255
|
-
# send notification to superuser when create or update successfully
|
|
256
|
-
# if title:
|
|
257
|
-
# for user in User.objects.filter(is_superuser= True):
|
|
258
|
-
# obj= Notification.objects.create(user= user, title= title,
|
|
259
|
-
# action_by= obj.updated_by if action == 'update' else obj.created_by,
|
|
260
|
-
# flag= 'info',
|
|
261
|
-
# action= action,
|
|
262
|
-
# obj_link= obj_link,
|
|
263
|
-
# content= content_html)
|
|
264
|
-
# obj.alert(request) # send alert to user
|
|
265
212
|
|
|
266
213
|
|
|
267
214
|
|
|
@@ -504,7 +451,12 @@ class ModelAdmin(UnfoldAdmin):
|
|
|
504
451
|
res = super().changelist_view(request, extra_context)
|
|
505
452
|
return res
|
|
506
453
|
|
|
507
|
-
def
|
|
454
|
+
def has_view_history_permission(self, request, obj=None):
|
|
455
|
+
if request.user.is_superuser:
|
|
456
|
+
return True
|
|
457
|
+
return self.has_change_permission(request, obj)
|
|
458
|
+
|
|
459
|
+
def changeform_view(self, request, object_id=None, form_url="", extra_context=None):
|
|
508
460
|
extra_context = extra_context or {}
|
|
509
461
|
extra_context["sidebar_default"] = self.default_toggle_sidebar
|
|
510
462
|
extra_context["sidebar_model_key"] = (
|
|
@@ -512,6 +464,31 @@ class ModelAdmin(UnfoldAdmin):
|
|
|
512
464
|
)
|
|
513
465
|
if self.default_toggle_sidebar is not None:
|
|
514
466
|
request.session["toggle_sidebar"] = self.default_toggle_sidebar
|
|
467
|
+
|
|
468
|
+
can_view_history = self.show_history and self.has_view_history_permission(request)
|
|
469
|
+
extra_context['show_history'] = can_view_history
|
|
470
|
+
if can_view_history and object_id:
|
|
471
|
+
try:
|
|
472
|
+
obj_link = reverse(
|
|
473
|
+
'admin:%s_%s_change' % (self.model._meta.app_label, self.model._meta.model_name),
|
|
474
|
+
args=[object_id],
|
|
475
|
+
)
|
|
476
|
+
notifications = (
|
|
477
|
+
Notification.objects
|
|
478
|
+
.filter(obj_link=obj_link)
|
|
479
|
+
.select_related('action_by')
|
|
480
|
+
.order_by('-created_at')[:50]
|
|
481
|
+
)
|
|
482
|
+
# Parse changed_data string → list of dicts for each notification
|
|
483
|
+
for notif in notifications:
|
|
484
|
+
try:
|
|
485
|
+
notif.parsed_changed_data = ast.literal_eval(notif.changed_data) if notif.changed_data else []
|
|
486
|
+
except Exception:
|
|
487
|
+
notif.parsed_changed_data = []
|
|
488
|
+
extra_context['history_notifications'] = notifications
|
|
489
|
+
except Exception:
|
|
490
|
+
extra_context['history_notifications'] = []
|
|
491
|
+
|
|
515
492
|
return super().changeform_view(request, object_id, form_url, extra_context)
|
|
516
493
|
|
|
517
494
|
# GridItemHeader
|
|
@@ -504,14 +504,37 @@ def _base_model_m2m_changed(sender, instance, action, pk_set, model, **kwargs):
|
|
|
504
504
|
|
|
505
505
|
# ── Build change description (same format as save()) ──
|
|
506
506
|
pk_list = list(pk_set or [])
|
|
507
|
+
|
|
508
|
+
def _labels_from_qs(qs):
|
|
509
|
+
"""Return list of '__str__ (pk)' for a queryset."""
|
|
510
|
+
return [f"{o} ({o.pk})" for o in qs]
|
|
511
|
+
|
|
512
|
+
def _resolve_labels(pks):
|
|
513
|
+
"""Return list of '__str__ (pk)' for pk list, fallback to pk on error."""
|
|
514
|
+
try:
|
|
515
|
+
objs = {o.pk: o for o in model.objects.filter(pk__in=pks)}
|
|
516
|
+
return [f"{objs[pk]} ({pk})" if pk in objs else str(pk) for pk in pks]
|
|
517
|
+
except Exception:
|
|
518
|
+
return [str(pk) for pk in pks]
|
|
519
|
+
|
|
520
|
+
# current state (post-signal, so add already happened / remove already happened)
|
|
521
|
+
try:
|
|
522
|
+
current_qs = getattr(instance, field_name).all()
|
|
523
|
+
current_labels = _labels_from_qs(current_qs)
|
|
524
|
+
except Exception:
|
|
525
|
+
current_labels = []
|
|
526
|
+
|
|
507
527
|
if action == 'post_add':
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
528
|
+
added_labels = _resolve_labels(pk_list)
|
|
529
|
+
added_set = set(pk_list)
|
|
530
|
+
old_value = [lbl for lbl in current_labels if not any(f"({pk})" in lbl for pk in added_set)]
|
|
531
|
+
new_value = current_labels
|
|
532
|
+
desc = f"{_('added')}: {added_labels}"
|
|
511
533
|
elif action == 'post_remove':
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
534
|
+
removed_labels = _resolve_labels(pk_list)
|
|
535
|
+
old_value = current_labels + removed_labels
|
|
536
|
+
new_value = current_labels
|
|
537
|
+
desc = f"{_('removed')}: {removed_labels}"
|
|
515
538
|
else: # post_clear
|
|
516
539
|
old_value = '(all)'
|
|
517
540
|
new_value = []
|
|
Binary file
|