django-microsys 2.2.2__tar.gz → 2.2.3__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_microsys-2.2.2 → django_microsys-2.2.3}/PKG-INFO +1 -1
- {django_microsys-2.2.2 → django_microsys-2.2.3}/django_microsys.egg-info/PKG-INFO +1 -1
- django_microsys-2.2.3/microsys/VERSION +1 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/utils.py +66 -63
- django_microsys-2.2.2/microsys/VERSION +0 -1
- {django_microsys-2.2.2 → django_microsys-2.2.3}/LICENSE +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/MANIFEST.in +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/README.md +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/django_microsys.egg-info/SOURCES.txt +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/django_microsys.egg-info/dependency_links.txt +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/django_microsys.egg-info/entry_points.txt +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/django_microsys.egg-info/requires.txt +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/django_microsys.egg-info/top_level.txt +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/__init__.py +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/__main__.py +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/admin.py +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/api.py +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/apps.py +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/cli.py +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/constants.py +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/context_processors.py +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/discovery.py +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/fetcher.py +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/filters.py +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/fonts.py +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/formats/__init__.py +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/formats/ar/__init__.py +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/formats/ar/formats.py +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/formats/en/__init__.py +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/formats/en/formats.py +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/forms.py +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/guards.py +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/management/commands/__init__.py +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/management/commands/microsys_check.py +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/management/commands/microsys_setup.py +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/management/commands/migrator.py +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/management/commands/seed_activity_log.py +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/managers.py +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/middleware.py +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/migrations/0001_initial.py +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/migrations/0002_public_registration.py +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/migrations/0003_public_root_split.py +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/migrations/0004_client_ip_and_trusted_devices.py +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/migrations/0005_systemsettings_allow_user_font_override_and_more.py +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/migrations/__init__.py +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/models.py +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/patches.py +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/registration.py +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/scaffold.py +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/scaffold_templates/app/README.md.tmpl +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/scaffold_templates/app/__init__.py.tmpl +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/scaffold_templates/app/apps.py.tmpl +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/scaffold_templates/app/filters.py.tmpl +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/scaffold_templates/app/forms.py.tmpl +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/scaffold_templates/app/migrations/__init__.py.tmpl +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/scaffold_templates/app/models.py.tmpl +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/scaffold_templates/app/tables.py.tmpl +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/scaffold_templates/app/templates/example_record_confirm_delete.html.tmpl +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/scaffold_templates/app/templates/example_record_detail.html.tmpl +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/scaffold_templates/app/templates/example_record_form.html.tmpl +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/scaffold_templates/app/templates/example_record_list.html.tmpl +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/scaffold_templates/app/tests/__init__.py.tmpl +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/scaffold_templates/app/tests/test_app.py.tmpl +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/scaffold_templates/app/translations.py.tmpl +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/scaffold_templates/app/urls.py.tmpl +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/scaffold_templates/app/views.py.tmpl +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/scaffold_templates/project/.nginx/nginx.conf.tmpl +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/scaffold_templates/project/.secrets/.env.tmpl +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/scaffold_templates/project/Dockerfile.tmpl +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/scaffold_templates/project/README.md.tmpl +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/scaffold_templates/project/compose.dev.yml.tmpl +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/scaffold_templates/project/compose.yml.tmpl +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/scaffold_templates/project/dockerignore.tmpl +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/scaffold_templates/project/docs/README.md.tmpl +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/scaffold_templates/project/entrypoint.sh.tmpl +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/scaffold_templates/project/gitattributes.tmpl +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/scaffold_templates/project/gitignore.tmpl +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/scaffold_templates/project/gunicorn.py.tmpl +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/scaffold_templates/project/manage.py.tmpl +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/scaffold_templates/project/package/__init__.py.tmpl +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/scaffold_templates/project/package/asgi.py.tmpl +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/scaffold_templates/project/package/celery.py.tmpl +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/scaffold_templates/project/package/settings.py.tmpl +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/scaffold_templates/project/package/urls.py.tmpl +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/scaffold_templates/project/package/wsgi.py.tmpl +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/scaffold_templates/project/req.txt.tmpl +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/scaffold_templates/project/start.ps1.tmpl +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/scaffold_templates/project/start.sh.tmpl +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/scaffold_templates/project/tests/__init__.py.tmpl +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/scaffold_templates/project/tests/test_scaffold.py.tmpl +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/scaffold_templates/project/tools/smtp_relay.py.tmpl +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/signals.py +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/bootstrap/bootstrap-icons.css +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/bootstrap/bootstrap-icons.woff +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/bootstrap/bootstrap-icons.woff2 +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/bootstrap/bootstrap.bundle.min.js +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/bootstrap/bootstrap.bundle.min.js.map +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/bootstrap/bootstrap.min.css +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/bootstrap/bootstrap.min.css.map +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/bootstrap/bootstrap.rtl.min.css +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/bootstrap/bootstrap.rtl.min.css.map +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/favicon.ico +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/img/base_logo.webp +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/img/default_profile.webp +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/img/login_logo.webp +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/accessibility/css/main.css +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/accessibility/js/main.js +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/activitylog/js/main.js +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/fonts/TwemojiCountryFlags.woff2 +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/fonts/cairo-bold.woff2 +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/fonts/cairo-medium.woff2 +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/fonts/cairo-regular.woff2 +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/fonts/shabwa-bold.woff2 +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/fonts/shabwa-medium.woff2 +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/fonts/shabwa-regular.woff2 +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/forms/css/file_field.css +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/forms/css/form_actions.css +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/forms/css/form_fields.css +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/forms/js/file_field.js +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/forms/js/filter_form.js +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/helpers/autofill/js/main.js +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/helpers/context_menu/css/main.css +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/helpers/context_menu/js/main.js +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/helpers/context_menu/js/section_manager.js +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/helpers/dynamic_modal/js/main.js +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/helpers/prevent_double_submit.js +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/helpers/scan_link/js/main.js +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/helpers/scan_link/js/scan_button.js +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/helpers/wizard/js/main.js +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/language/css/main.css +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/language/js/main.js +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/language/js/translations.js +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/main/css/buttons.css +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/main/css/dropdowns.css +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/main/css/index_cards.css +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/main/css/main.css +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/main/css/options.css +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/main/css/pagination.css +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/main/css/selectors.css +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/main/css/system_setup.css +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/main/css/tables.css +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/main/css/template_cleanup.css +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/main/css/titlebar.css +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/main/js/base_head.js +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/main/js/base_runtime.js +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/main/js/options.js +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/main/js/selectors.js +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/main/js/system_setup.js +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/main/js/tables.js +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/sections/js/manage_sections.js +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/sidebar/css/main.css +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/sidebar/css/reorder.css +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/sidebar/css/theme_picker.css +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/sidebar/js/main.js +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/sidebar/js/preload.js +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/sidebar/js/reorder.js +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/sidebar/js/theme_picker.js +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/themes/css/blue.css +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/themes/css/dark.css +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/themes/css/gold.css +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/themes/css/gothic.css +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/themes/css/green.css +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/themes/css/light.css +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/themes/css/mono.css +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/themes/css/neon.css +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/themes/css/red.css +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/themes/css/retro.css +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/themes/js/main.js +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/tutorial/css/main.css +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/tutorial/js/driver.js +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/tutorial/js/main.js +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/users/css/login.css +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/users/css/permissions.css +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/users/css/profile.css +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/users/css/user_hub.css +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/users/js/login.js +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/users/js/manage_users.js +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/users/js/permissions.js +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/users/js/profile_2fa.js +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/users/js/profile_image_widget.js +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/users/js/twofa_verify.js +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/microsys/users/js/user_hub.js +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/vanillajs-datepicker/datepicker-bs5.min.css +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/static/vanillajs-datepicker/datepicker.min.js +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/tables.py +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/templates/bootstrap5/layout/field_file.html +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/templates/microsys/2fa/verify.html +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/templates/microsys/activitylog/activity_log.html +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/templates/microsys/activitylog/activity_log_detail_modal.html +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/templates/microsys/base.html +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/templates/microsys/form_base.html +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/templates/microsys/forms/assets_head.html +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/templates/microsys/forms/assets_scripts.html +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/templates/microsys/forms/crispy_file_field.html +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/templates/microsys/forms/file_input.html +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/templates/microsys/forms/filter_assets_head.html +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/templates/microsys/forms/filter_assets_scripts.html +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/templates/microsys/helpers/dynamic_modal.html +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/templates/microsys/helpers/dynamic_modal_combined.html +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/templates/microsys/helpers/dynamic_modal_detail.html +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/templates/microsys/helpers/dynamic_modal_form.html +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/templates/microsys/helpers/dynamic_modal_list.html +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/templates/microsys/helpers/micro_context_menu.html +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/templates/microsys/includes/font_previews.html +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/templates/microsys/includes/font_settings_matrix.html +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/templates/microsys/includes/language_catalog_editor.html +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/templates/microsys/includes/language_fonts_editor.html +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/templates/microsys/includes/language_previews.html +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/templates/microsys/includes/options.html +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/templates/microsys/includes/sidebar_builder.html +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/templates/microsys/includes/sidebar_density_previews.html +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/templates/microsys/includes/sidebar_items.html +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/templates/microsys/includes/system_names_editor.html +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/templates/microsys/includes/system_setup.html +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/templates/microsys/includes/table_density_previews.html +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/templates/microsys/includes/theme_previews.html +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/templates/microsys/includes/theme_settings_matrix.html +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/templates/microsys/includes/titlebar.html +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/templates/microsys/includes/translation_matrix_editor.html +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/templates/microsys/includes/tutorial.html +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/templates/microsys/list_base.html +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/templates/microsys/registration/pending.html +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/templates/microsys/scopes/scope_actions.html +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/templates/microsys/scopes/scope_form.html +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/templates/microsys/scopes/scope_manager.html +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/templates/microsys/sections/manage_sections.html +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/templates/microsys/sections/subsection_select.html +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/templates/microsys/sidebar/auto.html +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/templates/microsys/sidebar/extra_groups.html +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/templates/microsys/sidebar/main.html +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/templates/microsys/sidebar/tree.html +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/templates/microsys/tables/table.html +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/templates/microsys/users/grouped_permissions.html +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/templates/microsys/users/manage_users.html +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/templates/microsys/users/profile.html +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/templates/microsys/users/profile_image_widget.html +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/templates/microsys/users/user_detail_modal.html +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/templates/microsys/users/user_hub.html +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/templates/registration/email/verify_registration.txt +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/templates/registration/login.html +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/templates/registration/register.html +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/templates/registration/register_sent.html +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/templates/registration/register_verify.html +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/templatetags/__init__.py +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/templatetags/microsys_tags.py +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/templatetags/microsys_translation.py +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/templatetags/sidebar_tags.py +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/themes.py +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/translations.py +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/urls.py +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/views/__init__.py +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/views/activitylog.py +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/views/general.py +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/views/profile.py +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/views/registration.py +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/views/scopes.py +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/views/sections.py +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/views/sidebar.py +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/views/twofa.py +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/views/users.py +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/widgets.py +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/pyproject.toml +0 -0
- {django_microsys-2.2.2 → django_microsys-2.2.3}/setup.cfg +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
2.2.3
|
|
@@ -8,7 +8,6 @@ from decimal import Decimal, InvalidOperation
|
|
|
8
8
|
from functools import lru_cache
|
|
9
9
|
import inspect
|
|
10
10
|
import unicodedata
|
|
11
|
-
|
|
12
11
|
from django import forms
|
|
13
12
|
from django.apps import apps
|
|
14
13
|
from django.conf import settings
|
|
@@ -22,7 +21,6 @@ from django.http import JsonResponse
|
|
|
22
21
|
from django.core.mail import EmailMessage, get_connection, send_mail
|
|
23
22
|
from django.core.exceptions import FieldDoesNotExist
|
|
24
23
|
from django.utils.module_loading import import_string
|
|
25
|
-
|
|
26
24
|
from .constants import (
|
|
27
25
|
DEFAULT_HOME_URL,
|
|
28
26
|
DEFAULT_SIDEBAR_COLLAPSE_MODE,
|
|
@@ -49,7 +47,6 @@ try:
|
|
|
49
47
|
except ImportError:
|
|
50
48
|
django_filters = None
|
|
51
49
|
|
|
52
|
-
|
|
53
50
|
SENSITIVE_ACTIVITY_MASK = "********"
|
|
54
51
|
_SENSITIVE_ACTIVITY_FIELD_NAMES = {
|
|
55
52
|
"password",
|
|
@@ -67,7 +64,7 @@ _SENSITIVE_ACTIVITY_FIELD_NAMES = {
|
|
|
67
64
|
"apitoken",
|
|
68
65
|
}
|
|
69
66
|
|
|
70
|
-
|
|
67
|
+
# Normalize activity log model names for translation lookup
|
|
71
68
|
def normalize_activity_log_model_key(value):
|
|
72
69
|
raw = str(value or '').strip().lower()
|
|
73
70
|
if not raw:
|
|
@@ -76,11 +73,11 @@ def normalize_activity_log_model_key(value):
|
|
|
76
73
|
normalized = re.sub(r'_+', '_', normalized).strip('_')
|
|
77
74
|
return normalized
|
|
78
75
|
|
|
79
|
-
|
|
76
|
+
# Normalize string for fuzzy model matching
|
|
80
77
|
def _normalize_fuzzy_string(s):
|
|
81
78
|
return unicodedata.normalize('NFKD', str(s)).casefold() if s else ""
|
|
82
79
|
|
|
83
|
-
|
|
80
|
+
# Fuzzy Model Lookup — Builds a cached mapping of normalized model names to model classes
|
|
84
81
|
@lru_cache(maxsize=1)
|
|
85
82
|
def _get_fuzzy_model_mapping():
|
|
86
83
|
"""Builds a cached mapping of normalized model names to model classes."""
|
|
@@ -102,7 +99,7 @@ def _get_fuzzy_model_mapping():
|
|
|
102
99
|
|
|
103
100
|
return mapping
|
|
104
101
|
|
|
105
|
-
|
|
102
|
+
# Translate activity log model names using MS_TRANS
|
|
106
103
|
def translate_activity_log_model_name(value, strings=None):
|
|
107
104
|
if not value:
|
|
108
105
|
return ""
|
|
@@ -122,7 +119,7 @@ def translate_activity_log_model_name(value, strings=None):
|
|
|
122
119
|
return s[key]
|
|
123
120
|
return value
|
|
124
121
|
|
|
125
|
-
|
|
122
|
+
# Coerce a setting value to a list
|
|
126
123
|
def _coerce_list_setting(scope, key):
|
|
127
124
|
value = scope.get(key)
|
|
128
125
|
if value is None:
|
|
@@ -132,7 +129,7 @@ def _coerce_list_setting(scope, key):
|
|
|
132
129
|
scope[key] = value
|
|
133
130
|
return value
|
|
134
131
|
|
|
135
|
-
|
|
132
|
+
# Insert middleware at specified position, removing duplicates
|
|
136
133
|
def _insert_middleware_once(middleware, middleware_path, *, after=None, before=None):
|
|
137
134
|
if middleware_path in middleware:
|
|
138
135
|
middleware.remove(middleware_path)
|
|
@@ -145,7 +142,7 @@ def _insert_middleware_once(middleware, middleware_path, *, after=None, before=N
|
|
|
145
142
|
|
|
146
143
|
middleware.insert(insert_at, middleware_path)
|
|
147
144
|
|
|
148
|
-
|
|
145
|
+
# Secret Management — Reads a Docker secret first, then falls back to an environment variable
|
|
149
146
|
def get_secret(secret_name, env_var):
|
|
150
147
|
"""Read a Docker secret first, then fall back to an environment variable."""
|
|
151
148
|
secret_path = os.path.join("/run/secrets", secret_name)
|
|
@@ -155,7 +152,6 @@ def get_secret(secret_name, env_var):
|
|
|
155
152
|
except OSError:
|
|
156
153
|
return os.getenv(env_var)
|
|
157
154
|
|
|
158
|
-
|
|
159
155
|
EMAIL_CONFIG_TRANSPORTS = {'direct', 'relay'}
|
|
160
156
|
EMAIL_CONFIG_SECRET_STORAGES = {'env', 'encrypted_db'}
|
|
161
157
|
MICROSYS_INTERNAL_SMTP_RELAY_HOST = 'smtp-relay'
|
|
@@ -173,7 +169,7 @@ CLIENT_IP_MODE_VALUES = {
|
|
|
173
169
|
CLIENT_IP_MODE_CUSTOM,
|
|
174
170
|
}
|
|
175
171
|
|
|
176
|
-
|
|
172
|
+
# Return default email configuration structure
|
|
177
173
|
def default_email_config():
|
|
178
174
|
return {
|
|
179
175
|
'transport': 'direct',
|
|
@@ -188,7 +184,7 @@ def default_email_config():
|
|
|
188
184
|
'password_configured': False,
|
|
189
185
|
}
|
|
190
186
|
|
|
191
|
-
|
|
187
|
+
# Return default client IP resolution configuration
|
|
192
188
|
def default_client_ip_config():
|
|
193
189
|
return {
|
|
194
190
|
'mode': CLIENT_IP_MODE_X_FORWARDED_FOR,
|
|
@@ -196,7 +192,7 @@ def default_client_ip_config():
|
|
|
196
192
|
'custom_header': '',
|
|
197
193
|
}
|
|
198
194
|
|
|
199
|
-
|
|
195
|
+
# Normalize custom header name for client IP resolution
|
|
200
196
|
def _normalize_client_ip_header_name(value):
|
|
201
197
|
raw_value = str(value or '').strip()
|
|
202
198
|
if not raw_value:
|
|
@@ -208,7 +204,7 @@ def _normalize_client_ip_header_name(value):
|
|
|
208
204
|
normalized = f'HTTP_{normalized}'
|
|
209
205
|
return normalized
|
|
210
206
|
|
|
211
|
-
|
|
207
|
+
# Normalize and validate client IP configuration
|
|
212
208
|
def normalize_client_ip_config(value):
|
|
213
209
|
normalized = default_client_ip_config()
|
|
214
210
|
if not isinstance(value, dict):
|
|
@@ -230,7 +226,7 @@ def normalize_client_ip_config(value):
|
|
|
230
226
|
|
|
231
227
|
return normalized
|
|
232
228
|
|
|
233
|
-
|
|
229
|
+
# Normalize and validate email configuration
|
|
234
230
|
def normalize_email_config(value, *, redact_secret=False):
|
|
235
231
|
config = value if isinstance(value, dict) else {}
|
|
236
232
|
normalized = default_email_config()
|
|
@@ -258,7 +254,7 @@ def normalize_email_config(value, *, redact_secret=False):
|
|
|
258
254
|
normalized.pop('encrypted_password', None)
|
|
259
255
|
return normalized
|
|
260
256
|
|
|
261
|
-
|
|
257
|
+
# Normalize allowed fonts list against available fonts
|
|
262
258
|
def normalize_allowed_fonts(allowed_fonts=None):
|
|
263
259
|
from .fonts import get_builtin_fonts
|
|
264
260
|
available = {f['slug'] for f in get_builtin_fonts()}
|
|
@@ -273,7 +269,7 @@ def normalize_allowed_fonts(allowed_fonts=None):
|
|
|
273
269
|
|
|
274
270
|
return normalized or list(available)
|
|
275
271
|
|
|
276
|
-
|
|
272
|
+
# Get seed key for email encryption
|
|
277
273
|
def _email_secret_seed():
|
|
278
274
|
configured_key = (
|
|
279
275
|
os.getenv('MICROSYS_EMAIL_SECRET_KEY')
|
|
@@ -283,21 +279,21 @@ def _email_secret_seed():
|
|
|
283
279
|
)
|
|
284
280
|
return str(configured_key or 'microsys-email-secret-dev-key')
|
|
285
281
|
|
|
286
|
-
|
|
282
|
+
# Get Fernet instance for email encryption
|
|
287
283
|
def _email_fernet():
|
|
288
284
|
from cryptography.fernet import Fernet
|
|
289
285
|
|
|
290
286
|
digest = hashlib.sha256(_email_secret_seed().encode('utf-8')).digest()
|
|
291
287
|
return Fernet(base64.urlsafe_b64encode(digest))
|
|
292
288
|
|
|
293
|
-
|
|
289
|
+
# Encrypt email secret using Fernet
|
|
294
290
|
def encrypt_email_secret(raw_secret):
|
|
295
291
|
raw_secret = str(raw_secret or '')
|
|
296
292
|
if not raw_secret:
|
|
297
293
|
return ''
|
|
298
294
|
return _email_fernet().encrypt(raw_secret.encode('utf-8')).decode('utf-8')
|
|
299
295
|
|
|
300
|
-
|
|
296
|
+
# Decrypt email secret using Fernet
|
|
301
297
|
def decrypt_email_secret(encrypted_secret):
|
|
302
298
|
encrypted_secret = str(encrypted_secret or '').strip()
|
|
303
299
|
if not encrypted_secret:
|
|
@@ -307,11 +303,10 @@ def decrypt_email_secret(encrypted_secret):
|
|
|
307
303
|
except Exception:
|
|
308
304
|
return ''
|
|
309
305
|
|
|
310
|
-
|
|
311
306
|
TOTP_SECRET_PREFIX = 'fernet$'
|
|
312
307
|
_UNSET = object()
|
|
313
308
|
|
|
314
|
-
|
|
309
|
+
# Get seed key for TOTP encryption
|
|
315
310
|
def _totp_secret_seed():
|
|
316
311
|
configured_key = (
|
|
317
312
|
os.getenv('MICROSYS_TOTP_SECRET_KEY')
|
|
@@ -321,18 +316,18 @@ def _totp_secret_seed():
|
|
|
321
316
|
)
|
|
322
317
|
return str(configured_key or 'microsys-totp-secret-dev-key')
|
|
323
318
|
|
|
324
|
-
|
|
319
|
+
# Get Fernet instance for TOTP encryption
|
|
325
320
|
def _totp_fernet():
|
|
326
321
|
from cryptography.fernet import Fernet
|
|
327
322
|
|
|
328
323
|
digest = hashlib.sha256(_totp_secret_seed().encode('utf-8')).digest()
|
|
329
324
|
return Fernet(base64.urlsafe_b64encode(digest))
|
|
330
325
|
|
|
331
|
-
|
|
326
|
+
# Check if TOTP secret is encrypted
|
|
332
327
|
def is_encrypted_totp_secret(value):
|
|
333
328
|
return isinstance(value, str) and value.startswith(TOTP_SECRET_PREFIX)
|
|
334
329
|
|
|
335
|
-
|
|
330
|
+
# Encrypt TOTP secret using Fernet
|
|
336
331
|
def encrypt_totp_secret(raw_secret):
|
|
337
332
|
raw_secret = str(raw_secret or '').strip()
|
|
338
333
|
if not raw_secret:
|
|
@@ -342,7 +337,7 @@ def encrypt_totp_secret(raw_secret):
|
|
|
342
337
|
encrypted = _totp_fernet().encrypt(raw_secret.encode('utf-8')).decode('utf-8')
|
|
343
338
|
return f'{TOTP_SECRET_PREFIX}{encrypted}'
|
|
344
339
|
|
|
345
|
-
|
|
340
|
+
# Decrypt TOTP secret using Fernet
|
|
346
341
|
def decrypt_totp_secret(stored_secret):
|
|
347
342
|
stored_secret = str(stored_secret or '').strip()
|
|
348
343
|
if not stored_secret:
|
|
@@ -355,11 +350,11 @@ def decrypt_totp_secret(stored_secret):
|
|
|
355
350
|
except Exception:
|
|
356
351
|
return ''
|
|
357
352
|
|
|
358
|
-
|
|
353
|
+
# Get decrypted TOTP secret from profile
|
|
359
354
|
def get_profile_totp_secret(profile):
|
|
360
355
|
return decrypt_totp_secret(getattr(profile, 'totp_secret', ''))
|
|
361
356
|
|
|
362
|
-
|
|
357
|
+
# Update profile TOTP state without full profile save
|
|
363
358
|
def set_profile_totp_state(profile, *, raw_secret=_UNSET, enabled=_UNSET):
|
|
364
359
|
if profile is None or not getattr(profile, 'pk', None):
|
|
365
360
|
raise ValueError('Profile must be saved before updating TOTP state.')
|
|
@@ -377,7 +372,7 @@ def set_profile_totp_state(profile, *, raw_secret=_UNSET, enabled=_UNSET):
|
|
|
377
372
|
setattr(profile, field_name, value)
|
|
378
373
|
return profile
|
|
379
374
|
|
|
380
|
-
|
|
375
|
+
# Get effective email configuration from settings or database
|
|
381
376
|
def get_microsys_email_config(*, include_secret=False):
|
|
382
377
|
try:
|
|
383
378
|
SystemSettings = apps.get_model('microsys', 'SystemSettings')
|
|
@@ -438,7 +433,7 @@ def get_microsys_email_config(*, include_secret=False):
|
|
|
438
433
|
'ui_hints': stored_hints,
|
|
439
434
|
}
|
|
440
435
|
|
|
441
|
-
|
|
436
|
+
# Get email service status
|
|
442
437
|
def get_email_service_status():
|
|
443
438
|
"""
|
|
444
439
|
Report whether Microsys-owned email flows are configured without touching
|
|
@@ -545,7 +540,7 @@ def get_email_service_status():
|
|
|
545
540
|
'reason': 'custom_backend_configured' if configured else 'missing_default_from_email',
|
|
546
541
|
}
|
|
547
542
|
|
|
548
|
-
|
|
543
|
+
# Send Microsys-owned transactional email
|
|
549
544
|
def send_microsys_mail(subject, message, recipient_list, *, from_email=None, fail_silently=False):
|
|
550
545
|
"""Send Microsys-owned transactional email through the selected delivery path."""
|
|
551
546
|
email_config = get_microsys_email_config(include_secret=True)
|
|
@@ -598,6 +593,7 @@ def is_sensitive_activity_field_name(field_name):
|
|
|
598
593
|
return True
|
|
599
594
|
return False
|
|
600
595
|
|
|
596
|
+
|
|
601
597
|
def microsys_settings(scope):
|
|
602
598
|
"""
|
|
603
599
|
Apply the default MicroSys settings requirements to a Django settings module.
|
|
@@ -706,6 +702,7 @@ def microsys_settings(scope):
|
|
|
706
702
|
scope["FORMAT_MODULE_PATH"] = [format_module_path, "microsys.formats"]
|
|
707
703
|
return scope
|
|
708
704
|
|
|
705
|
+
|
|
709
706
|
def _normalize_asset_url(value, fallback_base='/media/'):
|
|
710
707
|
"""Ensure stored media paths render as browser-safe absolute URLs."""
|
|
711
708
|
if not value:
|
|
@@ -888,7 +885,6 @@ def can_manage_target_user(actor, target_user=None):
|
|
|
888
885
|
target_scope = get_user_scope(target_user)
|
|
889
886
|
if actor_scope and actor_scope != target_scope:
|
|
890
887
|
return False
|
|
891
|
-
|
|
892
888
|
return True
|
|
893
889
|
|
|
894
890
|
|
|
@@ -933,7 +929,7 @@ def is_central_staff(user):
|
|
|
933
929
|
return False
|
|
934
930
|
return not user.has_perm('microsys.manage_scopes')
|
|
935
931
|
|
|
936
|
-
|
|
932
|
+
# Normalize permission codenames to a set
|
|
937
933
|
def _normalize_permission_codename_set(permission_codenames):
|
|
938
934
|
normalized = set()
|
|
939
935
|
for permission in permission_codenames or []:
|
|
@@ -1086,7 +1082,7 @@ def get_user_management_tier_state(
|
|
|
1086
1082
|
})
|
|
1087
1083
|
return tier_state
|
|
1088
1084
|
|
|
1089
|
-
|
|
1085
|
+
# Get user management tier state for a specific user
|
|
1090
1086
|
def get_user_management_tier_state_for_user(user):
|
|
1091
1087
|
if not user or not getattr(user, 'is_authenticated', False):
|
|
1092
1088
|
return get_user_management_tier_state(
|
|
@@ -1143,7 +1139,7 @@ def user_can_view_activity_log(user):
|
|
|
1143
1139
|
return True
|
|
1144
1140
|
return user.has_perm('microsys.view_activitylog') or user.has_perm('microsys.view_activity_log')
|
|
1145
1141
|
|
|
1146
|
-
|
|
1142
|
+
# Check if user has section view permission
|
|
1147
1143
|
def user_has_section_view_permission(user):
|
|
1148
1144
|
if not user or not getattr(user, 'is_authenticated', False):
|
|
1149
1145
|
return False
|
|
@@ -1152,6 +1148,7 @@ def user_has_section_view_permission(user):
|
|
|
1152
1148
|
return user.has_perm('microsys.view_sections') or user.has_perm('microsys.manage_sections')
|
|
1153
1149
|
|
|
1154
1150
|
|
|
1151
|
+
# Check if user has section manage permission
|
|
1155
1152
|
def user_has_section_manage_permission(user):
|
|
1156
1153
|
if not user or not getattr(user, 'is_authenticated', False):
|
|
1157
1154
|
return False
|
|
@@ -1216,7 +1213,6 @@ def user_has_any_permission_tokens(user, permissions, default_visible_to_all=Fal
|
|
|
1216
1213
|
permissions = [permissions]
|
|
1217
1214
|
return any(user_matches_permission_token(user, p) for p in permissions)
|
|
1218
1215
|
|
|
1219
|
-
|
|
1220
1216
|
# Activity Logging — Universal logging utility for user actions
|
|
1221
1217
|
def log_user_action(request, action, instance=None, model_name=None, details=None, number=None, object_id=None):
|
|
1222
1218
|
"""
|
|
@@ -1282,7 +1278,7 @@ DEFAULT_LANGUAGE_CATALOG = {
|
|
|
1282
1278
|
'ar': {'name': 'العربية', 'dir': 'rtl', 'flag': '🇱🇾'},
|
|
1283
1279
|
}
|
|
1284
1280
|
|
|
1285
|
-
|
|
1281
|
+
# Normalize language code to standard format
|
|
1286
1282
|
def _normalize_language_code(code):
|
|
1287
1283
|
normalized = str(code or '').strip().lower().replace('_', '-')
|
|
1288
1284
|
if not normalized:
|
|
@@ -1320,7 +1316,7 @@ def normalize_language_catalog(*layers):
|
|
|
1320
1316
|
}
|
|
1321
1317
|
return merged
|
|
1322
1318
|
|
|
1323
|
-
|
|
1319
|
+
# Normalize system names dictionary
|
|
1324
1320
|
def normalize_system_names(value):
|
|
1325
1321
|
names = {}
|
|
1326
1322
|
if isinstance(value, dict):
|
|
@@ -1331,7 +1327,7 @@ def normalize_system_names(value):
|
|
|
1331
1327
|
names[code] = name
|
|
1332
1328
|
return names
|
|
1333
1329
|
|
|
1334
|
-
|
|
1330
|
+
# Resolve system name for a given language
|
|
1335
1331
|
def resolve_system_name(system_names, lang_code=None, default_language='en'):
|
|
1336
1332
|
names = normalize_system_names(system_names)
|
|
1337
1333
|
lang = _normalize_language_code(lang_code) or _normalize_language_code(default_language) or 'en'
|
|
@@ -1344,7 +1340,7 @@ def resolve_system_name(system_names, lang_code=None, default_language='en'):
|
|
|
1344
1340
|
return value
|
|
1345
1341
|
return 'microSYS'
|
|
1346
1342
|
|
|
1347
|
-
|
|
1343
|
+
# Build grouped configuration structure
|
|
1348
1344
|
def build_config_groups(config, current_language=None):
|
|
1349
1345
|
languages = normalize_language_catalog(config.get('languages', {}))
|
|
1350
1346
|
default_language = _normalize_language_code(config.get('default_language')) or 'en'
|
|
@@ -1413,7 +1409,7 @@ def _dedupe_sidebar_entries(entries):
|
|
|
1413
1409
|
deduped.append(entry)
|
|
1414
1410
|
return deduped
|
|
1415
1411
|
|
|
1416
|
-
|
|
1412
|
+
# Return default titlebar configuration
|
|
1417
1413
|
def default_titlebar_config():
|
|
1418
1414
|
return {
|
|
1419
1415
|
'show_title': True,
|
|
@@ -1427,7 +1423,7 @@ def default_titlebar_config():
|
|
|
1427
1423
|
'surface': 'default',
|
|
1428
1424
|
}
|
|
1429
1425
|
|
|
1430
|
-
|
|
1426
|
+
# Normalize and validate titlebar configuration
|
|
1431
1427
|
def normalize_titlebar_config(titlebar_config):
|
|
1432
1428
|
config = titlebar_config if isinstance(titlebar_config, dict) else {}
|
|
1433
1429
|
normalized = default_titlebar_config()
|
|
@@ -1463,7 +1459,7 @@ def normalize_titlebar_config(titlebar_config):
|
|
|
1463
1459
|
|
|
1464
1460
|
return normalized
|
|
1465
1461
|
|
|
1466
|
-
|
|
1462
|
+
# Return default sidebar configuration
|
|
1467
1463
|
def default_sidebar_config():
|
|
1468
1464
|
return {
|
|
1469
1465
|
'enabled': True,
|
|
@@ -1477,7 +1473,7 @@ def default_sidebar_config():
|
|
|
1477
1473
|
'collapse_mode': DEFAULT_SIDEBAR_COLLAPSE_MODE,
|
|
1478
1474
|
}
|
|
1479
1475
|
|
|
1480
|
-
|
|
1476
|
+
# Normalize and validate sidebar behavior configuration
|
|
1481
1477
|
def normalize_sidebar_behavior(sidebar_config):
|
|
1482
1478
|
config = sidebar_config if isinstance(sidebar_config, dict) else {}
|
|
1483
1479
|
normalized = default_sidebar_config()
|
|
@@ -1508,13 +1504,13 @@ def normalize_sidebar_behavior(sidebar_config):
|
|
|
1508
1504
|
|
|
1509
1505
|
return normalized
|
|
1510
1506
|
|
|
1511
|
-
|
|
1507
|
+
# Get effective allowed themes from configuration
|
|
1512
1508
|
def get_effective_allowed_themes(config):
|
|
1513
1509
|
if not isinstance(config, dict):
|
|
1514
1510
|
return tuple(normalize_allowed_themes())
|
|
1515
1511
|
return tuple(normalize_allowed_themes(config.get('allowed_themes')))
|
|
1516
1512
|
|
|
1517
|
-
|
|
1513
|
+
# Resolve user theme preference against allowed themes
|
|
1518
1514
|
def resolve_user_theme_preference(user_prefs, config):
|
|
1519
1515
|
prefs = dict(user_prefs or {})
|
|
1520
1516
|
allowed_themes = set(get_effective_allowed_themes(config))
|
|
@@ -1531,7 +1527,7 @@ def resolve_user_theme_preference(user_prefs, config):
|
|
|
1531
1527
|
prefs['theme'] = default_theme
|
|
1532
1528
|
return prefs
|
|
1533
1529
|
|
|
1534
|
-
|
|
1530
|
+
# Resolve user sidebar density preference
|
|
1535
1531
|
def resolve_sidebar_density_preference(user_prefs, config):
|
|
1536
1532
|
prefs = dict(user_prefs or {})
|
|
1537
1533
|
sidebar_config = normalize_sidebar_behavior(config.get('sidebar', {}))
|
|
@@ -1544,7 +1540,7 @@ def resolve_sidebar_density_preference(user_prefs, config):
|
|
|
1544
1540
|
prefs['sidebar_density'] = sidebar_config.get('density', DEFAULT_SIDEBAR_DENSITY)
|
|
1545
1541
|
return prefs
|
|
1546
1542
|
|
|
1547
|
-
|
|
1543
|
+
# Resolve user sidebar collapsed preference
|
|
1548
1544
|
def resolve_sidebar_collapsed_preference(user_prefs, config, session_collapsed=False):
|
|
1549
1545
|
prefs = dict(user_prefs or {})
|
|
1550
1546
|
collapse_mode = normalize_sidebar_behavior(config.get('sidebar', {})).get('collapse_mode', DEFAULT_SIDEBAR_COLLAPSE_MODE)
|
|
@@ -1557,11 +1553,9 @@ def resolve_sidebar_collapsed_preference(user_prefs, config, session_collapsed=F
|
|
|
1557
1553
|
raw_value = raw_value.lower() == 'true'
|
|
1558
1554
|
return bool(raw_value), prefs
|
|
1559
1555
|
|
|
1560
|
-
|
|
1561
1556
|
SYSTEM_SETTINGS_EXPORT_FORMAT = 'django-microsys.system-settings'
|
|
1562
1557
|
SYSTEM_SETTINGS_EXPORT_VERSION = 1
|
|
1563
1558
|
|
|
1564
|
-
|
|
1565
1559
|
SYSTEM_SETTINGS_EXPORT_FIELDS = (
|
|
1566
1560
|
'system_names',
|
|
1567
1561
|
'logo',
|
|
@@ -1588,13 +1582,13 @@ SYSTEM_SETTINGS_EXPORT_FIELDS = (
|
|
|
1588
1582
|
'titlebar_config',
|
|
1589
1583
|
)
|
|
1590
1584
|
|
|
1591
|
-
|
|
1585
|
+
# Extract filename from FieldFile or string
|
|
1592
1586
|
def _field_file_name(value):
|
|
1593
1587
|
if isinstance(value, FieldFile):
|
|
1594
1588
|
return value.name or ''
|
|
1595
1589
|
return str(value or '')
|
|
1596
1590
|
|
|
1597
|
-
|
|
1591
|
+
# Coerce value to boolean for import settings
|
|
1598
1592
|
def _coerce_import_bool(value):
|
|
1599
1593
|
if isinstance(value, str):
|
|
1600
1594
|
return value.strip().lower() in {'1', 'true', 'yes', 'on'}
|
|
@@ -1695,7 +1689,6 @@ def normalize_system_settings_import_payload(payload):
|
|
|
1695
1689
|
):
|
|
1696
1690
|
if bool_field in normalized:
|
|
1697
1691
|
normalized[bool_field] = _coerce_import_bool(normalized[bool_field])
|
|
1698
|
-
|
|
1699
1692
|
return normalized
|
|
1700
1693
|
|
|
1701
1694
|
|
|
@@ -2467,7 +2460,7 @@ def collect_related_objects(instance):
|
|
|
2467
2460
|
|
|
2468
2461
|
return related_data
|
|
2469
2462
|
|
|
2470
|
-
|
|
2463
|
+
# Generate detail context from model instance
|
|
2471
2464
|
def _build_generic_detail_context(instance, request=None):
|
|
2472
2465
|
"""
|
|
2473
2466
|
Dynamically generates a list of {'label': ..., 'value': ...} dictionaries
|
|
@@ -2532,7 +2525,7 @@ def _build_generic_detail_context(instance, request=None):
|
|
|
2532
2525
|
|
|
2533
2526
|
return fields_data
|
|
2534
2527
|
|
|
2535
|
-
|
|
2528
|
+
# Resolve translated display label for detail views
|
|
2536
2529
|
def resolve_detail_field_label(instance, field, request=None, strings=None):
|
|
2537
2530
|
"""Resolve a translated display label for generic detail views."""
|
|
2538
2531
|
s = strings or get_strings(get_current_language_code(request))
|
|
@@ -2563,7 +2556,6 @@ def resolve_detail_field_label(instance, field, request=None, strings=None):
|
|
|
2563
2556
|
|
|
2564
2557
|
return raw_label or field_name
|
|
2565
2558
|
|
|
2566
|
-
|
|
2567
2559
|
# Dynamic Table Builder — Generates a django-tables2 Table class at runtime
|
|
2568
2560
|
def _build_generic_table_class(model):
|
|
2569
2561
|
"""
|
|
@@ -3127,7 +3119,6 @@ def toggle_sidebar(request):
|
|
|
3127
3119
|
return JsonResponse({"status": "success"})
|
|
3128
3120
|
return JsonResponse({"status": "error"}, status=400)
|
|
3129
3121
|
|
|
3130
|
-
|
|
3131
3122
|
# Form Helper — Applies shared field classes, direction, and optional inline labels
|
|
3132
3123
|
def set_field_attrs(form, request=None, inline_labels=False):
|
|
3133
3124
|
"""Set common attributes for all fields in the form."""
|
|
@@ -3260,7 +3251,7 @@ def set_field_attrs(form, request=None, inline_labels=False):
|
|
|
3260
3251
|
if is_date:
|
|
3261
3252
|
field.widget.attrs['autocomplete'] = 'off'
|
|
3262
3253
|
|
|
3263
|
-
|
|
3254
|
+
# Set up modern Crispy layout for django-filter FilterSet
|
|
3264
3255
|
def setup_filter_helper(filter_instance, request=None, preserve_keys=None, inline_labels=True):
|
|
3265
3256
|
"""
|
|
3266
3257
|
Sets up a modern, responsive Crispy layout for a django-filter FilterSet.
|
|
@@ -3333,7 +3324,7 @@ def setup_filter_helper(filter_instance, request=None, preserve_keys=None, inlin
|
|
|
3333
3324
|
# Apply shared field attrs, defaulting filters to inline placeholder labels.
|
|
3334
3325
|
set_field_attrs(filter_instance.form, request, inline_labels=inline_labels)
|
|
3335
3326
|
|
|
3336
|
-
|
|
3327
|
+
# Build advanced filter helper with expandable fields
|
|
3337
3328
|
def advanced_filter_helper(filter_instance, config=None, request=None, preserve_keys=None, inline_labels=True):
|
|
3338
3329
|
"""
|
|
3339
3330
|
Build an "advanced" filter helper with:
|
|
@@ -3647,7 +3638,7 @@ def advanced_filter_helper(filter_instance, config=None, request=None, preserve_
|
|
|
3647
3638
|
helper.layout = Layout(*layout_items)
|
|
3648
3639
|
filter_instance.form.helper = helper
|
|
3649
3640
|
|
|
3650
|
-
# Form Helper —
|
|
3641
|
+
# Form Helper — Set the first choice of a selection field safely
|
|
3651
3642
|
def set_first_choice(field, placeholder):
|
|
3652
3643
|
"""Set the first choice of a specified field safely without overwriting data."""
|
|
3653
3644
|
# 1. Handle fields with explicit empty_label (ModelChoiceField, etc.)
|
|
@@ -3681,7 +3672,6 @@ def set_first_choice(field, placeholder):
|
|
|
3681
3672
|
|
|
3682
3673
|
field.choices = choices
|
|
3683
3674
|
|
|
3684
|
-
|
|
3685
3675
|
# Form Helper — Translates a choices list using MS_TRANS choice_ prefix
|
|
3686
3676
|
def translate_choices(choices, ms_trans):
|
|
3687
3677
|
"""
|
|
@@ -3697,7 +3687,6 @@ def translate_choices(choices, ms_trans):
|
|
|
3697
3687
|
translated.append((value, ms_trans.get(f'choice_{value}', label)))
|
|
3698
3688
|
return translated
|
|
3699
3689
|
|
|
3700
|
-
|
|
3701
3690
|
# Form Helper — Detects if a Crispy form layout already contains Submit/Button elements
|
|
3702
3691
|
def has_submit_button(form):
|
|
3703
3692
|
"""
|
|
@@ -3747,3 +3736,17 @@ def has_submit_button(form):
|
|
|
3747
3736
|
return True
|
|
3748
3737
|
|
|
3749
3738
|
return False
|
|
3739
|
+
|
|
3740
|
+
# Return app version from VERSION file at the parent caller's folder
|
|
3741
|
+
def get_app_version(calling_file_path: str) -> str:
|
|
3742
|
+
"""
|
|
3743
|
+
Reads the VERSION file from the same directory as the calling file.
|
|
3744
|
+
Usage: VERSION = get_app_version(__file__)
|
|
3745
|
+
"""
|
|
3746
|
+
try:
|
|
3747
|
+
# Resolves the directory of the file that called this function
|
|
3748
|
+
app_dir = Path(calling_file_path).resolve().parent
|
|
3749
|
+
with open(app_dir / "VERSION", "r") as f:
|
|
3750
|
+
return f.read().strip()
|
|
3751
|
+
except FileNotFoundError:
|
|
3752
|
+
return "unknown"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
2.2.2
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{django_microsys-2.2.2 → django_microsys-2.2.3}/django_microsys.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/management/commands/microsys_check.py
RENAMED
|
File without changes
|
{django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/management/commands/microsys_setup.py
RENAMED
|
File without changes
|
|
File without changes
|
{django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/management/commands/seed_activity_log.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/migrations/0002_public_registration.py
RENAMED
|
File without changes
|
{django_microsys-2.2.2 → django_microsys-2.2.3}/microsys/migrations/0003_public_root_split.py
RENAMED
|
File without changes
|
|
File without changes
|