modoboa 2.4.10__py3-none-any.whl → 2.5.0__py3-none-any.whl

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 (331) hide show
  1. modoboa/admin/api/v1/serializers.py +1 -0
  2. modoboa/admin/api/v2/serializers.py +15 -1
  3. modoboa/admin/api/v2/tests.py +11 -1
  4. modoboa/admin/lib.py +1 -1
  5. modoboa/admin/models/mailbox.py +15 -13
  6. modoboa/admin/models/mxrecord.py +4 -0
  7. modoboa/admin/tests/test_import_.py +11 -11
  8. modoboa/amavis/__init__.py +3 -0
  9. modoboa/amavis/app_settings.py +276 -0
  10. modoboa/amavis/apps.py +18 -0
  11. modoboa/amavis/checks/__init__.py +2 -0
  12. modoboa/amavis/checks/settings_checks.py +59 -0
  13. modoboa/amavis/dbrouter.py +35 -0
  14. modoboa/amavis/factories.py +164 -0
  15. modoboa/amavis/handlers.py +146 -0
  16. modoboa/amavis/lib.py +381 -0
  17. modoboa/amavis/management/__init__.py +0 -0
  18. modoboa/amavis/management/commands/__init__.py +0 -0
  19. modoboa/amavis/management/commands/amnotify.py +99 -0
  20. modoboa/amavis/management/commands/qcleanup.py +84 -0
  21. modoboa/amavis/migrations/0001_initial.py +340 -0
  22. modoboa/amavis/migrations/__init__.py +0 -0
  23. modoboa/amavis/models.py +226 -0
  24. modoboa/amavis/serializers.py +139 -0
  25. modoboa/amavis/sql_connector.py +240 -0
  26. modoboa/amavis/sql_email.py +66 -0
  27. modoboa/amavis/tasks.py +33 -0
  28. modoboa/amavis/templates/amavis/notifications/pending_requests.html +16 -0
  29. modoboa/amavis/tests/__init__.py +0 -0
  30. modoboa/amavis/tests/sa-learn +3 -0
  31. modoboa/amavis/tests/sample_messages/quarantined-input.txt +80 -0
  32. modoboa/amavis/tests/sample_messages/quarantined-output-plain_nolinks.txt +17 -0
  33. modoboa/amavis/tests/spamc +3 -0
  34. modoboa/amavis/tests/test_checks.py +25 -0
  35. modoboa/amavis/tests/test_handlers.py +214 -0
  36. modoboa/amavis/tests/test_lib.py +90 -0
  37. modoboa/amavis/tests/test_management_commands.py +45 -0
  38. modoboa/amavis/tests/test_sql_email.py +67 -0
  39. modoboa/amavis/tests/test_utils.py +19 -0
  40. modoboa/amavis/tests/test_viewsets.py +319 -0
  41. modoboa/amavis/urls.py +11 -0
  42. modoboa/amavis/utils.py +105 -0
  43. modoboa/amavis/viewsets.py +265 -0
  44. modoboa/core/api/v1/serializers.py +7 -5
  45. modoboa/core/api/v2/serializers.py +13 -2
  46. modoboa/core/api/v2/tests.py +34 -4
  47. modoboa/core/api/v2/urls.py +10 -5
  48. modoboa/core/api/v2/views.py +23 -2
  49. modoboa/core/api/v2/viewsets.py +24 -3
  50. modoboa/core/app_settings.py +11 -0
  51. modoboa/core/fido2_auth.py +1 -2
  52. modoboa/core/handlers.py +6 -2
  53. modoboa/core/management/commands/add_allowed_hosts.py +33 -0
  54. modoboa/core/management/commands/cleanlogs.py +9 -0
  55. modoboa/core/management/commands/load_initial_data.py +10 -0
  56. modoboa/core/migrations/0025_rename_user_email_is_active_core_user_email_c0c03f_idx.py +23 -5
  57. modoboa/core/tests/test_core.py +29 -0
  58. modoboa/core/utils.py +6 -0
  59. modoboa/dnstools/api/v2/serializers.py +9 -11
  60. modoboa/frontend_dist/assets/AccountAliasForm-BuSy_1n9.js +1 -0
  61. modoboa/frontend_dist/assets/AccountEditView-qdJmLM_e.js +1 -0
  62. modoboa/frontend_dist/assets/AccountLayout-DrN7vHsX.js +1 -0
  63. modoboa/frontend_dist/assets/AccountPasswordSubForm-DZGt_Xgq.js +1 -0
  64. modoboa/frontend_dist/assets/AccountView-CO65y0vZ.js +1 -0
  65. modoboa/frontend_dist/assets/AddressBook-BZNUlhek.js +1 -0
  66. modoboa/frontend_dist/assets/AdminLayout-CTNhuwTw.js +1 -0
  67. modoboa/frontend_dist/assets/AlarmsView-9yKGbmkC.css +1 -0
  68. modoboa/frontend_dist/assets/AlarmsView-DN_JIw9g.js +1 -0
  69. modoboa/frontend_dist/assets/AliasEditView-DjpPUTp9.js +1 -0
  70. modoboa/frontend_dist/assets/{AliasRecipientForm-DVZXWaUX.js → AliasRecipientForm-B1Y8wFdP.js} +1 -1
  71. modoboa/frontend_dist/assets/AliasView-GOJ5lyQH.js +1 -0
  72. modoboa/frontend_dist/assets/AuditTrailView-fbXmq70e.js +1 -0
  73. modoboa/frontend_dist/assets/CalendarView-LlQQNEPL.js +1 -0
  74. modoboa/frontend_dist/assets/{ChoiceField-7eU7c_rI.js → ChoiceField-B3ReQHVe.js} +1 -1
  75. modoboa/frontend_dist/assets/ComposeEmailForm-Bs1fZXAL.js +1 -0
  76. modoboa/frontend_dist/assets/ComposeEmailView-s3LMl3pO.js +1 -0
  77. modoboa/frontend_dist/assets/ConfirmDialog-DY_kUHLG.js +1 -0
  78. modoboa/frontend_dist/assets/{ConnectedLayout-BaJZ3BeR.css → ConnectedLayout-Bxh21hcH.css} +1 -1
  79. modoboa/frontend_dist/assets/ConnectedLayout-UWjiYBNw.js +1 -0
  80. modoboa/frontend_dist/assets/CreationForm-ORg3fazt.js +1 -0
  81. modoboa/frontend_dist/assets/DashboardView-Dplk9itS.js +1 -0
  82. modoboa/frontend_dist/assets/{DashboardView-BLlMi6Qb.css → DashboardView-gwwVAPvt.css} +1 -1
  83. modoboa/frontend_dist/assets/DomainAdminList-DVn9x0rB.js +1 -0
  84. modoboa/frontend_dist/assets/DomainEditView-nAoL64D_.js +1 -0
  85. modoboa/frontend_dist/assets/{DomainTransportForm-DPnPGBOp.js → DomainTransportForm-CA-DNUxX.js} +1 -1
  86. modoboa/frontend_dist/assets/{DomainView-BDKoBFYr.css → DomainView-CCLYXPHx.css} +1 -1
  87. modoboa/frontend_dist/assets/DomainView-CdXPpwJG.js +5 -0
  88. modoboa/frontend_dist/assets/DomainsView-B_59gowf.js +1 -0
  89. modoboa/frontend_dist/assets/DomainsView-DZ-ss9bI.css +1 -0
  90. modoboa/frontend_dist/assets/EmailField-CwcwI5xW.js +1 -0
  91. modoboa/frontend_dist/assets/EmailView-BshxcfAK.js +1 -0
  92. modoboa/frontend_dist/assets/EmptyLayout-DFfhnhLi.js +1 -0
  93. modoboa/frontend_dist/assets/FiltersView-Cf20MSTK.js +1 -0
  94. modoboa/frontend_dist/assets/ForwardEmailView-CZG062os.js +1 -0
  95. modoboa/frontend_dist/assets/{HtmlEditor-uM4AtIGi.js → HtmlEditor-Bh4c689R.js} +1 -1
  96. modoboa/frontend_dist/assets/IdentitiesView-BXAuU1YX.js +1 -0
  97. modoboa/frontend_dist/assets/{IdentitiesView-jmuItyMZ.css → IdentitiesView-DPrrRMS5.css} +1 -1
  98. modoboa/frontend_dist/assets/InformationView-C9vvvQhJ.css +1 -0
  99. modoboa/frontend_dist/assets/InformationView-Cn5FZW7H.js +1 -0
  100. modoboa/frontend_dist/assets/{LoadingData-CVD2Aen8.js → LoadingData-CdVvm4FI.js} +1 -1
  101. modoboa/frontend_dist/assets/{LoginCallbackView-sWzBke1g.js → LoginCallbackView-B9hAH4MI.js} +1 -1
  102. modoboa/frontend_dist/assets/{LoginView-wmN73W0f.js → LoginView-tHIR4Adc.js} +1 -1
  103. modoboa/frontend_dist/assets/MailboxView-Bugu2vhg.js +1 -0
  104. modoboa/frontend_dist/assets/MenuItems-PXjiG-fs.js +1 -0
  105. modoboa/frontend_dist/assets/MessageView-Cy4STShm.js +1 -0
  106. modoboa/frontend_dist/assets/MessagesView-DdkuEgfX.js +1 -0
  107. modoboa/frontend_dist/assets/MigrationsView-CidSEjCF.js +1 -0
  108. modoboa/frontend_dist/assets/{ParametersForm-BCeQljir.js → ParametersForm-CAv4SH-E.js} +1 -1
  109. modoboa/frontend_dist/assets/ParametersView-CX7Ffemw.js +1 -0
  110. modoboa/frontend_dist/assets/ParametersView-CrbNcmV3.js +1 -0
  111. modoboa/frontend_dist/assets/ProviderEditView-CrltAQXl.js +1 -0
  112. modoboa/frontend_dist/assets/ProviderGeneralForm-BYAzVnXM.js +1 -0
  113. modoboa/frontend_dist/assets/ProvidersView-osjIY4Ex.js +1 -0
  114. modoboa/frontend_dist/assets/QuarantineLayout-B8EcU9vS.js +1 -0
  115. modoboa/frontend_dist/assets/QuarantineView-D4gOE4EQ.css +1 -0
  116. modoboa/frontend_dist/assets/QuarantineView-D8Qg0MXA.js +1 -0
  117. modoboa/frontend_dist/assets/ReplyEmailView-BABPqWhd.js +1 -0
  118. modoboa/frontend_dist/assets/ResourcesForm-OaqdRYVs.js +1 -0
  119. modoboa/frontend_dist/assets/SelfServiceLayout-d277YTGR.js +1 -0
  120. modoboa/frontend_dist/assets/SettingsView-9iNcDhkI.js +6 -0
  121. modoboa/frontend_dist/assets/StatisticsView-cHsPyGkL.js +1 -0
  122. modoboa/frontend_dist/assets/TimeSerieChart--V83dcJ9.js +1 -0
  123. modoboa/frontend_dist/assets/UserLayout-B3sBiTcZ.js +1 -0
  124. modoboa/frontend_dist/assets/VAlert-BuaaYN2h.js +1 -0
  125. modoboa/frontend_dist/assets/VApp-CKP-6zGP.js +1 -0
  126. modoboa/frontend_dist/assets/VAutocomplete-Dwv6_Rzq.js +1 -0
  127. modoboa/frontend_dist/assets/VAvatar-Cmga0vj6.js +1 -0
  128. modoboa/frontend_dist/assets/VBadge-BQrRJ9S0.css +1 -0
  129. modoboa/frontend_dist/assets/VBadge-CixeK87a.js +1 -0
  130. modoboa/frontend_dist/assets/VCard-CxH9DWoK.js +1 -0
  131. modoboa/frontend_dist/assets/VCheckbox-62GOpvvP.js +1 -0
  132. modoboa/frontend_dist/assets/{VCheckboxBtn-j7di4leN.js → VCheckboxBtn-DMoNtKT8.js} +1 -1
  133. modoboa/frontend_dist/assets/VChip-D_styETR.js +1 -0
  134. modoboa/frontend_dist/assets/VColorPicker-BHscBGQV.js +1 -0
  135. modoboa/frontend_dist/assets/VContainer-B46caNs1.js +1 -0
  136. modoboa/frontend_dist/assets/VDataTable-Bh8NbVSx.js +1 -0
  137. modoboa/frontend_dist/assets/VDataTableServer-BDR5hOmo.js +1 -0
  138. modoboa/frontend_dist/assets/VDataTableVirtual-BOQlNtIG.js +1 -0
  139. modoboa/frontend_dist/assets/{VDialog-CZqM2Ofu.js → VDialog-BcTg7w6P.js} +1 -1
  140. modoboa/frontend_dist/assets/VExpansionPanels-BmH5Jl2Z.js +1 -0
  141. modoboa/frontend_dist/assets/VFileInput-BC4yAygd.js +1 -0
  142. modoboa/frontend_dist/assets/VForm-D5iPGkde.js +1 -0
  143. modoboa/frontend_dist/assets/VInput-CcxkaOXT.css +1 -0
  144. modoboa/frontend_dist/assets/VInput-CoDJzvaW.js +1 -0
  145. modoboa/frontend_dist/assets/VMenu-gUG70-zD.js +1 -0
  146. modoboa/frontend_dist/assets/VPicker-BXuKT3zB.js +1 -0
  147. modoboa/frontend_dist/assets/VProgressCircular-BtOPiGCg.js +1 -0
  148. modoboa/frontend_dist/assets/VRadioGroup-DIFZKSn-.js +1 -0
  149. modoboa/frontend_dist/assets/{VRow-C_Ydf6yr.js → VRow-ozg66L7j.js} +1 -1
  150. modoboa/frontend_dist/assets/VSelect-C3RjAa45.js +1 -0
  151. modoboa/frontend_dist/assets/VSelectionControl-zyz-fJvC.js +1 -0
  152. modoboa/frontend_dist/assets/VSheet-BNx2X4Mk.js +1 -0
  153. modoboa/frontend_dist/assets/VSpacer-DinPiXs9.js +1 -0
  154. modoboa/frontend_dist/assets/VSwitch-DwxdeAEq.js +1 -0
  155. modoboa/frontend_dist/assets/VTable-DaLxa4FO.js +1 -0
  156. modoboa/frontend_dist/assets/VTabs-BP0Hgsgm.js +1 -0
  157. modoboa/frontend_dist/assets/VTextField-BzBVKKob.css +1 -0
  158. modoboa/frontend_dist/assets/VTextField-XoGTj1KG.js +1 -0
  159. modoboa/frontend_dist/assets/VTextarea-wBlRMIv_.js +1 -0
  160. modoboa/frontend_dist/assets/VToolbar-CFZfqeOr.js +1 -0
  161. modoboa/frontend_dist/assets/VWindowItem-BB7ETW3b.js +1 -0
  162. modoboa/frontend_dist/assets/WebmailLayout-_Hk1XhVq.js +1 -0
  163. modoboa/frontend_dist/assets/accounts-DUzbx6k8.js +1 -0
  164. modoboa/frontend_dist/assets/admin-DewTk2H8.js +1 -0
  165. modoboa/frontend_dist/assets/{aliases-c3n-dCV_.js → aliases-4sXmjwXp.js} +1 -1
  166. modoboa/frontend_dist/assets/amavis-CC0li7_T.js +1 -0
  167. modoboa/frontend_dist/assets/amavis-DK8SHE6o.js +1 -0
  168. modoboa/frontend_dist/assets/{contacts-Bqckz8sr.js → contacts-BjghrPqZ.js} +1 -1
  169. modoboa/frontend_dist/assets/{domains-nBMR-fRf.js → domains-BSawReeu.js} +1 -1
  170. modoboa/frontend_dist/assets/{domains.store-ChZgLcqP.js → domains.store-D-vWCEIK.js} +1 -1
  171. modoboa/frontend_dist/assets/{filter-D7NrAf6a.js → filter-C82FUCw_.js} +1 -1
  172. modoboa/frontend_dist/assets/forwardRefs-cvcnlhoK.js +1 -0
  173. modoboa/frontend_dist/assets/global.store-DbkcI5o2.js +1 -0
  174. modoboa/frontend_dist/assets/{importExport-Dn9vYw7T.js → importExport-DzoL4Mvc.js} +1 -1
  175. modoboa/frontend_dist/assets/index-BImkz5Jx.js +984 -0
  176. modoboa/frontend_dist/assets/index-DuzUMVLM.js +1 -0
  177. modoboa/frontend_dist/assets/layout-C5FyYCHK.js +1 -0
  178. modoboa/frontend_dist/assets/{layout.store-BxBoBlgf.js → layout.store-NXWtFIwL.js} +1 -1
  179. modoboa/frontend_dist/assets/logos-BswdveCV.js +1 -0
  180. modoboa/frontend_dist/assets/{logs-DrTzylW7.js → logs-6CbtfaZS.js} +1 -1
  181. modoboa/frontend_dist/assets/{parameters-Lgiqp7aw.js → parameters-aSQiR7kN.js} +1 -1
  182. modoboa/frontend_dist/assets/{parameters.store-C9k9DuTj.js → parameters.store-CzQqVatx.js} +1 -1
  183. modoboa/frontend_dist/assets/permissions-DNoefz-n.js +1 -0
  184. modoboa/frontend_dist/assets/{ssrBoot-BsxW6uW4.js → ssrBoot-CKUX4kcb.js} +1 -1
  185. modoboa/frontend_dist/assets/{tag-CFK9dzMJ.js → tag-B_yWNNJD.js} +1 -1
  186. modoboa/frontend_dist/assets/transports-BDNB9wR5.js +1 -0
  187. modoboa/frontend_dist/assets/{webmail-KrD8ZhNM.js → webmail-CdU6CD9b.js} +1 -1
  188. modoboa/frontend_dist/index.html +1 -1
  189. modoboa/lib/email_utils.py +2 -2
  190. modoboa/lib/permissions.py +7 -0
  191. modoboa/lib/redis.py +1 -5
  192. modoboa/lib/test_runners.py +29 -0
  193. modoboa/lib/tests/__init__.py +5 -1
  194. modoboa/locale/br/LC_MESSAGES/django.po +87 -75
  195. modoboa/locale/cs/LC_MESSAGES/django.po +82 -74
  196. modoboa/locale/cs_CZ/LC_MESSAGES/django.po +145 -121
  197. modoboa/locale/de/LC_MESSAGES/django.mo +0 -0
  198. modoboa/locale/de/LC_MESSAGES/django.po +339 -651
  199. modoboa/locale/de_DE/LC_MESSAGES/django.po +87 -75
  200. modoboa/locale/el_GR/LC_MESSAGES/django.po +160 -135
  201. modoboa/locale/en/LC_MESSAGES/django.po +82 -74
  202. modoboa/locale/es/LC_MESSAGES/django.po +158 -131
  203. modoboa/locale/es_MX/LC_MESSAGES/django.po +82 -74
  204. modoboa/locale/fi/LC_MESSAGES/django.po +87 -75
  205. modoboa/locale/fr/LC_MESSAGES/django.mo +0 -0
  206. modoboa/locale/fr/LC_MESSAGES/django.po +469 -201
  207. modoboa/locale/hu/LC_MESSAGES/django.po +82 -74
  208. modoboa/locale/it/LC_MESSAGES/django.mo +0 -0
  209. modoboa/locale/it/LC_MESSAGES/django.po +148 -122
  210. modoboa/locale/ja_JP/LC_MESSAGES/django.mo +0 -0
  211. modoboa/locale/ja_JP/LC_MESSAGES/django.po +201 -334
  212. modoboa/locale/ka/LC_MESSAGES/django.po +82 -74
  213. modoboa/locale/nl_NL/LC_MESSAGES/django.po +160 -132
  214. modoboa/locale/no/LC_MESSAGES/django.po +82 -74
  215. modoboa/locale/pl_PL/LC_MESSAGES/django.mo +0 -0
  216. modoboa/locale/pl_PL/LC_MESSAGES/django.po +172 -149
  217. modoboa/locale/pt_BR/LC_MESSAGES/django.mo +0 -0
  218. modoboa/locale/pt_BR/LC_MESSAGES/django.po +172 -144
  219. modoboa/locale/pt_PT/LC_MESSAGES/django.po +135 -112
  220. modoboa/locale/ro_RO/LC_MESSAGES/django.po +87 -75
  221. modoboa/locale/ru/LC_MESSAGES/django.po +142 -118
  222. modoboa/locale/si/LC_MESSAGES/django.po +82 -74
  223. modoboa/locale/sk/LC_MESSAGES/django.po +82 -74
  224. modoboa/locale/sk_SK/LC_MESSAGES/django.po +84 -76
  225. modoboa/locale/sl_SI/LC_MESSAGES/django.po +90 -76
  226. modoboa/locale/sv/LC_MESSAGES/django.mo +0 -0
  227. modoboa/locale/sv/LC_MESSAGES/django.po +172 -139
  228. modoboa/locale/tr/LC_MESSAGES/django.po +87 -75
  229. modoboa/locale/tr_TR/LC_MESSAGES/django.po +84 -74
  230. modoboa/locale/uk/LC_MESSAGES/django.po +82 -74
  231. modoboa/locale/zh/LC_MESSAGES/django.po +82 -74
  232. modoboa/locale/zh_CN/LC_MESSAGES/django.po +82 -74
  233. modoboa/locale/zh_TW/LC_MESSAGES/django.po +87 -75
  234. modoboa/parameters/api/v2/tests.py +2 -2
  235. modoboa/parameters/api/v2/viewsets.py +2 -0
  236. modoboa/policyd/tests.py +2 -0
  237. modoboa/urls_api_v2.py +6 -0
  238. {modoboa-2.4.10.dist-info → modoboa-2.5.0.dist-info}/METADATA +6 -4
  239. {modoboa-2.4.10.dist-info → modoboa-2.5.0.dist-info}/RECORD +244 -193
  240. modoboa/frontend_dist/assets/AccountAliasForm-DVXatAhB.js +0 -1
  241. modoboa/frontend_dist/assets/AccountEditView-DmvQjxpx.js +0 -1
  242. modoboa/frontend_dist/assets/AccountLayout-OGtZvlHR.js +0 -1
  243. modoboa/frontend_dist/assets/AccountPasswordSubForm-g3IEGrgM.js +0 -1
  244. modoboa/frontend_dist/assets/AccountView-DsxYqr3k.js +0 -1
  245. modoboa/frontend_dist/assets/AddressBook-3RoKiKon.js +0 -1
  246. modoboa/frontend_dist/assets/AdminLayout-CWfn8zaQ.js +0 -1
  247. modoboa/frontend_dist/assets/AlarmsView-Bheey-gp.css +0 -1
  248. modoboa/frontend_dist/assets/AlarmsView-D3Mh8ntf.js +0 -1
  249. modoboa/frontend_dist/assets/AliasEditView-C15eUZ11.js +0 -1
  250. modoboa/frontend_dist/assets/AliasView-Cyvc5vMb.js +0 -1
  251. modoboa/frontend_dist/assets/AuditTrailView-LI2XuLLn.js +0 -1
  252. modoboa/frontend_dist/assets/CalendarView-BpOlPh3f.js +0 -1
  253. modoboa/frontend_dist/assets/ComposeEmailForm-CNfI7ept.js +0 -1
  254. modoboa/frontend_dist/assets/ComposeEmailView-B866Xsrc.js +0 -1
  255. modoboa/frontend_dist/assets/ConfirmDialog-BvqxQsD1.js +0 -1
  256. modoboa/frontend_dist/assets/ConnectedLayout-BQ3ug6Jh.js +0 -1
  257. modoboa/frontend_dist/assets/CreationForm-C9Kh05ax.js +0 -1
  258. modoboa/frontend_dist/assets/DashboardView-Cw-gIcuB.js +0 -1
  259. modoboa/frontend_dist/assets/DomainAdminList-CsWUNKVk.js +0 -1
  260. modoboa/frontend_dist/assets/DomainEditView-DocxeOeW.js +0 -1
  261. modoboa/frontend_dist/assets/DomainView-Djc_0PsF.js +0 -5
  262. modoboa/frontend_dist/assets/DomainsView-B-Lxru7P.js +0 -1
  263. modoboa/frontend_dist/assets/DomainsView-DasJ0NdZ.css +0 -1
  264. modoboa/frontend_dist/assets/EmailField-BEKxuYni.js +0 -1
  265. modoboa/frontend_dist/assets/EmailView-BsR1Wes5.js +0 -1
  266. modoboa/frontend_dist/assets/EmptyLayout-BzPFOeLU.js +0 -1
  267. modoboa/frontend_dist/assets/FiltersView-5rmpC5cC.js +0 -1
  268. modoboa/frontend_dist/assets/ForwardEmailView-D7MbetcT.js +0 -1
  269. modoboa/frontend_dist/assets/IdentitiesView-BqjD9Lue.js +0 -1
  270. modoboa/frontend_dist/assets/InformationView-CghcvPn2.js +0 -1
  271. modoboa/frontend_dist/assets/InformationView-U5Ww-Sx1.css +0 -1
  272. modoboa/frontend_dist/assets/MailboxView-T_p-_ZtJ.js +0 -1
  273. modoboa/frontend_dist/assets/MenuItems-kHCMzR5E.js +0 -1
  274. modoboa/frontend_dist/assets/MessagesView-Cerv3xsy.js +0 -1
  275. modoboa/frontend_dist/assets/MigrationsView-7kjqPyYU.js +0 -1
  276. modoboa/frontend_dist/assets/ParametersView-DspBxVMV.js +0 -1
  277. modoboa/frontend_dist/assets/ParametersView-Dy0H5ep1.js +0 -1
  278. modoboa/frontend_dist/assets/ProviderEditView-DDLMOylC.js +0 -1
  279. modoboa/frontend_dist/assets/ProviderGeneralForm-BwOSKNHK.js +0 -1
  280. modoboa/frontend_dist/assets/ProvidersView-C99UD0WB.js +0 -1
  281. modoboa/frontend_dist/assets/ReplyEmailView-DAPBHldd.js +0 -1
  282. modoboa/frontend_dist/assets/ResourcesForm-D87PHeH0.js +0 -1
  283. modoboa/frontend_dist/assets/SettingsView-OxDo9wNd.js +0 -6
  284. modoboa/frontend_dist/assets/StatisticsView-CM__Eqku.js +0 -1
  285. modoboa/frontend_dist/assets/TimeSerieChart-C6j0uYR_.js +0 -1
  286. modoboa/frontend_dist/assets/UserLayout-CckCGnPS.js +0 -1
  287. modoboa/frontend_dist/assets/VAlert-B7mzOJIO.js +0 -1
  288. modoboa/frontend_dist/assets/VApp-BKxnjOoi.js +0 -1
  289. modoboa/frontend_dist/assets/VAutocomplete-Cc4_tcl1.js +0 -1
  290. modoboa/frontend_dist/assets/VAvatar-BAgTUIvX.js +0 -1
  291. modoboa/frontend_dist/assets/VCard-DFWiFORP.js +0 -1
  292. modoboa/frontend_dist/assets/VCheckbox-CKsH_vq3.js +0 -1
  293. modoboa/frontend_dist/assets/VChip-nZ0uhY7t.js +0 -1
  294. modoboa/frontend_dist/assets/VColorPicker-Os2aeP6J.js +0 -1
  295. modoboa/frontend_dist/assets/VContainer-Btam4lk2.js +0 -1
  296. modoboa/frontend_dist/assets/VDataTable-D_0_xJTl.js +0 -1
  297. modoboa/frontend_dist/assets/VDataTableServer-BWTt4Mzi.js +0 -1
  298. modoboa/frontend_dist/assets/VDataTableVirtual-BwVmkt4u.js +0 -1
  299. modoboa/frontend_dist/assets/VExpansionPanels-B5D6GOa3.js +0 -1
  300. modoboa/frontend_dist/assets/VFileInput-Cv9DIPki.js +0 -1
  301. modoboa/frontend_dist/assets/VForm-CpoZf60D.js +0 -1
  302. modoboa/frontend_dist/assets/VMenu-B_dVqOmo.js +0 -1
  303. modoboa/frontend_dist/assets/VPicker-CXkIGEze.js +0 -1
  304. modoboa/frontend_dist/assets/VProgressCircular-CrEXxs7k.js +0 -1
  305. modoboa/frontend_dist/assets/VRadioGroup-D8ypjYOO.js +0 -1
  306. modoboa/frontend_dist/assets/VSelect-CS51PDEt.js +0 -1
  307. modoboa/frontend_dist/assets/VSelectionControl-DiOqtY38.js +0 -1
  308. modoboa/frontend_dist/assets/VSheet-5VVWtHvs.js +0 -1
  309. modoboa/frontend_dist/assets/VSpacer-C6PZ3X24.js +0 -1
  310. modoboa/frontend_dist/assets/VSwitch-Chg5o-Cp.js +0 -1
  311. modoboa/frontend_dist/assets/VTable-KsiZ3cBz.js +0 -1
  312. modoboa/frontend_dist/assets/VTabs-tNrJIYO0.js +0 -1
  313. modoboa/frontend_dist/assets/VTextField-C-J20yj_.css +0 -1
  314. modoboa/frontend_dist/assets/VTextField-CLwRV0Cb.js +0 -1
  315. modoboa/frontend_dist/assets/VTextarea-Di8jbl8m.js +0 -1
  316. modoboa/frontend_dist/assets/VToolbar-CGwhgdmI.js +0 -1
  317. modoboa/frontend_dist/assets/VWindowItem-wWSFAGI-.js +0 -1
  318. modoboa/frontend_dist/assets/WebmailLayout-DsYThBrz.js +0 -1
  319. modoboa/frontend_dist/assets/admin-CJVLMHh9.js +0 -1
  320. modoboa/frontend_dist/assets/forwardRefs-Cpc3YYl6.js +0 -1
  321. modoboa/frontend_dist/assets/global.store-DUP26-A5.js +0 -1
  322. modoboa/frontend_dist/assets/index-DV9Li2cg.js +0 -852
  323. modoboa/frontend_dist/assets/index-DzL89N4E.js +0 -1
  324. modoboa/frontend_dist/assets/layout-CHO37cA6.js +0 -1
  325. modoboa/frontend_dist/assets/permissions-mL5hLHcW.js +0 -1
  326. modoboa/frontend_dist/assets/transports-TO08iTXJ.js +0 -1
  327. {modoboa-2.4.10.data → modoboa-2.5.0.data}/scripts/modoboa-admin.py +0 -0
  328. {modoboa-2.4.10.dist-info → modoboa-2.5.0.dist-info}/WHEEL +0 -0
  329. {modoboa-2.4.10.dist-info → modoboa-2.5.0.dist-info}/entry_points.txt +0 -0
  330. {modoboa-2.4.10.dist-info → modoboa-2.5.0.dist-info}/licenses/LICENSE +0 -0
  331. {modoboa-2.4.10.dist-info → modoboa-2.5.0.dist-info}/top_level.txt +0 -0
