django-microsys 2.2.0__tar.gz → 2.2.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (264) hide show
  1. {django_microsys-2.2.0 → django_microsys-2.2.2}/PKG-INFO +1 -1
  2. {django_microsys-2.2.0 → django_microsys-2.2.2}/django_microsys.egg-info/PKG-INFO +1 -1
  3. django_microsys-2.2.2/microsys/VERSION +1 -0
  4. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/context_processors.py +1 -0
  5. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/fetcher.py +21 -4
  6. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/forms.py +89 -8
  7. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/main/css/system_setup.css +44 -0
  8. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/main/js/options.js +3 -1
  9. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/main/js/system_setup.js +8 -1
  10. django_microsys-2.2.2/microsys/templates/microsys/includes/font_previews.html +20 -0
  11. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/templates/microsys/includes/font_settings_matrix.html +1 -18
  12. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/templates/microsys/includes/language_fonts_editor.html +4 -4
  13. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/templates/microsys/includes/options.html +3 -3
  14. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/templates/microsys/tables/table.html +1 -1
  15. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/translations.py +44 -16
  16. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/utils.py +24 -6
  17. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/views/sections.py +7 -1
  18. django_microsys-2.2.0/microsys/VERSION +0 -1
  19. django_microsys-2.2.0/microsys/templates/microsys/includes/font_previews.html +0 -23
  20. {django_microsys-2.2.0 → django_microsys-2.2.2}/LICENSE +0 -0
  21. {django_microsys-2.2.0 → django_microsys-2.2.2}/MANIFEST.in +0 -0
  22. {django_microsys-2.2.0 → django_microsys-2.2.2}/README.md +0 -0
  23. {django_microsys-2.2.0 → django_microsys-2.2.2}/django_microsys.egg-info/SOURCES.txt +0 -0
  24. {django_microsys-2.2.0 → django_microsys-2.2.2}/django_microsys.egg-info/dependency_links.txt +0 -0
  25. {django_microsys-2.2.0 → django_microsys-2.2.2}/django_microsys.egg-info/entry_points.txt +0 -0
  26. {django_microsys-2.2.0 → django_microsys-2.2.2}/django_microsys.egg-info/requires.txt +0 -0
  27. {django_microsys-2.2.0 → django_microsys-2.2.2}/django_microsys.egg-info/top_level.txt +0 -0
  28. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/__init__.py +0 -0
  29. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/__main__.py +0 -0
  30. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/admin.py +0 -0
  31. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/api.py +0 -0
  32. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/apps.py +0 -0
  33. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/cli.py +0 -0
  34. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/constants.py +0 -0
  35. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/discovery.py +0 -0
  36. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/filters.py +0 -0
  37. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/fonts.py +0 -0
  38. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/formats/__init__.py +0 -0
  39. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/formats/ar/__init__.py +0 -0
  40. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/formats/ar/formats.py +0 -0
  41. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/formats/en/__init__.py +0 -0
  42. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/formats/en/formats.py +0 -0
  43. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/guards.py +0 -0
  44. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/management/commands/__init__.py +0 -0
  45. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/management/commands/microsys_check.py +0 -0
  46. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/management/commands/microsys_setup.py +0 -0
  47. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/management/commands/migrator.py +0 -0
  48. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/management/commands/seed_activity_log.py +0 -0
  49. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/managers.py +0 -0
  50. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/middleware.py +0 -0
  51. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/migrations/0001_initial.py +0 -0
  52. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/migrations/0002_public_registration.py +0 -0
  53. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/migrations/0003_public_root_split.py +0 -0
  54. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/migrations/0004_client_ip_and_trusted_devices.py +0 -0
  55. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/migrations/0005_systemsettings_allow_user_font_override_and_more.py +0 -0
  56. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/migrations/__init__.py +0 -0
  57. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/models.py +0 -0
  58. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/patches.py +0 -0
  59. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/registration.py +0 -0
  60. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/scaffold.py +0 -0
  61. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/scaffold_templates/app/README.md.tmpl +0 -0
  62. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/scaffold_templates/app/__init__.py.tmpl +0 -0
  63. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/scaffold_templates/app/apps.py.tmpl +0 -0
  64. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/scaffold_templates/app/filters.py.tmpl +0 -0
  65. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/scaffold_templates/app/forms.py.tmpl +0 -0
  66. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/scaffold_templates/app/migrations/__init__.py.tmpl +0 -0
  67. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/scaffold_templates/app/models.py.tmpl +0 -0
  68. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/scaffold_templates/app/tables.py.tmpl +0 -0
  69. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/scaffold_templates/app/templates/example_record_confirm_delete.html.tmpl +0 -0
  70. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/scaffold_templates/app/templates/example_record_detail.html.tmpl +0 -0
  71. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/scaffold_templates/app/templates/example_record_form.html.tmpl +0 -0
  72. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/scaffold_templates/app/templates/example_record_list.html.tmpl +0 -0
  73. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/scaffold_templates/app/tests/__init__.py.tmpl +0 -0
  74. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/scaffold_templates/app/tests/test_app.py.tmpl +0 -0
  75. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/scaffold_templates/app/translations.py.tmpl +0 -0
  76. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/scaffold_templates/app/urls.py.tmpl +0 -0
  77. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/scaffold_templates/app/views.py.tmpl +0 -0
  78. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/scaffold_templates/project/.nginx/nginx.conf.tmpl +0 -0
  79. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/scaffold_templates/project/.secrets/.env.tmpl +0 -0
  80. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/scaffold_templates/project/Dockerfile.tmpl +0 -0
  81. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/scaffold_templates/project/README.md.tmpl +0 -0
  82. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/scaffold_templates/project/compose.dev.yml.tmpl +0 -0
  83. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/scaffold_templates/project/compose.yml.tmpl +0 -0
  84. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/scaffold_templates/project/dockerignore.tmpl +0 -0
  85. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/scaffold_templates/project/docs/README.md.tmpl +0 -0
  86. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/scaffold_templates/project/entrypoint.sh.tmpl +0 -0
  87. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/scaffold_templates/project/gitattributes.tmpl +0 -0
  88. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/scaffold_templates/project/gitignore.tmpl +0 -0
  89. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/scaffold_templates/project/gunicorn.py.tmpl +0 -0
  90. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/scaffold_templates/project/manage.py.tmpl +0 -0
  91. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/scaffold_templates/project/package/__init__.py.tmpl +0 -0
  92. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/scaffold_templates/project/package/asgi.py.tmpl +0 -0
  93. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/scaffold_templates/project/package/celery.py.tmpl +0 -0
  94. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/scaffold_templates/project/package/settings.py.tmpl +0 -0
  95. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/scaffold_templates/project/package/urls.py.tmpl +0 -0
  96. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/scaffold_templates/project/package/wsgi.py.tmpl +0 -0
  97. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/scaffold_templates/project/req.txt.tmpl +0 -0
  98. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/scaffold_templates/project/start.ps1.tmpl +0 -0
  99. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/scaffold_templates/project/start.sh.tmpl +0 -0
  100. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/scaffold_templates/project/tests/__init__.py.tmpl +0 -0
  101. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/scaffold_templates/project/tests/test_scaffold.py.tmpl +0 -0
  102. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/scaffold_templates/project/tools/smtp_relay.py.tmpl +0 -0
  103. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/signals.py +0 -0
  104. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/bootstrap/bootstrap-icons.css +0 -0
  105. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/bootstrap/bootstrap-icons.woff +0 -0
  106. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/bootstrap/bootstrap-icons.woff2 +0 -0
  107. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/bootstrap/bootstrap.bundle.min.js +0 -0
  108. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/bootstrap/bootstrap.bundle.min.js.map +0 -0
  109. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/bootstrap/bootstrap.min.css +0 -0
  110. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/bootstrap/bootstrap.min.css.map +0 -0
  111. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/bootstrap/bootstrap.rtl.min.css +0 -0
  112. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/bootstrap/bootstrap.rtl.min.css.map +0 -0
  113. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/favicon.ico +0 -0
  114. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/img/base_logo.webp +0 -0
  115. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/img/default_profile.webp +0 -0
  116. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/img/login_logo.webp +0 -0
  117. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/accessibility/css/main.css +0 -0
  118. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/accessibility/js/main.js +0 -0
  119. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/activitylog/js/main.js +0 -0
  120. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/fonts/TwemojiCountryFlags.woff2 +0 -0
  121. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/fonts/cairo-bold.woff2 +0 -0
  122. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/fonts/cairo-medium.woff2 +0 -0
  123. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/fonts/cairo-regular.woff2 +0 -0
  124. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/fonts/shabwa-bold.woff2 +0 -0
  125. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/fonts/shabwa-medium.woff2 +0 -0
  126. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/fonts/shabwa-regular.woff2 +0 -0
  127. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/forms/css/file_field.css +0 -0
  128. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/forms/css/form_actions.css +0 -0
  129. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/forms/css/form_fields.css +0 -0
  130. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/forms/js/file_field.js +0 -0
  131. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/forms/js/filter_form.js +0 -0
  132. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/helpers/autofill/js/main.js +0 -0
  133. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/helpers/context_menu/css/main.css +0 -0
  134. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/helpers/context_menu/js/main.js +0 -0
  135. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/helpers/context_menu/js/section_manager.js +0 -0
  136. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/helpers/dynamic_modal/js/main.js +0 -0
  137. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/helpers/prevent_double_submit.js +0 -0
  138. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/helpers/scan_link/js/main.js +0 -0
  139. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/helpers/scan_link/js/scan_button.js +0 -0
  140. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/helpers/wizard/js/main.js +0 -0
  141. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/language/css/main.css +0 -0
  142. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/language/js/main.js +0 -0
  143. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/language/js/translations.js +0 -0
  144. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/main/css/buttons.css +0 -0
  145. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/main/css/dropdowns.css +0 -0
  146. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/main/css/index_cards.css +0 -0
  147. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/main/css/main.css +0 -0
  148. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/main/css/options.css +0 -0
  149. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/main/css/pagination.css +0 -0
  150. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/main/css/selectors.css +0 -0
  151. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/main/css/tables.css +0 -0
  152. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/main/css/template_cleanup.css +0 -0
  153. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/main/css/titlebar.css +0 -0
  154. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/main/js/base_head.js +0 -0
  155. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/main/js/base_runtime.js +0 -0
  156. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/main/js/selectors.js +0 -0
  157. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/main/js/tables.js +0 -0
  158. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/sections/js/manage_sections.js +0 -0
  159. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/sidebar/css/main.css +0 -0
  160. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/sidebar/css/reorder.css +0 -0
  161. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/sidebar/css/theme_picker.css +0 -0
  162. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/sidebar/js/main.js +0 -0
  163. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/sidebar/js/preload.js +0 -0
  164. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/sidebar/js/reorder.js +0 -0
  165. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/sidebar/js/theme_picker.js +0 -0
  166. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/themes/css/blue.css +0 -0
  167. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/themes/css/dark.css +0 -0
  168. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/themes/css/gold.css +0 -0
  169. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/themes/css/gothic.css +0 -0
  170. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/themes/css/green.css +0 -0
  171. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/themes/css/light.css +0 -0
  172. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/themes/css/mono.css +0 -0
  173. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/themes/css/neon.css +0 -0
  174. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/themes/css/red.css +0 -0
  175. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/themes/css/retro.css +0 -0
  176. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/themes/js/main.js +0 -0
  177. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/tutorial/css/main.css +0 -0
  178. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/tutorial/js/driver.js +0 -0
  179. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/tutorial/js/main.js +0 -0
  180. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/users/css/login.css +0 -0
  181. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/users/css/permissions.css +0 -0
  182. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/users/css/profile.css +0 -0
  183. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/users/css/user_hub.css +0 -0
  184. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/users/js/login.js +0 -0
  185. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/users/js/manage_users.js +0 -0
  186. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/users/js/permissions.js +0 -0
  187. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/users/js/profile_2fa.js +0 -0
  188. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/users/js/profile_image_widget.js +0 -0
  189. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/users/js/twofa_verify.js +0 -0
  190. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/microsys/users/js/user_hub.js +0 -0
  191. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/vanillajs-datepicker/datepicker-bs5.min.css +0 -0
  192. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/static/vanillajs-datepicker/datepicker.min.js +0 -0
  193. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/tables.py +0 -0
  194. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/templates/bootstrap5/layout/field_file.html +0 -0
  195. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/templates/microsys/2fa/verify.html +0 -0
  196. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/templates/microsys/activitylog/activity_log.html +0 -0
  197. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/templates/microsys/activitylog/activity_log_detail_modal.html +0 -0
  198. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/templates/microsys/base.html +0 -0
  199. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/templates/microsys/form_base.html +0 -0
  200. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/templates/microsys/forms/assets_head.html +0 -0
  201. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/templates/microsys/forms/assets_scripts.html +0 -0
  202. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/templates/microsys/forms/crispy_file_field.html +0 -0
  203. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/templates/microsys/forms/file_input.html +0 -0
  204. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/templates/microsys/forms/filter_assets_head.html +0 -0
  205. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/templates/microsys/forms/filter_assets_scripts.html +0 -0
  206. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/templates/microsys/helpers/dynamic_modal.html +0 -0
  207. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/templates/microsys/helpers/dynamic_modal_combined.html +0 -0
  208. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/templates/microsys/helpers/dynamic_modal_detail.html +0 -0
  209. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/templates/microsys/helpers/dynamic_modal_form.html +0 -0
  210. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/templates/microsys/helpers/dynamic_modal_list.html +0 -0
  211. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/templates/microsys/helpers/micro_context_menu.html +0 -0
  212. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/templates/microsys/includes/language_catalog_editor.html +0 -0
  213. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/templates/microsys/includes/language_previews.html +0 -0
  214. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/templates/microsys/includes/sidebar_builder.html +0 -0
  215. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/templates/microsys/includes/sidebar_density_previews.html +0 -0
  216. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/templates/microsys/includes/sidebar_items.html +0 -0
  217. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/templates/microsys/includes/system_names_editor.html +0 -0
  218. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/templates/microsys/includes/system_setup.html +0 -0
  219. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/templates/microsys/includes/table_density_previews.html +0 -0
  220. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/templates/microsys/includes/theme_previews.html +0 -0
  221. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/templates/microsys/includes/theme_settings_matrix.html +0 -0
  222. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/templates/microsys/includes/titlebar.html +0 -0
  223. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/templates/microsys/includes/translation_matrix_editor.html +0 -0
  224. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/templates/microsys/includes/tutorial.html +0 -0
  225. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/templates/microsys/list_base.html +0 -0
  226. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/templates/microsys/registration/pending.html +0 -0
  227. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/templates/microsys/scopes/scope_actions.html +0 -0
  228. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/templates/microsys/scopes/scope_form.html +0 -0
  229. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/templates/microsys/scopes/scope_manager.html +0 -0
  230. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/templates/microsys/sections/manage_sections.html +0 -0
  231. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/templates/microsys/sections/subsection_select.html +0 -0
  232. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/templates/microsys/sidebar/auto.html +0 -0
  233. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/templates/microsys/sidebar/extra_groups.html +0 -0
  234. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/templates/microsys/sidebar/main.html +0 -0
  235. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/templates/microsys/sidebar/tree.html +0 -0
  236. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/templates/microsys/users/grouped_permissions.html +0 -0
  237. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/templates/microsys/users/manage_users.html +0 -0
  238. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/templates/microsys/users/profile.html +0 -0
  239. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/templates/microsys/users/profile_image_widget.html +0 -0
  240. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/templates/microsys/users/user_detail_modal.html +0 -0
  241. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/templates/microsys/users/user_hub.html +0 -0
  242. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/templates/registration/email/verify_registration.txt +0 -0
  243. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/templates/registration/login.html +0 -0
  244. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/templates/registration/register.html +0 -0
  245. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/templates/registration/register_sent.html +0 -0
  246. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/templates/registration/register_verify.html +0 -0
  247. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/templatetags/__init__.py +0 -0
  248. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/templatetags/microsys_tags.py +0 -0
  249. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/templatetags/microsys_translation.py +0 -0
  250. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/templatetags/sidebar_tags.py +0 -0
  251. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/themes.py +0 -0
  252. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/urls.py +0 -0
  253. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/views/__init__.py +0 -0
  254. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/views/activitylog.py +0 -0
  255. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/views/general.py +0 -0
  256. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/views/profile.py +0 -0
  257. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/views/registration.py +0 -0
  258. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/views/scopes.py +0 -0
  259. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/views/sidebar.py +0 -0
  260. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/views/twofa.py +0 -0
  261. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/views/users.py +0 -0
  262. {django_microsys-2.2.0 → django_microsys-2.2.2}/microsys/widgets.py +0 -0
  263. {django_microsys-2.2.0 → django_microsys-2.2.2}/pyproject.toml +0 -0
  264. {django_microsys-2.2.0 → django_microsys-2.2.2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: django_microsys
3
- Version: 2.2.0
3
+ Version: 2.2.2
4
4
  Summary: Django microSYS (System Integration Service) - Multilingual Django Starter Pack, Packed with Features.
5
5
  Author-email: DeBeski <debeski1@gmail.com>
6
6
  License: NON-COMMERCIAL
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: django-microsys
3
- Version: 2.2.0
3
+ Version: 2.2.2
4
4
  Summary: Django microSYS (System Integration Service) - Multilingual Django Starter Pack, Packed with Features.
5
5
  Author-email: DeBeski <debeski1@gmail.com>
6
6
  License: NON-COMMERCIAL
@@ -0,0 +1 @@
1
+ 2.2.2
@@ -346,6 +346,7 @@ def microsys_context(request):
346
346
  from .fonts import generate_font_face_css, get_builtin_fonts
347
347
  allowed_fonts = normalize_allowed_fonts(final_config.get('allowed_fonts'))
348
348
  context['font_face_css'] = generate_font_face_css(allowed_fonts)
349
+ context['option_fonts'] = [font for font in get_builtin_fonts() if font.get('slug') in allowed_fonts]
349
350
 
350
351
  allow_user_font_override = bool(final_config.get('allow_user_font_override', True))
351
352
  default_fonts_by_lang = final_config.get('default_fonts', {})
@@ -4,12 +4,29 @@ from django.http import HttpResponse, HttpResponseRedirect
4
4
  from django.db.models.query import QuerySet
5
5
  from django.contrib import messages
6
6
  from django.db.models import FileField
7
+ from django.utils.http import url_has_allowed_host_and_scheme
7
8
  from io import BytesIO
8
9
  import mimetypes
9
10
  import openpyxl
10
11
  import zipfile
11
12
  from .utils import log_user_action
12
13
 
14
+ def _safe_referer(request, fallback='/'):
15
+ referer = request.META.get('HTTP_REFERER')
16
+ if referer:
17
+ from django.conf import settings
18
+ allowed_hosts = {request.get_host()}
19
+ if hasattr(settings, 'ALLOWED_HOSTS') and settings.ALLOWED_HOSTS:
20
+ for host in settings.ALLOWED_HOSTS:
21
+ if host:
22
+ if host.startswith('.'):
23
+ allowed_hosts.add(host[1:])
24
+ elif host != '*':
25
+ allowed_hosts.add(host)
26
+ if url_has_allowed_host_and_scheme(url=referer, allowed_hosts=allowed_hosts, require_https=request.is_secure()):
27
+ return referer
28
+ return fallback
29
+
13
30
  # Universal Downloader
14
31
  #####################################################################
15
32
  def fetch_file(request, data, file_type=None):
@@ -38,7 +55,7 @@ def fetch_file(request, data, file_type=None):
38
55
 
39
56
  if not records:
40
57
  messages.error(request, "لا توجد سجلات للتحميل.")
41
- return HttpResponseRedirect(request.META.get('HTTP_REFERER', '/'))
58
+ return HttpResponseRedirect(_safe_referer(request))
42
59
 
43
60
  # 2. Collect Files
44
61
  files_to_download = []
@@ -78,7 +95,7 @@ def fetch_file(request, data, file_type=None):
78
95
  # 3. Decision: Error, Single File, or Zip
79
96
  if not files_to_download:
80
97
  messages.error(request, "لا توجد ملفات صالحة للتحميل في السجلات المختارة.")
81
- return HttpResponseRedirect(request.META.get('HTTP_REFERER', '/'))
98
+ return HttpResponseRedirect(_safe_referer(request))
82
99
 
83
100
  if len(files_to_download) == 1:
84
101
  # Serve Single File
@@ -248,7 +265,7 @@ def fetch_excel(request, queryset, exclude_fields=None, hidden_fields=None, shee
248
265
  """
249
266
  if not queryset:
250
267
  messages.error(request, "لا توجد بيانات للتصدير.")
251
- return HttpResponseRedirect(request.META.get('HTTP_REFERER', '/'))
268
+ return HttpResponseRedirect(_safe_referer(request))
252
269
 
253
270
  # Normalize to iterable if list passed
254
271
  data_list = queryset
@@ -260,7 +277,7 @@ def fetch_excel(request, queryset, exclude_fields=None, hidden_fields=None, shee
260
277
 
261
278
  if not model:
262
279
  messages.error(request, "تعذر تحديد نموذج البيانات.")
263
- return HttpResponseRedirect(request.META.get('HTTP_REFERER', '/'))
280
+ return HttpResponseRedirect(_safe_referer(request))
264
281
 
265
282
  from openpyxl.utils import get_column_letter
266
283
  from django.db.models import FileField, DateTimeField
@@ -1,5 +1,6 @@
1
1
  # Imports of the required python modules and libraries
2
2
  ######################################################
3
+ import os
3
4
  import json
4
5
  from types import MethodType, SimpleNamespace
5
6
 
@@ -1686,6 +1687,12 @@ class SystemSettingsForm(forms.ModelForm):
1686
1687
  if parsed_step in (0, 1, 2, 3, 4, 5):
1687
1688
  self.single_step_mode = True
1688
1689
  self.single_step_index = parsed_step
1690
+ if self.mode != 'setup' and self.single_step_mode:
1691
+ # Single-step modal posts can legitimately omit values owned by another
1692
+ # wizard step; field-level required validation must not fire before the
1693
+ # step-preservation cleaners get a chance to restore them.
1694
+ self.fields['default_theme'].required = False
1695
+ self.fields['default_table_density'].required = False
1689
1696
 
1690
1697
  from microsys.discovery import discover_sidebar_catalog, sanitize_sidebar_config
1691
1698
  from microsys.utils import get_system_config
@@ -1766,6 +1773,7 @@ class SystemSettingsForm(forms.ModelForm):
1766
1773
  'help_sys_allow_user_theme_override',
1767
1774
  'Allow users to switch between the allowed themes at runtime from Options and the sidebar toolbar.',
1768
1775
  )
1776
+ self.fields['allow_user_language_override'].label = s.get('form_sys_allow_user_language_override', 'Allow user language override')
1769
1777
  self.fields['allow_user_language_override'].help_text = s.get(
1770
1778
  'help_sys_allow_user_language_override',
1771
1779
  'Allow users to change their display language from Options. When disabled, the system default language is enforced.',
@@ -1786,6 +1794,11 @@ class SystemSettingsForm(forms.ModelForm):
1786
1794
  'help_sys_default_table_density',
1787
1795
  'Choose the default table density for new users; each user can still override it later from Options.',
1788
1796
  )
1797
+ self.fields['default_table_density'].choices = (
1798
+ ('dense', s.get('table_density_dense', 'Dense')),
1799
+ (DEFAULT_TABLE_DENSITY, s.get('table_density_balanced', 'Balanced')),
1800
+ ('roomy', s.get('table_density_roomy', 'Roomy')),
1801
+ )
1789
1802
  self.fields['logo'].label = s.get('form_sys_logo', "System Logo (Logo)")
1790
1803
  self.fields['favicon'].label = s.get('form_sys_favicon', "Site Icon (Favicon)")
1791
1804
  self.fields['logo'].widget = _build_archive_file_widget(
@@ -2747,11 +2760,7 @@ class SystemSettingsForm(forms.ModelForm):
2747
2760
  css_class=_step_css_class(3),
2748
2761
  ),
2749
2762
  Div(
2750
- HTML(f"<div class='mb-3'><span class='badge rounded-pill text-bg-primary'>{s.get('system_setup_step5', 'Step 5: UI & Layout')}</span></div>"),
2751
- HTML(f"<h6 class='fw-bold my-3'>{s.get('tables_settings_title', 'Tables Settings')}</h6>"),
2752
- Row(
2753
- Div(Field('default_table_density'), css_class='col'),
2754
- ),
2763
+ HTML(f"<div class='mb-3'><span class='badge rounded-pill text-bg-primary'>{s.get('system_setup_step5', 'Step 5: Titlebar')}</span></div>"),
2755
2764
  HTML(f"<h6 class='fw-bold my-3'>{s.get('titlebar_settings_title', 'Titlebar Settings')}</h6>"),
2756
2765
  Row(
2757
2766
  build_settings_toggle_field(self, 'titlebar_show_title', css_class='col-lg-6 col-xl-3'),
@@ -2791,6 +2800,11 @@ class SystemSettingsForm(forms.ModelForm):
2791
2800
  build_settings_toggle_field(self, 'allow_user_font_override', css_class='col-12 mt-2'),
2792
2801
  HTML(self.language_fonts_editor_html),
2793
2802
  Field('default_fonts'),
2803
+ HTML(f"<h6 class='fw-bold my-3'>{s.get('tables_settings_title', 'Tables Settings')}</h6>"),
2804
+ Row(
2805
+ Div(Field('default_table_density'), css_class='col'),
2806
+ css_class='mb-3'
2807
+ ),
2794
2808
  css_class=_step_css_class(5),
2795
2809
  ),
2796
2810
  FormActions(
@@ -2866,12 +2880,27 @@ class SystemSettingsForm(forms.ModelForm):
2866
2880
  return str(self.cleaned_data.get('default_language') or 'en').strip().lower().replace('_', '-')
2867
2881
 
2868
2882
  def clean_allowed_fonts(self):
2883
+ if self.is_bound and self.mode != 'setup' and self.single_step_mode and 'allowed_fonts' not in self.data:
2884
+ preserved = getattr(self.instance, 'allowed_fonts', None)
2885
+ if preserved in (None, ''):
2886
+ preserved = self.initial.get('allowed_fonts')
2887
+ return list(normalize_allowed_fonts(preserved))
2869
2888
  data = self.cleaned_data.get('allowed_fonts')
2870
2889
  if not data:
2871
2890
  return []
2872
2891
  return list(data)
2873
2892
 
2874
2893
  def clean_default_fonts(self):
2894
+ if self.is_bound and self.mode != 'setup' and self.single_step_mode and 'default_fonts' not in self.data:
2895
+ preserved = getattr(self.instance, 'default_fonts', None)
2896
+ if preserved in (None, ''):
2897
+ preserved = self.initial.get('default_fonts')
2898
+ if isinstance(preserved, str):
2899
+ try:
2900
+ preserved = json.loads(preserved)
2901
+ except json.JSONDecodeError:
2902
+ preserved = {}
2903
+ return preserved if isinstance(preserved, dict) else {}
2875
2904
  data = self.cleaned_data.get('default_fonts')
2876
2905
  if not data:
2877
2906
  return {}
@@ -2884,12 +2913,27 @@ class SystemSettingsForm(forms.ModelForm):
2884
2913
  return {}
2885
2914
 
2886
2915
  def clean_default_theme(self):
2887
- value = self.cleaned_data.get('default_theme') or 'light'
2916
+ if self.is_bound and self.mode != 'setup' and self.single_step_mode and 'default_theme' not in self.data:
2917
+ value = (
2918
+ getattr(self.instance, 'default_theme', None)
2919
+ or self.initial.get('default_theme')
2920
+ or 'light'
2921
+ )
2922
+ else:
2923
+ value = self.cleaned_data.get('default_theme') or 'light'
2888
2924
  if not is_valid_theme(value):
2889
2925
  raise ValidationError("Invalid theme choice.")
2890
2926
  return value
2891
2927
 
2892
2928
  def clean_allowed_themes(self):
2929
+ if self.is_bound and self.mode != 'setup' and self.single_step_mode and 'allowed_themes' not in self.data:
2930
+ values = getattr(self.instance, 'allowed_themes', None)
2931
+ if not values:
2932
+ values = self.initial.get('allowed_themes')
2933
+ normalized = list(normalize_allowed_themes(values))
2934
+ if not normalized:
2935
+ raise ValidationError("At least one theme must remain enabled.")
2936
+ return normalized
2893
2937
  values = self.cleaned_data.get('allowed_themes') or []
2894
2938
  if not values:
2895
2939
  raise ValidationError("At least one theme must remain enabled.")
@@ -2899,7 +2943,14 @@ class SystemSettingsForm(forms.ModelForm):
2899
2943
  return normalized
2900
2944
 
2901
2945
  def clean_default_table_density(self):
2902
- value = self.cleaned_data.get('default_table_density') or DEFAULT_TABLE_DENSITY
2946
+ if self.is_bound and self.mode != 'setup' and self.single_step_mode and 'default_table_density' not in self.data:
2947
+ value = (
2948
+ getattr(self.instance, 'default_table_density', None)
2949
+ or self.initial.get('default_table_density')
2950
+ or DEFAULT_TABLE_DENSITY
2951
+ )
2952
+ else:
2953
+ value = self.cleaned_data.get('default_table_density') or DEFAULT_TABLE_DENSITY
2903
2954
  if value not in TABLE_DENSITY_VALUES:
2904
2955
  raise ValidationError("Invalid table density choice.")
2905
2956
  return value
@@ -3239,13 +3290,43 @@ class SystemSettingsForm(forms.ModelForm):
3239
3290
  if cleaned.get('registration_activation_mode') not in REGISTRATION_ACTIVATION_VALUES:
3240
3291
  cleaned['registration_activation_mode'] = 'auto_login_after_verify'
3241
3292
  email_ready = get_email_service_status().get('available')
3242
- if email_config.get('secret_storage') == 'encrypted_db':
3293
+ backend = getattr(settings, 'EMAIL_BACKEND', 'django.core.mail.backends.smtp.EmailBackend')
3294
+ local_backends = {
3295
+ 'django.core.mail.backends.console.EmailBackend',
3296
+ 'django.core.mail.backends.locmem.EmailBackend',
3297
+ 'django.core.mail.backends.filebased.EmailBackend',
3298
+ }
3299
+ if backend in local_backends and getattr(settings, 'DEBUG', False):
3300
+ email_ready = True
3301
+ elif email_config.get('transport') == 'relay':
3302
+ relay_host = str(email_config.get('host') or os.getenv('SMTP_RELAY_HOST') or '').strip()
3303
+ relay_port = email_config.get('port')
3304
+ relay_from_email = str(
3305
+ email_config.get('default_from_email')
3306
+ or getattr(settings, 'DEFAULT_FROM_EMAIL', '')
3307
+ or os.getenv('DEFAULT_FROM_EMAIL')
3308
+ or ''
3309
+ ).strip()
3310
+ relay_password_ok = True
3311
+ if email_config.get('secret_storage') == 'encrypted_db' and email_config.get('username'):
3312
+ relay_password_ok = bool(email_config.get('encrypted_password'))
3313
+ email_ready = bool(relay_host and relay_port and relay_from_email and relay_password_ok)
3314
+ elif email_config.get('secret_storage') == 'encrypted_db':
3243
3315
  email_ready = bool(
3244
3316
  email_config.get('host')
3245
3317
  and email_config.get('port')
3246
3318
  and email_config.get('default_from_email')
3247
3319
  and (not email_config.get('username') or email_config.get('encrypted_password'))
3248
3320
  )
3321
+ elif backend == 'django.core.mail.backends.smtp.EmailBackend':
3322
+ email_ready = bool(
3323
+ (email_config.get('host') or getattr(settings, 'EMAIL_HOST', ''))
3324
+ and (email_config.get('port') or getattr(settings, 'EMAIL_PORT', None))
3325
+ and (
3326
+ email_config.get('default_from_email')
3327
+ or getattr(settings, 'DEFAULT_FROM_EMAIL', '')
3328
+ )
3329
+ )
3249
3330
  if (cleaned.get('public_registration_enabled') or cleaned.get('email_2fa')) and not email_ready:
3250
3331
  self.add_error(
3251
3332
  'email_config',
@@ -681,3 +681,47 @@
681
681
  grid-template-columns: 1fr;
682
682
  }
683
683
  }
684
+
685
+ /* Font Previews Support */
686
+ .ms-font-option {
687
+ align-items: center;
688
+ }
689
+
690
+ .ms-font-option__icon {
691
+ width: 3.5rem;
692
+ height: 3.5rem;
693
+ border-radius: 1rem;
694
+ }
695
+
696
+ .font-preview-sample {
697
+ font-weight: 500;
698
+ font-size: 1.5rem;
699
+ line-height: 1;
700
+ }
701
+
702
+ /* Specific Font Mapping Rules for Previews */
703
+ .ms-font-option[data-font="shabwa"] .font-preview-sample,
704
+ .ms-font-settings-option[data-font-option="shabwa"] .font-preview-sample {
705
+ font-family: 'Shabwa', sans-serif !important;
706
+ }
707
+
708
+ .ms-font-option[data-font="cairo"] .font-preview-sample,
709
+ .ms-font-settings-option[data-font-option="cairo"] .font-preview-sample {
710
+ font-family: 'Cairo', sans-serif !important;
711
+ }
712
+
713
+ /* Font settings option spacing */
714
+ .ms-font-settings-option__header {
715
+ position: absolute;
716
+ top: 8px;
717
+ right: 8px;
718
+ z-index: 2;
719
+ }
720
+ [dir="rtl"] .ms-font-settings-option__header {
721
+ right: auto;
722
+ left: 8px;
723
+ }
724
+ .ms-font-settings-option {
725
+ position: relative;
726
+ min-width: 120px;
727
+ }
@@ -103,7 +103,9 @@
103
103
  element.addEventListener('click', function () {
104
104
  const font = this.getAttribute('data-font');
105
105
  // Update local UI
106
- document.querySelectorAll('[data-font]').forEach(el => el.classList.toggle('active', el === this));
106
+ document.querySelectorAll('[data-font]').forEach((option) => {
107
+ option.classList.toggle('is-active', option === this);
108
+ });
107
109
 
108
110
  // Apply immediately
109
111
  const familyName = font.charAt(0).toUpperCase() + font.slice(1);
@@ -2858,13 +2858,15 @@
2858
2858
  input.value = activeTheme;
2859
2859
  allowedCheckboxes.forEach((checkbox) => {
2860
2860
  const theme = checkbox.getAttribute('data-setup-theme-allowed');
2861
- checkbox.disabled = checkbox.checked && resolvedAllowedThemes.length === 1;
2861
+ const isLocked = checkbox.checked && resolvedAllowedThemes.length === 1;
2862
+ checkbox.setAttribute('aria-disabled', isLocked ? 'true' : 'false');
2862
2863
  const container = checkbox.closest('[data-theme-option]');
2863
2864
  if (!container) {
2864
2865
  return;
2865
2866
  }
2866
2867
  const isAllowed = resolvedAllowedThemes.includes(theme);
2867
2868
  const isDefault = theme === activeTheme;
2869
+ container.classList.toggle('is-locked', isLocked);
2868
2870
  container.classList.toggle('opacity-50', !isAllowed);
2869
2871
  container.classList.toggle('is-default', isDefault);
2870
2872
  const button = container.querySelector('[data-setup-theme-choice]');
@@ -2897,6 +2899,11 @@
2897
2899
  });
2898
2900
 
2899
2901
  allowedCheckboxes.forEach((checkbox) => {
2902
+ checkbox.addEventListener('click', (event) => {
2903
+ if (checkbox.checked && getAllowedThemes().length === 1) {
2904
+ event.preventDefault();
2905
+ }
2906
+ });
2900
2907
  checkbox.addEventListener('change', () => {
2901
2908
  if (!getAllowedThemes().length) {
2902
2909
  checkbox.checked = true;
@@ -0,0 +1,20 @@
1
+ {% load static %}
2
+ <div class="ms-font-picker" id="msFontPreviews">
3
+ <div class="ms-density-options" role="group" aria-label="{{ MS_TRANS.typography }}">
4
+ {% for font in option_fonts %}
5
+ <button
6
+ type="button"
7
+ class="ms-density-option ms-font-option{% if selected_font == font.slug %} is-active{% endif %}"
8
+ data-font="{{ font.slug }}"
9
+ >
10
+ <span class="ms-density-option__icon ms-font-option__icon" aria-hidden="true">
11
+ <span class="font-preview-sample">Aa</span>
12
+ </span>
13
+ <span class="ms-density-option__copy">
14
+ <strong>{{ font.label }}</strong>
15
+ <small>{{ font.slug }}</small>
16
+ </span>
17
+ </button>
18
+ {% endfor %}
19
+ </div>
20
+ </div>
@@ -30,7 +30,7 @@
30
30
  </label>
31
31
  </div>
32
32
  <div class="ms-font-settings-option__content p-2 text-center">
33
- <div class="font-preview-sample h2 mb-1" style="font-family: '{{ font.label }}', sans-serif !important;">
33
+ <div class="font-preview-sample h2 mb-1">
34
34
  Aa
35
35
  </div>
36
36
  <span class="ms-choice-option__copy">
@@ -44,20 +44,3 @@
44
44
  </div>
45
45
  </div>
46
46
  </div>
47
-
48
- <style>
49
- .ms-font-settings-option__header {
50
- position: absolute;
51
- top: 8px;
52
- right: 8px;
53
- z-index: 2;
54
- }
55
- [dir="rtl"] .ms-font-settings-option__header {
56
- right: auto;
57
- left: 8px;
58
- }
59
- .ms-font-settings-option {
60
- position: relative;
61
- min-width: 120px;
62
- }
63
- </style>
@@ -1,14 +1,14 @@
1
1
  {% load microsys_tags %}
2
2
  <div class="ms-language-fonts-editor mt-4" id="msLanguageFontsEditor">
3
- <h6 class="fw-bold mb-3">{{ MS_TRANS.default_fonts_per_lang|default:"Default Fonts by Language" }}</h6>
4
- <p class="text-muted small mb-4">{{ MS_TRANS.default_fonts_per_lang_desc|default:"Choose which font to use as the default for each active language." }}</p>
3
+ <h6 class="fw-bold mb-3">{{ MS_TRANS.default_fonts_per_lang }}</h6>
4
+ <p class="text-muted small mb-4">{{ MS_TRANS.default_fonts_per_lang_desc }}</p>
5
5
 
6
6
  <div class="table-responsive">
7
7
  <table class="table table-sm table-borderless align-middle">
8
8
  <thead>
9
9
  <tr>
10
- <th width="40%">{{ MS_TRANS.language|default:"Language" }}</th>
11
- <th>{{ MS_TRANS.default_font|default:"Default Font" }}</th>
10
+ <th width="40%">{{ MS_TRANS.language }}</th>
11
+ <th>{{ MS_TRANS.default_font }}</th>
12
12
  </tr>
13
13
  </thead>
14
14
  <tbody>
@@ -301,10 +301,10 @@
301
301
  title="{{ MS_TRANS.sidebar_reorder|default:'Reorder' }}">
302
302
  <i class="bi bi-grip-vertical"></i>
303
303
  </button>
304
- <h4 class="mb-3"><i class="bi bi-fonts me-2"></i> {{ MS_TRANS.typography|default:"Typography" }}</h4>
305
- <p class="text-muted small mb-4">{{ MS_TRANS.typography_desc|default:"Choose your preferred font for the interface." }}</p>
304
+ <h4 class="mb-3"><i class="bi bi-fonts me-2"></i> {{ MS_TRANS.typography }}</h4>
305
+ <p class="text-muted small mb-4">{{ MS_TRANS.typography_desc }}</p>
306
306
 
307
- {% include 'microsys/includes/font_previews.html' with selected_font=active_font allowed_fonts=APP_CONFIG.allowed_fonts %}
307
+ {% include 'microsys/includes/font_previews.html' with selected_font=active_font option_fonts=option_fonts %}
308
308
  </div>
309
309
  </div>
310
310
  {% endif %}
@@ -73,7 +73,7 @@
73
73
  <div class="ms-table-page-size__options">
74
74
  {% for per_page_value in table.microsys_per_page_options %}
75
75
  <a
76
- class="ms-table-page-size__option{% if table.microsys_per_page == per_page_value %} is-active{% endif %}"
76
+ class="ms-table-page-size__option{% if table.microsys_per_page|stringformat:'s' == per_page_value|stringformat:'s' %} is-active{% endif %}"
77
77
  href="{% ms_querystring_multi table.prefixed_page_field '' table.microsys_per_page_field per_page_value %}"
78
78
  >
79
79
  {{ per_page_value }}
@@ -63,8 +63,8 @@ MICROSYS_STRINGS = {
63
63
  'system_settings_languages': 'اللغات',
64
64
  'system_settings_security': 'الوصول والأمان',
65
65
  'system_settings_sidebar': 'الشريط الجانبي',
66
- 'system_settings_ui_layout': 'الجداول وشريط العنوان',
67
- 'system_settings_appearance': 'المظهر والتخصيص',
66
+ 'system_settings_ui_layout': 'شريط العنوان',
67
+ 'system_settings_appearance': 'المظهر والخط',
68
68
  'system_setup_title': 'التهيئة الأولى للبرنامج',
69
69
  'system_setup_heading': 'ابدأ تهيئة Microsys',
70
70
  'system_setup_desc': 'أكمل إعدادات الهوية واللغات والشريط الجانبي والمظهر قبل البدء.',
@@ -73,8 +73,8 @@ MICROSYS_STRINGS = {
73
73
  'system_setup_step2': 'الخطوة 2: اللغات والترجمات',
74
74
  'system_setup_step3': 'الخطوة 3: الوصول والأمان',
75
75
  'system_setup_step4': 'الخطوة 4: الشريط الجانبي والتنقل',
76
- 'system_setup_step5': 'الخطوة 5: الجداول وشريط العنوان',
77
- 'system_setup_step6': 'الخطوة 6: المظهر والتخصيص',
76
+ 'system_setup_step5': 'الخطوة 5: شريط العنوان',
77
+ 'system_setup_step6': 'الخطوة 6: المظهر والخط',
78
78
  'apply_language': 'تطبيق اللغة',
79
79
 
80
80
  # System Settings Form
@@ -97,8 +97,18 @@ MICROSYS_STRINGS = {
97
97
  'help_sys_allowed_themes': 'اختر المظاهر التي تريد السماح بها داخل هذا المشروع. يجب أن يبقى المظهر الافتراضي ضمن القائمة.',
98
98
  'form_sys_allow_user_theme_override': 'السماح للمستخدم بتغيير المظهر',
99
99
  'help_sys_allow_user_theme_override': 'السماح للمستخدمين بالتبديل بين المظاهر المسموح بها من صفحة الخيارات ومن شريط أدوات الشريط الجانبي.',
100
+ 'form_sys_allowed_fonts': 'الخطوط المسموح بها',
101
+ 'help_sys_allowed_fonts': 'اختر الخطوط المتاحة للاستخدام في هذا المشروع. يجب أن تبقى الخطوط الافتراضية لكل لغة مفعلة.',
102
+ 'form_sys_allow_user_font_override': 'السماح للمستخدم بتغيير الخط',
103
+ 'help_sys_allow_user_font_override': 'السماح للمستخدمين بالتبديل بين الخطوط المسموح بها من صفحة الخيارات.',
104
+ 'form_sys_default_fonts': 'الخطوط الافتراضية حسب اللغة',
100
105
  'form_sys_allow_user_language_override': 'السماح للمستخدم بتغيير اللغة',
101
106
  'help_sys_allow_user_language_override': 'السماح للمستخدمين باختيار لغة العرض من صفحة الخيارات. عند التعطيل سيتم فرض اللغة الافتراضية للنظام.',
107
+ 'tables_settings_title': 'إعدادات الجداول',
108
+ 'typography_settings_title': 'إعدادات الخطوط والتخصيص',
109
+ 'default_fonts_per_lang': 'الخطوط الافتراضية حسب اللغة',
110
+ 'default_fonts_per_lang_desc': 'اختر الخط الافتراضي لكل لغة نشطة في النظام.',
111
+ 'default_font': 'الخط الافتراضي',
102
112
  'form_sys_default_table_density': 'الكثافة الافتراضية للجداول',
103
113
  'help_sys_default_table_density': 'اختر كثافة الجداول الافتراضية للمستخدمين الجدد، مع إمكانية تجاوزها لاحقاً من صفحة الخيارات.',
104
114
  'form_sys_logo': 'الشعار (Logo)',
@@ -145,11 +155,11 @@ MICROSYS_STRINGS = {
145
155
  'sidebar_collapse_hidden_desc': 'يطوي الشريط إلى حالة مخفية بالكامل على سطح المكتب.',
146
156
  'sidebar_collapse_locked_expanded': 'يبقى موسعاً دائماً',
147
157
  'sidebar_collapse_locked_expanded_desc': 'يعطّل طي الشريط الجانبي ويُبقيه موسعاً دائماً.',
148
- 'form_sys_titlebar_show_title': 'إظهار عنوان شريط العنوان',
158
+ 'form_sys_titlebar_show_title': 'إظهار العنوان في الشريط',
149
159
  'help_sys_titlebar_show_title': 'إظهار اسم النظام داخل شريط العنوان.',
150
- 'form_sys_titlebar_show_logo': 'إظهار شعار شريط العنوان',
160
+ 'form_sys_titlebar_show_logo': 'إظهار الشعار في الشريط',
151
161
  'help_sys_titlebar_show_logo': 'إظهار شعار الهوية بجانب العنوان.',
152
- 'form_sys_titlebar_show_home_button': 'إظهار زر الرئيسية في شريط العنوان',
162
+ 'form_sys_titlebar_show_home_button': 'إظهار زر الرئيسية في الشريط',
153
163
  'help_sys_titlebar_show_home_button': 'إظهار زر اختصار الرئيسية في شريط العنوان.',
154
164
  'form_sys_titlebar_home_shape': 'شكل زر الرئيسية',
155
165
  'form_sys_titlebar_title_align': 'محاذاة العنوان',
@@ -217,11 +227,13 @@ MICROSYS_STRINGS = {
217
227
  'form_sys_email_default_from': 'بريد المرسل الافتراضي',
218
228
  'form_sys_public_root': 'السماح بالوصول العام للصفحة الرئيسية',
219
229
  'help_sys_public_root': 'السماح للمستخدمين غير المسجلين بالدخول بالوصول إلى الرابط الرئيسي (/). عند التفعيل، لن يتم فرض التحويل إلى صفحة تسجيل الدخول.',
230
+ 'form_sys_public_registration': 'تفعيل التسجيل العام',
231
+ 'help_sys_public_registration': 'السماح للمستخدمين غير المسجلين بطلب إنشاء حساب. يتطلب التحقق من البريد الإلكتروني وتجهيز إعدادات توصيل البريد.',
220
232
  'form_sys_public_root_split_enabled': 'فصل الجذر العام للمستخدم غير المسجّل عن رابط الصفحة الرئيسية',
221
233
  'help_sys_public_root_split_enabled': 'عند التفعيل، يمكن توجيه المستخدمين غير المسجّلين إلى رابط جذر عام منفصل بينما يستمر المستخدمون المسجّلون في استخدام رابط الصفحة الرئيسية الأساسي.',
222
234
  'root_home_settings_title': 'وجهات الصفحة الرئيسية والجذر العام',
223
- 'form_sys_titlebar_hide_on_public_unauthenticated_index': 'إخفاء الشريط العلوي في الصفحة العامة للمستخدم غير المسجّل',
224
- 'help_sys_titlebar_hide_on_public_unauthenticated_index': 'إخفاء الشريط العلوي عندما يفتح مستخدم غير مسجّل صفحة الجذر/الرئيسية العامة.',
235
+ 'form_sys_titlebar_hide_on_public_unauthenticated_index': 'إخفاء الشريط في الصفحة العامة للمستخدم غير المسجّل',
236
+ 'help_sys_titlebar_hide_on_public_unauthenticated_index': 'إخفاء الشريط العلوي لأي مستخدم غير مسجّل في الصفحة الرئيسية العامة.',
225
237
  'sidebar_disabled_navigation_note': 'تعطيل الشريط الجانبي قد يجعل التطبيق بلا تنقل مدمج. ستحتاج إلى الاعتماد على لوحات المعلومات والنوافذ المنبثقة، أو إضافة أزرار رجوع وروابط تنقل مخصصة داخل النماذج والقوائم ولوحات المعلومات. ابتداءً من v2.2.0، مدير الأقسام الديناميكي متاح فقط من خلال الشريط الجانبي، لذلك أضف له زراً في لوحة معلومات أو مدخلاً مخصصاً إذا كنت تحتاج الوصول إليه. سيتم تحديث هذا التحذير مستقبلاً إذا تمت إضافة بديل مدمج.',
226
238
  'sidebar_toolbar_disable_note': 'تعطيل شريط أدوات الشريط الجانبي يزيل أيضاً الاختصار المدمج الوحيد لمدير الأقسام الديناميكي. إذا كنت لا تزال تريد الوصول إليه من الواجهة، فعّل عناصر النظام داخل منشئ الشريط الجانبي ثم أضف إدارة الأقسام إلى الشريط الجانبي.',
227
239
  'btn_save': 'حفظ التعديلات',
@@ -292,6 +304,8 @@ MICROSYS_STRINGS = {
292
304
  'microsys_version': 'إصدار النظام (microSYS)',
293
305
  'themes': 'سمة الألوان',
294
306
  'themes_desc': 'اختر مظهر الألوان المفضل لديك لواجهة المنظومة.',
307
+ 'typography': 'الخطوط والطباعة',
308
+ 'typography_desc': 'اختر الخط المفضل لديك لواجهة المنظومة.',
295
309
  'table_density': 'كثافة الجداول',
296
310
  'table_density_desc': 'تحكم في مقدار المساحة الرأسية التي تستخدمها الجداول أثناء العمل.',
297
311
  'sidebar_density': 'كثافة الشريط الجانبي',
@@ -912,7 +926,7 @@ MICROSYS_STRINGS = {
912
926
  'system_settings_languages': 'Languages',
913
927
  'system_settings_security': 'Access & Security',
914
928
  'system_settings_sidebar': 'Sidebar',
915
- 'system_settings_ui_layout': 'Tables & Titlebar',
929
+ 'system_settings_ui_layout': 'Titlebar',
916
930
  'system_settings_appearance': 'Themes & Typography',
917
931
  'system_setup_title': 'Initial System Setup',
918
932
  'system_setup_heading': 'Set up Microsys',
@@ -922,7 +936,7 @@ MICROSYS_STRINGS = {
922
936
  'system_setup_step2': 'Step 2: Localization',
923
937
  'system_setup_step3': 'Step 3: Access & Security',
924
938
  'system_setup_step4': 'Step 4: Navigation',
925
- 'system_setup_step5': 'Step 5: Tables & Titlebar',
939
+ 'system_setup_step5': 'Step 5: Titlebar',
926
940
  'system_setup_step6': 'Step 6: Themes & Typography',
927
941
  'apply_language': 'Apply language',
928
942
 
@@ -944,12 +958,22 @@ MICROSYS_STRINGS = {
944
958
  'selector_search_pages': 'Search discovered pages',
945
959
  'form_sys_default_lang': 'Default Language',
946
960
  'form_sys_default_theme': 'Default Theme',
947
- 'form_sys_allowed_themes': 'Allowed Themes',
961
+ 'form_sys_allowed_themes': 'Allowed themes',
948
962
  'help_sys_allowed_themes': 'Choose which themes are available in this project. The default theme must remain enabled.',
949
- 'form_sys_allow_user_theme_override': 'Allow User Theme Override',
950
- 'help_sys_allow_user_theme_override': 'Allow users to switch between the allowed themes from Options and the sidebar toolbar.',
963
+ 'form_sys_allow_user_theme_override': 'Allow user theme override',
964
+ 'help_sys_allow_user_theme_override': 'Allow users to switch between the allowed themes at runtime from Options and the sidebar toolbar.',
965
+ 'form_sys_allowed_fonts': 'Allowed fonts',
966
+ 'help_sys_allowed_fonts': 'Choose which fonts are available in this project. The default fonts for each language must remain enabled.',
967
+ 'form_sys_allow_user_font_override': 'Allow user font override',
968
+ 'help_sys_allow_user_font_override': 'Allow users to switch between the allowed fonts at runtime from Options.',
969
+ 'form_sys_default_fonts': 'Default fonts by language',
951
970
  'form_sys_allow_user_language_override': 'Allow User Language Override',
952
971
  'help_sys_allow_user_language_override': 'Allow users to change their display language from Options. When disabled, the system default language is enforced.',
972
+ 'tables_settings_title': 'Tables Settings',
973
+ 'typography_settings_title': 'Typography Settings',
974
+ 'default_fonts_per_lang': 'Default Fonts by Language',
975
+ 'default_fonts_per_lang_desc': 'Choose which font to use as the default for each active language.',
976
+ 'default_font': 'Default Font',
953
977
  'form_sys_default_table_density': 'Default Table Density',
954
978
  'help_sys_default_table_density': 'Choose the default table density for new users; each user can still override it later from Options.',
955
979
  'form_sys_logo': 'System Logo (Logo)',
@@ -1068,6 +1092,8 @@ MICROSYS_STRINGS = {
1068
1092
  'form_sys_email_default_from': 'Default from email',
1069
1093
  'form_sys_public_root': 'Public Root Access',
1070
1094
  'help_sys_public_root': 'Allow anonymous (non-logged-in) users to access the root URL (/). When enabled, the system will not force-redirect to login.',
1095
+ 'form_sys_public_registration': 'Enable Public Registration',
1096
+ 'help_sys_public_registration': 'Allow anonymous users to request an account. Email verification is mandatory and SMTP/email delivery must be configured.',
1071
1097
  'form_sys_public_root_split_enabled': 'Separate anonymous public root from Home URL',
1072
1098
  'help_sys_public_root_split_enabled': 'When enabled, anonymous users can be redirected to a separate Public Root URL while authenticated users still use the main Home URL.',
1073
1099
  'root_home_settings_title': 'Home & Public Root Destinations',
@@ -1139,8 +1165,10 @@ MICROSYS_STRINGS = {
1139
1165
  'cache': 'Cache',
1140
1166
  'tasks': 'Task Server',
1141
1167
  'microsys_version': 'microSYS Version',
1142
- 'themes': 'Themes',
1143
- 'themes_desc': 'Choose your preferred color scheme for the interface.',
1168
+ 'themes': 'Color Theme',
1169
+ 'themes_desc': 'Choose your preferred color theme for the interface.',
1170
+ 'typography': 'Typography',
1171
+ 'typography_desc': 'Choose your preferred font for the interface.',
1144
1172
  'table_density': 'Table Density',
1145
1173
  'table_density_desc': 'Control how much vertical space data tables use while you work.',
1146
1174
  'sidebar_density': 'Sidebar Density',