@@ -208,6 +208,7 @@ class MailboxSerializer(serializers.ModelSerializer):
208
208
  "message_limit",
209
209
  "quota_usage",
210
210
  "is_send_only",
211
+ "sent_messages_in_percent",
211
212
  )
212
213
 
213
214
  def get_quota_usage(self, mb) -> int:
@@ -398,6 +398,20 @@ class AccountMeSerializer(v1_serializers.AccountSerializer):
398
398
  return [f"{p.content_type.app_label}.{p.codename}" for p in permissions]
399
399
 
400
400
 
401
+ class AccountMeUpdateSerializer(serializers.ModelSerializer):
402
+ """Update serializer for connected user."""
403
+
404
+ class Meta:
405
+ model = core_models.User
406
+ fields = (
407
+ "first_name",
408
+ "last_name",
409
+ "language",
410
+ "phone_number",
411
+ "secondary_email",
412
+ )
413
+
414
+
401
415
  class AccountSerializer(v1_serializers.AccountSerializer):
402
416
  """Add support for user resources."""
403
417
 
@@ -731,7 +745,7 @@ class CSVImportSerializer(serializers.Serializer):
731
745
  class CSVIdentityImportSerializer(CSVImportSerializer):
732
746
  """Custom serializer for identity import."""
733
747
 
734
- crypt_password = serializers.BooleanField()
748
+ crypt_passwords = serializers.BooleanField()
735
749
 
736
750
 
737
751
  class AlarmSerializer(serializers.ModelSerializer):
@@ -331,6 +331,16 @@ class AccountViewSetTestCase(ModoAPITestCase):
331
331
  resp = self.client.patch(url, data, format="json")
332
332
  self.assertEqual(resp.status_code, 200)
333
333
 
334
+ def test_update_quota(self):
335
+ account = core_models.User.objects.get(username="user@test.com")
336
+ url = reverse("v2:account-detail", args=[account.pk])
337
+ data = {
338
+ "mailbox": {"quota": 1},
339
+ }
340
+ resp = self.client.patch(url, data, format="json")
341
+ self.assertEqual(resp.status_code, 200)
342
+ self.assertEqual(account.mailbox.quota, data["mailbox"]["quota"])
343
+
334
344
  def test_user_updates_password(self):
335
345
  account = core_models.User.objects.get(username="user@test.com")
336
346
  self.authenticate_user(account)
@@ -583,7 +593,7 @@ dlist; dlist@test.com; True; user1@test.com; user@extdomain.com
583
593
  ) # NOQA:E501
584
594
  self.client.post(
585
595
  reverse("v2:identities-import-from-csv"),
586
- {"sourcefile": f, "crypt_password": True},
596
+ {"sourcefile": f, "crypt_passwords": True},
587
597
  )
588
598
  admin = core_models.User.objects.get(username="admin")
589
599
  u1 = core_models.User.objects.get(username="user1@test.com")
modoboa/admin/lib.py CHANGED
@@ -138,7 +138,7 @@ def import_domainalias(user, row, formopts):
138
138
  def import_account(user, row, formopts):
139
139
  """Specific code for accounts import"""
140
140
  account = User()
141
- account.from_csv(user, row, formopts["crypt_password"])
141
+ account.from_csv(user, row, formopts["crypt_passwords"])
142
142
 
143
143
 
144
144
  def _import_alias(user, row, **kwargs):
@@ -295,19 +295,21 @@ class Mailbox(mixins.MessageLimitMixin, AdminObject):
295
295
  def update_from_dict(self, user, values):
296
296
  """Update mailbox from a dictionary."""
297
297
  newaddress = None
298
- if values["email"] != self.full_address:
299
- newaddress = values["email"]
300
- elif (
301
- self.user.role == "SimpleUsers" and self.user.username != self.full_address
302
- ):
303
- newaddress = self.user.username
304
- if newaddress is not None:
305
- local_part, domname = split_mailbox(newaddress)
306
- domain = Domain.objects.filter(name=domname).first()
307
- if domain is None:
308
- raise lib_exceptions.NotFound(_("Domain does not exist"))
309
- if not user.can_access(domain):
310
- raise lib_exceptions.PermDeniedException
298
+ if "email" in values:
299
+ if values["email"] != self.full_address:
300
+ newaddress = values["email"]
301
+ elif (
302
+ self.user.role == "SimpleUsers"
303
+ and self.user.username != self.full_address
304
+ ):
305
+ newaddress = self.user.username
306
+ if newaddress is not None:
307
+ local_part, domname = split_mailbox(newaddress)
308
+ domain = Domain.objects.filter(name=domname).first()
309
+ if domain is None:
310
+ raise lib_exceptions.NotFound(_("Domain does not exist"))
311
+ if not user.can_access(domain):
312
+ raise lib_exceptions.PermDeniedException
311
313
  if "use_domain_quota" in values:
312
314
  self.use_domain_quota = values["use_domain_quota"]
313
315
  if "use_domain_quota" in values or "quota" in values:
@@ -71,6 +71,10 @@ class MXRecord(models.Model):
71
71
  def __str__(self):
72
72
  return f"{self.name} ({self.address}) for {self.domain} "
73
73
 
74
+ @property
75
+ def active_dnsbl_results(self):
76
+ return self.dnsblresult_set.exclude(status="")
77
+
74
78
 
75
79
  class DNSBLQuerySet(models.QuerySet):
76
80
  """Custom manager for DNSBLResultManager."""
@@ -88,7 +88,7 @@ account; user1@nonlocal.com; toto; User; One; True; SimpleUsers; user1@nonlocal.
88
88
  name="identities.csv",
89
89
  ) # NOQA:E501
90
90
  url = reverse("v2:identities-import-from-csv")
91
- self.client.post(url, {"sourcefile": f, "crypt_password": True})
91
+ self.client.post(url, {"sourcefile": f, "crypt_passwords": True})
92
92
  self.assertFalse(User.objects.filter(username="user1@nonlocal.com").exists())
93
93
 
94
94
  def test_import_invalid_quota(self):
@@ -99,7 +99,7 @@ account; user1@test.com; toto; User; One; True; SimpleUsers; user1@test.com; ; t
99
99
  name="identities.csv",
100
100
  ) # NOQA:E501
101
101
  url = reverse("v2:identities-import-from-csv")
102
- resp = self.client.post(url, {"sourcefile": f, "crypt_password": True})
102
+ resp = self.client.post(url, {"sourcefile": f, "crypt_passwords": True})
103
103
  self.assertIn("wrong quota value", resp.content.decode())
104
104
 
105
105
  def test_import_domain_by_domainadmin(self):
@@ -114,7 +114,7 @@ domain; domain2.com; 1000; 200; False
114
114
  name="identities.csv",
115
115
  )
116
116
  url = reverse("v2:identities-import-from-csv")
117
- resp = self.client.post(url, {"sourcefile": f, "crypt_password": True})
117
+ resp = self.client.post(url, {"sourcefile": f, "crypt_passwords": True})
118
118
  self.assertIn("You are not allowed to import domains", resp.content.decode())
119
119
  f = ContentFile(
120
120
  b"""
@@ -122,7 +122,7 @@ domainalias; domalias1.com; test.com; True
122
122
  """,
123
123
  name="identities.csv",
124
124
  )
125
- resp = self.client.post(url, {"sourcefile": f, "crypt_password": True})
125
+ resp = self.client.post(url, {"sourcefile": f, "crypt_passwords": True})
126
126
  self.assertIn(
127
127
  "You are not allowed to import domain aliases", resp.content.decode()
128
128
  )
@@ -138,7 +138,7 @@ account; user1@test.com; toto; User; One; True; SimpleUsers; user1@test.com; 40
138
138
  )
139
139
  resp = self.client.post(
140
140
  reverse("v2:identities-import-from-csv"),
141
- {"sourcefile": f, "crypt_password": True},
141
+ {"sourcefile": f, "crypt_passwords": True},
142
142
  )
143
143
  self.assertIn("test.com: domain quota exceeded", resp.content.decode())
144
144
 
@@ -151,7 +151,7 @@ account; user1@test.com; toto; User; One; True; SimpleUsers; user1@test.com
151
151
  )
152
152
  self.client.post(
153
153
  reverse("v2:identities-import-from-csv"),
154
- {"sourcefile": f, "crypt_password": True},
154
+ {"sourcefile": f, "crypt_passwords": True},
155
155
  )
156
156
  account = User.objects.get(username="user1@test.com")
157
157
  self.assertEqual(
@@ -168,7 +168,7 @@ account; truc@test.com; toto; René; Truc; True; DomainAdmins; truc@test.com; 0;
168
168
  ) # NOQA:E501
169
169
  self.client.post(
170
170
  reverse("v2:identities-import-from-csv"),
171
- {"sourcefile": f, "crypt_password": True, "continue_if_exists": True},
171
+ {"sourcefile": f, "crypt_passwords": True, "continue_if_exists": True},
172
172
  )
173
173
  admin = User.objects.get(username="admin")
174
174
  u1 = User.objects.get(username="truc@test.com")
@@ -189,7 +189,7 @@ account; sa@test.com; toto; Super; Admin; True; SuperAdmins; superadmin@test.com
189
189
  ) # NOQA:E501
190
190
  self.client.post(
191
191
  reverse("v2:identities-import-from-csv"),
192
- {"sourcefile": f, "crypt_password": True, "continue_if_exists": True},
192
+ {"sourcefile": f, "crypt_passwords": True, "continue_if_exists": True},
193
193
  )
194
194
  with self.assertRaises(User.DoesNotExist):
195
195
  User.objects.get(username="sa@test.com")
@@ -203,7 +203,7 @@ alias;user.alias@test.com;True;user@test.com;;;;;;;;;;;;;;;;
203
203
  )
204
204
  self.client.post(
205
205
  reverse("v2:identities-import-from-csv"),
206
- {"sourcefile": f, "crypt_password": True, "continue_if_exists": True},
206
+ {"sourcefile": f, "crypt_passwords": True, "continue_if_exists": True},
207
207
  )
208
208
  alias = Alias.objects.get(address="user.alias@test.com")
209
209
  self.assertEqual(alias.type, "alias")
@@ -218,7 +218,7 @@ alias;user@test.com;True;admin@test.com
218
218
  )
219
219
  self.client.post(
220
220
  reverse("v2:identities-import-from-csv"),
221
- {"sourcefile": f, "crypt_password": True},
221
+ {"sourcefile": f, "crypt_passwords": True},
222
222
  )
223
223
  self.assertTrue(
224
224
  Alias.objects.filter(address="user@test.com", internal=False).exists()
@@ -315,7 +315,7 @@ alias; alias1@test.com; True; user1@test.com
315
315
  ) # NOQA:E501
316
316
  resp = self.client.post(
317
317
  reverse("v2:identities-import-from-csv"),
318
- {"sourcefile": f, "crypt_password": True},
318
+ {"sourcefile": f, "crypt_passwords": True},
319
319
  )
320
320
  self.assertEqual(resp.status_code, 200)
321
321
 
@@ -0,0 +1,3 @@
1
+ """The amavis frontend of Modoboa."""
2
+
3
+ default_app_config = "modoboa.amavis.apps.AmavisConfig"
@@ -0,0 +1,276 @@
1
+ import collections
2
+
3
+ from django.utils.translation import gettext_lazy
4
+
5
+
6
+ GLOBAL_PARAMETERS_STRUCT = collections.OrderedDict(
7
+ [
8
+ (
9
+ "amavis",
10
+ {
11
+ "label": gettext_lazy("Amavis settings"),
12
+ "params": collections.OrderedDict(
13
+ [
14
+ (
15
+ "localpart_is_case_sensitive",
16
+ {
17
+ "label": gettext_lazy("Localpart is case sensitive"),
18
+ "help_text": gettext_lazy(
19
+ "Value should match amavisd.conf variable "
20
+ "$localpart_is_case_sensitive"
21
+ ),
22
+ },
23
+ ),
24
+ (
25
+ "recipient_delimiter",
26
+ {
27
+ "label": gettext_lazy("Recipient delimiter"),
28
+ "help_text": gettext_lazy(
29
+ "Value should match amavisd.conf variable "
30
+ "$recipient_delimiter"
31
+ ),
32
+ },
33
+ ),
34
+ ]
35
+ ),
36
+ },
37
+ ),
38
+ (
39
+ "quarantine",
40
+ {
41
+ "label": gettext_lazy("Quarantine settings"),
42
+ "params": collections.OrderedDict(
43
+ [
44
+ (
45
+ "max_messages_age",
46
+ {
47
+ "label": gettext_lazy("Maximum message age"),
48
+ "help_text": gettext_lazy(
49
+ "Quarantine messages maximum age (in days) before deletion"
50
+ ),
51
+ },
52
+ )
53
+ ]
54
+ ),
55
+ },
56
+ ),
57
+ (
58
+ "releasing",
59
+ {
60
+ "label": gettext_lazy("Message releasing"),
61
+ "params": collections.OrderedDict(
62
+ [
63
+ (
64
+ "released_msgs_cleanup",
65
+ {
66
+ "label": gettext_lazy("Remove released messages"),
67
+ "help_text": gettext_lazy(
68
+ "Remove messages marked as released while cleaning up "
69
+ "the database"
70
+ ),
71
+ },
72
+ ),
73
+ (
74
+ "am_pdp_mode",
75
+ {
76
+ "label": gettext_lazy("Amavis connection mode"),
77
+ "help_text": gettext_lazy(
78
+ "Mode used to access the PDP server"
79
+ ),
80
+ },
81
+ ),
82
+ (
83
+ "am_pdp_host",
84
+ {
85
+ "label": gettext_lazy("PDP server address"),
86
+ "help_text": gettext_lazy(
87
+ "PDP server address (if inet mode)"
88
+ ),
89
+ "display": "am_pdp_mode=inet",
90
+ },
91
+ ),
92
+ (
93
+ "am_pdp_port",
94
+ {
95
+ "label": gettext_lazy("PDP server port"),
96
+ "help_text": gettext_lazy(
97
+ "PDP server port (if inet mode)"
98
+ ),
99
+ "display": "am_pdp_mode=inet",
100
+ },
101
+ ),
102
+ (
103
+ "am_pdp_socket",
104
+ {
105
+ "label": gettext_lazy("PDP server socket"),
106
+ "help_text": gettext_lazy(
107
+ "Path to the PDP server socket (if unix mode)"
108
+ ),
109
+ "display": "am_pdp_mode=unix",
110
+ },
111
+ ),
112
+ (
113
+ "user_can_release",
114
+ {
115
+ "label": gettext_lazy("Allow direct release"),
116
+ "help_text": gettext_lazy(
117
+ "Allow users to directly release their messages"
118
+ ),
119
+ },
120
+ ),
121
+ (
122
+ "self_service",
123
+ {
124
+ "label": gettext_lazy("Enable self-service mode"),
125
+ "help_text": gettext_lazy(
126
+ "Activate the 'self-service' mode"
127
+ ),
128
+ },
129
+ ),
130
+ (
131
+ "notifications_sender",
132
+ {
133
+ "label": gettext_lazy("Notifications sender"),
134
+ "help_text": gettext_lazy(
135
+ "The e-mail address used to send notitications"
136
+ ),
137
+ },
138
+ ),
139
+ ]
140
+ ),
141
+ },
142
+ ),
143
+ (
144
+ "learning",
145
+ {
146
+ "label": gettext_lazy("Manual learning"),
147
+ "params": collections.OrderedDict(
148
+ [
149
+ (
150
+ "manual_learning",
151
+ {
152
+ "label": gettext_lazy("Enable manual learning"),
153
+ "help_text": gettext_lazy(
154
+ "Allow super administrators to manually train Spamassassin"
155
+ ),
156
+ },
157
+ ),
158
+ (
159
+ "sa_is_local",
160
+ {
161
+ "label": gettext_lazy("Is Spamassassin local?"),
162
+ "help_text": gettext_lazy(
163
+ "Tell if Spamassassin is running on the same server than modoboa"
164
+ ),
165
+ "display": "manual_learning=true",
166
+ },
167
+ ),
168
+ (
169
+ "default_user",
170
+ {
171
+ "label": gettext_lazy("Default user"),
172
+ "help_text": gettext_lazy(
173
+ "Name of the user owning the default bayesian database"
174
+ ),
175
+ "display": "manual_learning=true",
176
+ },
177
+ ),
178
+ (
179
+ "spamd_address",
180
+ {
181
+ "label": gettext_lazy("Spamd address"),
182
+ "help_text": gettext_lazy(
183
+ "The IP address where spamd can be reached"
184
+ ),
185
+ "display": "sa_is_local=false",
186
+ },
187
+ ),
188
+ (
189
+ "spamd_port",
190
+ {
191
+ "label": gettext_lazy("Spamd port"),
192
+ "help_text": gettext_lazy(
193
+ "The TCP port spamd is listening on"
194
+ ),
195
+ "display": "sa_is_local=false",
196
+ },
197
+ ),
198
+ (
199
+ "domain_level_learning",
200
+ {
201
+ "label": gettext_lazy(
202
+ "Enable per-domain manual learning"
203
+ ),
204
+ "help_text": gettext_lazy(
205
+ "Allow domain administrators to train Spamassassin "
206
+ "(within dedicated per-domain databases)"
207
+ ),
208
+ "display": "manual_learning=true",
209
+ },
210
+ ),
211
+ (
212
+ "user_level_learning",
213
+ {
214
+ "label": gettext_lazy(
215
+ "Enable per-user manual learning"
216
+ ),
217
+ "help_text": gettext_lazy(
218
+ "Allow simple users to personally train Spamassassin "
219
+ "(within a dedicated database)"
220
+ ),
221
+ "display": "manual_learning=true",
222
+ },
223
+ ),
224
+ ]
225
+ ),
226
+ },
227
+ ),
228
+ ]
229
+ )
230
+
231
+ USER_PREFERENCES_STRUCT = collections.OrderedDict(
232
+ [
233
+ (
234
+ "display",
235
+ {
236
+ "label": gettext_lazy("Display"),
237
+ "params": collections.OrderedDict(
238
+ [
239
+ (
240
+ "messages_per_page",
241
+ {
242
+ "label": gettext_lazy(
243
+ "Number of displayed emails per page"
244
+ ),
245
+ "help_text": gettext_lazy(
246
+ "Set the maximum number of messages displayed in a page"
247
+ ),
248
+ },
249
+ )
250
+ ]
251
+ ),
252
+ },
253
+ )
254
+ ]
255
+ )
256
+
257
+
258
+ def load_settings():
259
+ """Load amavis settings."""
260
+ from modoboa.parameters import tools as param_tools
261
+ from modoboa.amavis import serializers
262
+
263
+ param_tools.registry.add(
264
+ "global",
265
+ "amavis",
266
+ gettext_lazy("Amavis"),
267
+ GLOBAL_PARAMETERS_STRUCT,
268
+ serializers.GlobalParametersSerializer,
269
+ )
270
+ param_tools.registry.add(
271
+ "user",
272
+ "amavis",
273
+ gettext_lazy("Amavis"),
274
+ USER_PREFERENCES_STRUCT,
275
+ serializers.UserPreferencesSerializer,
276
+ )
modoboa/amavis/apps.py ADDED
@@ -0,0 +1,18 @@
1
+ """AppConfig for amavis."""
2
+
3
+ from django.apps import AppConfig
4
+
5
+
6
+ class AmavisConfig(AppConfig):
7
+ """App configuration."""
8
+
9
+ name = "modoboa.amavis"
10
+ verbose_name = "Modoboa amavis frontend"
11
+
12
+ def ready(self):
13
+ # Import these to force registration of checks and signals
14
+ from . import checks # NOQA:F401
15
+ from . import handlers # NOQA:F401
16
+ from modoboa.amavis.app_settings import load_settings
17
+
18
+ load_settings()
@@ -0,0 +1,2 @@
1
+ # Import these to force registration of checks
2
+ from . import settings_checks # NOQA:F401
@@ -0,0 +1,59 @@
1
+ from django.conf import settings
2
+ from django.core.checks import Warning, register
3
+ from django.db import connections
4
+ from django.utils.translation import gettext as _
5
+
6
+ W001 = Warning(
7
+ _(
8
+ "AMAVIS_DEFAULT_DATABASE_ENCODING does not match the character "
9
+ "encoding used by the Amavis database."
10
+ ),
11
+ hint=_(
12
+ "Check your database character encoding and set/update "
13
+ "AMAVIS_DEFAULT_DATABASE_ENCODING."
14
+ ),
15
+ id="modoboa-amavis.W001",
16
+ )
17
+
18
+ W002 = Warning(
19
+ _("Modoboa Amavis has not been tested using the selected database engine."),
20
+ hint=_("Try using PostgreSQL, MySQL or MariaDB."),
21
+ id="modoboa-amavis.W002",
22
+ )
23
+
24
+
25
+ @register(deploy=True)
26
+ def check_amavis_database_encoding(app_configs, **kwargs):
27
+ """Ensure AMAVIS_DEFAULT_DATABASE_ENCODING is set to the correct value."""
28
+ errors = []
29
+ db_engine = settings.DATABASES["amavis"]["ENGINE"]
30
+ sql_query = None
31
+ if "postgresql" in db_engine:
32
+ sql_query = (
33
+ "SELECT pg_encoding_to_char(encoding) "
34
+ "FROM pg_database "
35
+ "WHERE datname = %s;"
36
+ )
37
+ elif "mysql" in db_engine:
38
+ sql_query = (
39
+ "SELECT DEFAULT_CHARACTER_SET_NAME "
40
+ "FROM INFORMATION_SCHEMA.SCHEMATA "
41
+ "WHERE SCHEMA_NAME = %s;"
42
+ )
43
+ elif "sqlite" in db_engine:
44
+ sql_query = "PRAGMA encoding;"
45
+ else:
46
+ errors.append(W002)
47
+ return errors
48
+
49
+ with connections["amavis"].cursor() as cursor:
50
+ if "sqlite" in db_engine:
51
+ cursor.execute(sql_query)
52
+ else:
53
+ cursor.execute(sql_query, [settings.DATABASES["amavis"]["NAME"]])
54
+ encoding = cursor.fetchone()[0]
55
+
56
+ if encoding.lower() != settings.AMAVIS_DEFAULT_DATABASE_ENCODING.lower():
57
+ errors.append(W001)
58
+
59
+ return errors
@@ -0,0 +1,35 @@
1
+ class AmavisRouter:
2
+ """A router to control all database operations on models in
3
+ the amavis application"""
4
+
5
+ def db_for_read(self, model, **hints):
6
+ """Point all operations on amavis models to 'amavis'."""
7
+ if model._meta.app_label == "amavis":
8
+ return "amavis"
9
+ return None
10
+
11
+ def db_for_write(self, model, **hints):
12
+ """Point all operations on amavis models to 'amavis'."""
13
+ if model._meta.app_label == "amavis":
14
+ return "amavis"
15
+ return None
16
+
17
+ def allow_relation(self, obj1, obj2, **hints):
18
+ """Allow any relation if a model in amavis is involved."""
19
+ if obj1._meta.app_label == "amavis" or obj2._meta.app_label == "amavis":
20
+ return True
21
+ return None
22
+
23
+ def allow_migrate(self, db, app_label, model=None, **hints):
24
+ """
25
+ Make sure the auth app only appears in the 'amavis'
26
+ database.
27
+ """
28
+ if app_label == "amavis":
29
+ # modoboa_amavis migrations should be created in the amavis
30
+ # database.
31
+ return db == "amavis"
32
+ elif db == "amavis":
33
+ # Don't create non modoboa_amavis migrations in the amavis database.
34
+ return False
35
+ return None