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.
- modoboa/admin/api/v1/serializers.py +1 -0
- modoboa/admin/api/v2/serializers.py +15 -1
- modoboa/admin/api/v2/tests.py +11 -1
- modoboa/admin/lib.py +1 -1
- modoboa/admin/models/mailbox.py +15 -13
- modoboa/admin/models/mxrecord.py +4 -0
- modoboa/admin/tests/test_import_.py +11 -11
- modoboa/amavis/__init__.py +3 -0
- modoboa/amavis/app_settings.py +276 -0
- modoboa/amavis/apps.py +18 -0
- modoboa/amavis/checks/__init__.py +2 -0
- modoboa/amavis/checks/settings_checks.py +59 -0
- modoboa/amavis/dbrouter.py +35 -0
- modoboa/amavis/factories.py +164 -0
- modoboa/amavis/handlers.py +146 -0
- modoboa/amavis/lib.py +381 -0
- modoboa/amavis/management/__init__.py +0 -0
- modoboa/amavis/management/commands/__init__.py +0 -0
- modoboa/amavis/management/commands/amnotify.py +99 -0
- modoboa/amavis/management/commands/qcleanup.py +84 -0
- modoboa/amavis/migrations/0001_initial.py +340 -0
- modoboa/amavis/migrations/__init__.py +0 -0
- modoboa/amavis/models.py +226 -0
- modoboa/amavis/serializers.py +139 -0
- modoboa/amavis/sql_connector.py +240 -0
- modoboa/amavis/sql_email.py +66 -0
- modoboa/amavis/tasks.py +33 -0
- modoboa/amavis/templates/amavis/notifications/pending_requests.html +16 -0
- modoboa/amavis/tests/__init__.py +0 -0
- modoboa/amavis/tests/sa-learn +3 -0
- modoboa/amavis/tests/sample_messages/quarantined-input.txt +80 -0
- modoboa/amavis/tests/sample_messages/quarantined-output-plain_nolinks.txt +17 -0
- modoboa/amavis/tests/spamc +3 -0
- modoboa/amavis/tests/test_checks.py +25 -0
- modoboa/amavis/tests/test_handlers.py +214 -0
- modoboa/amavis/tests/test_lib.py +90 -0
- modoboa/amavis/tests/test_management_commands.py +45 -0
- modoboa/amavis/tests/test_sql_email.py +67 -0
- modoboa/amavis/tests/test_utils.py +19 -0
- modoboa/amavis/tests/test_viewsets.py +319 -0
- modoboa/amavis/urls.py +11 -0
- modoboa/amavis/utils.py +105 -0
- modoboa/amavis/viewsets.py +265 -0
- modoboa/core/api/v1/serializers.py +7 -5
- modoboa/core/api/v2/serializers.py +13 -2
- modoboa/core/api/v2/tests.py +34 -4
- modoboa/core/api/v2/urls.py +10 -5
- modoboa/core/api/v2/views.py +23 -2
- modoboa/core/api/v2/viewsets.py +24 -3
- modoboa/core/app_settings.py +11 -0
- modoboa/core/fido2_auth.py +1 -2
- modoboa/core/handlers.py +6 -2
- modoboa/core/management/commands/add_allowed_hosts.py +33 -0
- modoboa/core/management/commands/cleanlogs.py +9 -0
- modoboa/core/management/commands/load_initial_data.py +10 -0
- modoboa/core/migrations/0025_rename_user_email_is_active_core_user_email_c0c03f_idx.py +23 -5
- modoboa/core/tests/test_core.py +29 -0
- modoboa/core/utils.py +6 -0
- modoboa/dnstools/api/v2/serializers.py +9 -11
- modoboa/frontend_dist/assets/AccountAliasForm-BuSy_1n9.js +1 -0
- modoboa/frontend_dist/assets/AccountEditView-qdJmLM_e.js +1 -0
- modoboa/frontend_dist/assets/AccountLayout-DrN7vHsX.js +1 -0
- modoboa/frontend_dist/assets/AccountPasswordSubForm-DZGt_Xgq.js +1 -0
- modoboa/frontend_dist/assets/AccountView-CO65y0vZ.js +1 -0
- modoboa/frontend_dist/assets/AddressBook-BZNUlhek.js +1 -0
- modoboa/frontend_dist/assets/AdminLayout-CTNhuwTw.js +1 -0
- modoboa/frontend_dist/assets/AlarmsView-9yKGbmkC.css +1 -0
- modoboa/frontend_dist/assets/AlarmsView-DN_JIw9g.js +1 -0
- modoboa/frontend_dist/assets/AliasEditView-DjpPUTp9.js +1 -0
- modoboa/frontend_dist/assets/{AliasRecipientForm-DVZXWaUX.js → AliasRecipientForm-B1Y8wFdP.js} +1 -1
- modoboa/frontend_dist/assets/AliasView-GOJ5lyQH.js +1 -0
- modoboa/frontend_dist/assets/AuditTrailView-fbXmq70e.js +1 -0
- modoboa/frontend_dist/assets/CalendarView-LlQQNEPL.js +1 -0
- modoboa/frontend_dist/assets/{ChoiceField-7eU7c_rI.js → ChoiceField-B3ReQHVe.js} +1 -1
- modoboa/frontend_dist/assets/ComposeEmailForm-Bs1fZXAL.js +1 -0
- modoboa/frontend_dist/assets/ComposeEmailView-s3LMl3pO.js +1 -0
- modoboa/frontend_dist/assets/ConfirmDialog-DY_kUHLG.js +1 -0
- modoboa/frontend_dist/assets/{ConnectedLayout-BaJZ3BeR.css → ConnectedLayout-Bxh21hcH.css} +1 -1
- modoboa/frontend_dist/assets/ConnectedLayout-UWjiYBNw.js +1 -0
- modoboa/frontend_dist/assets/CreationForm-ORg3fazt.js +1 -0
- modoboa/frontend_dist/assets/DashboardView-Dplk9itS.js +1 -0
- modoboa/frontend_dist/assets/{DashboardView-BLlMi6Qb.css → DashboardView-gwwVAPvt.css} +1 -1
- modoboa/frontend_dist/assets/DomainAdminList-DVn9x0rB.js +1 -0
- modoboa/frontend_dist/assets/DomainEditView-nAoL64D_.js +1 -0
- modoboa/frontend_dist/assets/{DomainTransportForm-DPnPGBOp.js → DomainTransportForm-CA-DNUxX.js} +1 -1
- modoboa/frontend_dist/assets/{DomainView-BDKoBFYr.css → DomainView-CCLYXPHx.css} +1 -1
- modoboa/frontend_dist/assets/DomainView-CdXPpwJG.js +5 -0
- modoboa/frontend_dist/assets/DomainsView-B_59gowf.js +1 -0
- modoboa/frontend_dist/assets/DomainsView-DZ-ss9bI.css +1 -0
- modoboa/frontend_dist/assets/EmailField-CwcwI5xW.js +1 -0
- modoboa/frontend_dist/assets/EmailView-BshxcfAK.js +1 -0
- modoboa/frontend_dist/assets/EmptyLayout-DFfhnhLi.js +1 -0
- modoboa/frontend_dist/assets/FiltersView-Cf20MSTK.js +1 -0
- modoboa/frontend_dist/assets/ForwardEmailView-CZG062os.js +1 -0
- modoboa/frontend_dist/assets/{HtmlEditor-uM4AtIGi.js → HtmlEditor-Bh4c689R.js} +1 -1
- modoboa/frontend_dist/assets/IdentitiesView-BXAuU1YX.js +1 -0
- modoboa/frontend_dist/assets/{IdentitiesView-jmuItyMZ.css → IdentitiesView-DPrrRMS5.css} +1 -1
- modoboa/frontend_dist/assets/InformationView-C9vvvQhJ.css +1 -0
- modoboa/frontend_dist/assets/InformationView-Cn5FZW7H.js +1 -0
- modoboa/frontend_dist/assets/{LoadingData-CVD2Aen8.js → LoadingData-CdVvm4FI.js} +1 -1
- modoboa/frontend_dist/assets/{LoginCallbackView-sWzBke1g.js → LoginCallbackView-B9hAH4MI.js} +1 -1
- modoboa/frontend_dist/assets/{LoginView-wmN73W0f.js → LoginView-tHIR4Adc.js} +1 -1
- modoboa/frontend_dist/assets/MailboxView-Bugu2vhg.js +1 -0
- modoboa/frontend_dist/assets/MenuItems-PXjiG-fs.js +1 -0
- modoboa/frontend_dist/assets/MessageView-Cy4STShm.js +1 -0
- modoboa/frontend_dist/assets/MessagesView-DdkuEgfX.js +1 -0
- modoboa/frontend_dist/assets/MigrationsView-CidSEjCF.js +1 -0
- modoboa/frontend_dist/assets/{ParametersForm-BCeQljir.js → ParametersForm-CAv4SH-E.js} +1 -1
- modoboa/frontend_dist/assets/ParametersView-CX7Ffemw.js +1 -0
- modoboa/frontend_dist/assets/ParametersView-CrbNcmV3.js +1 -0
- modoboa/frontend_dist/assets/ProviderEditView-CrltAQXl.js +1 -0
- modoboa/frontend_dist/assets/ProviderGeneralForm-BYAzVnXM.js +1 -0
- modoboa/frontend_dist/assets/ProvidersView-osjIY4Ex.js +1 -0
- modoboa/frontend_dist/assets/QuarantineLayout-B8EcU9vS.js +1 -0
- modoboa/frontend_dist/assets/QuarantineView-D4gOE4EQ.css +1 -0
- modoboa/frontend_dist/assets/QuarantineView-D8Qg0MXA.js +1 -0
- modoboa/frontend_dist/assets/ReplyEmailView-BABPqWhd.js +1 -0
- modoboa/frontend_dist/assets/ResourcesForm-OaqdRYVs.js +1 -0
- modoboa/frontend_dist/assets/SelfServiceLayout-d277YTGR.js +1 -0
- modoboa/frontend_dist/assets/SettingsView-9iNcDhkI.js +6 -0
- modoboa/frontend_dist/assets/StatisticsView-cHsPyGkL.js +1 -0
- modoboa/frontend_dist/assets/TimeSerieChart--V83dcJ9.js +1 -0
- modoboa/frontend_dist/assets/UserLayout-B3sBiTcZ.js +1 -0
- modoboa/frontend_dist/assets/VAlert-BuaaYN2h.js +1 -0
- modoboa/frontend_dist/assets/VApp-CKP-6zGP.js +1 -0
- modoboa/frontend_dist/assets/VAutocomplete-Dwv6_Rzq.js +1 -0
- modoboa/frontend_dist/assets/VAvatar-Cmga0vj6.js +1 -0
- modoboa/frontend_dist/assets/VBadge-BQrRJ9S0.css +1 -0
- modoboa/frontend_dist/assets/VBadge-CixeK87a.js +1 -0
- modoboa/frontend_dist/assets/VCard-CxH9DWoK.js +1 -0
- modoboa/frontend_dist/assets/VCheckbox-62GOpvvP.js +1 -0
- modoboa/frontend_dist/assets/{VCheckboxBtn-j7di4leN.js → VCheckboxBtn-DMoNtKT8.js} +1 -1
- modoboa/frontend_dist/assets/VChip-D_styETR.js +1 -0
- modoboa/frontend_dist/assets/VColorPicker-BHscBGQV.js +1 -0
- modoboa/frontend_dist/assets/VContainer-B46caNs1.js +1 -0
- modoboa/frontend_dist/assets/VDataTable-Bh8NbVSx.js +1 -0
- modoboa/frontend_dist/assets/VDataTableServer-BDR5hOmo.js +1 -0
- modoboa/frontend_dist/assets/VDataTableVirtual-BOQlNtIG.js +1 -0
- modoboa/frontend_dist/assets/{VDialog-CZqM2Ofu.js → VDialog-BcTg7w6P.js} +1 -1
- modoboa/frontend_dist/assets/VExpansionPanels-BmH5Jl2Z.js +1 -0
- modoboa/frontend_dist/assets/VFileInput-BC4yAygd.js +1 -0
- modoboa/frontend_dist/assets/VForm-D5iPGkde.js +1 -0
- modoboa/frontend_dist/assets/VInput-CcxkaOXT.css +1 -0
- modoboa/frontend_dist/assets/VInput-CoDJzvaW.js +1 -0
- modoboa/frontend_dist/assets/VMenu-gUG70-zD.js +1 -0
- modoboa/frontend_dist/assets/VPicker-BXuKT3zB.js +1 -0
- modoboa/frontend_dist/assets/VProgressCircular-BtOPiGCg.js +1 -0
- modoboa/frontend_dist/assets/VRadioGroup-DIFZKSn-.js +1 -0
- modoboa/frontend_dist/assets/{VRow-C_Ydf6yr.js → VRow-ozg66L7j.js} +1 -1
- modoboa/frontend_dist/assets/VSelect-C3RjAa45.js +1 -0
- modoboa/frontend_dist/assets/VSelectionControl-zyz-fJvC.js +1 -0
- modoboa/frontend_dist/assets/VSheet-BNx2X4Mk.js +1 -0
- modoboa/frontend_dist/assets/VSpacer-DinPiXs9.js +1 -0
- modoboa/frontend_dist/assets/VSwitch-DwxdeAEq.js +1 -0
- modoboa/frontend_dist/assets/VTable-DaLxa4FO.js +1 -0
- modoboa/frontend_dist/assets/VTabs-BP0Hgsgm.js +1 -0
- modoboa/frontend_dist/assets/VTextField-BzBVKKob.css +1 -0
- modoboa/frontend_dist/assets/VTextField-XoGTj1KG.js +1 -0
- modoboa/frontend_dist/assets/VTextarea-wBlRMIv_.js +1 -0
- modoboa/frontend_dist/assets/VToolbar-CFZfqeOr.js +1 -0
- modoboa/frontend_dist/assets/VWindowItem-BB7ETW3b.js +1 -0
- modoboa/frontend_dist/assets/WebmailLayout-_Hk1XhVq.js +1 -0
- modoboa/frontend_dist/assets/accounts-DUzbx6k8.js +1 -0
- modoboa/frontend_dist/assets/admin-DewTk2H8.js +1 -0
- modoboa/frontend_dist/assets/{aliases-c3n-dCV_.js → aliases-4sXmjwXp.js} +1 -1
- modoboa/frontend_dist/assets/amavis-CC0li7_T.js +1 -0
- modoboa/frontend_dist/assets/amavis-DK8SHE6o.js +1 -0
- modoboa/frontend_dist/assets/{contacts-Bqckz8sr.js → contacts-BjghrPqZ.js} +1 -1
- modoboa/frontend_dist/assets/{domains-nBMR-fRf.js → domains-BSawReeu.js} +1 -1
- modoboa/frontend_dist/assets/{domains.store-ChZgLcqP.js → domains.store-D-vWCEIK.js} +1 -1
- modoboa/frontend_dist/assets/{filter-D7NrAf6a.js → filter-C82FUCw_.js} +1 -1
- modoboa/frontend_dist/assets/forwardRefs-cvcnlhoK.js +1 -0
- modoboa/frontend_dist/assets/global.store-DbkcI5o2.js +1 -0
- modoboa/frontend_dist/assets/{importExport-Dn9vYw7T.js → importExport-DzoL4Mvc.js} +1 -1
- modoboa/frontend_dist/assets/index-BImkz5Jx.js +984 -0
- modoboa/frontend_dist/assets/index-DuzUMVLM.js +1 -0
- modoboa/frontend_dist/assets/layout-C5FyYCHK.js +1 -0
- modoboa/frontend_dist/assets/{layout.store-BxBoBlgf.js → layout.store-NXWtFIwL.js} +1 -1
- modoboa/frontend_dist/assets/logos-BswdveCV.js +1 -0
- modoboa/frontend_dist/assets/{logs-DrTzylW7.js → logs-6CbtfaZS.js} +1 -1
- modoboa/frontend_dist/assets/{parameters-Lgiqp7aw.js → parameters-aSQiR7kN.js} +1 -1
- modoboa/frontend_dist/assets/{parameters.store-C9k9DuTj.js → parameters.store-CzQqVatx.js} +1 -1
- modoboa/frontend_dist/assets/permissions-DNoefz-n.js +1 -0
- modoboa/frontend_dist/assets/{ssrBoot-BsxW6uW4.js → ssrBoot-CKUX4kcb.js} +1 -1
- modoboa/frontend_dist/assets/{tag-CFK9dzMJ.js → tag-B_yWNNJD.js} +1 -1
- modoboa/frontend_dist/assets/transports-BDNB9wR5.js +1 -0
- modoboa/frontend_dist/assets/{webmail-KrD8ZhNM.js → webmail-CdU6CD9b.js} +1 -1
- modoboa/frontend_dist/index.html +1 -1
- modoboa/lib/email_utils.py +2 -2
- modoboa/lib/permissions.py +7 -0
- modoboa/lib/redis.py +1 -5
- modoboa/lib/test_runners.py +29 -0
- modoboa/lib/tests/__init__.py +5 -1
- modoboa/locale/br/LC_MESSAGES/django.po +87 -75
- modoboa/locale/cs/LC_MESSAGES/django.po +82 -74
- modoboa/locale/cs_CZ/LC_MESSAGES/django.po +145 -121
- modoboa/locale/de/LC_MESSAGES/django.mo +0 -0
- modoboa/locale/de/LC_MESSAGES/django.po +339 -651
- modoboa/locale/de_DE/LC_MESSAGES/django.po +87 -75
- modoboa/locale/el_GR/LC_MESSAGES/django.po +160 -135
- modoboa/locale/en/LC_MESSAGES/django.po +82 -74
- modoboa/locale/es/LC_MESSAGES/django.po +158 -131
- modoboa/locale/es_MX/LC_MESSAGES/django.po +82 -74
- modoboa/locale/fi/LC_MESSAGES/django.po +87 -75
- modoboa/locale/fr/LC_MESSAGES/django.mo +0 -0
- modoboa/locale/fr/LC_MESSAGES/django.po +469 -201
- modoboa/locale/hu/LC_MESSAGES/django.po +82 -74
- modoboa/locale/it/LC_MESSAGES/django.mo +0 -0
- modoboa/locale/it/LC_MESSAGES/django.po +148 -122
- modoboa/locale/ja_JP/LC_MESSAGES/django.mo +0 -0
- modoboa/locale/ja_JP/LC_MESSAGES/django.po +201 -334
- modoboa/locale/ka/LC_MESSAGES/django.po +82 -74
- modoboa/locale/nl_NL/LC_MESSAGES/django.po +160 -132
- modoboa/locale/no/LC_MESSAGES/django.po +82 -74
- modoboa/locale/pl_PL/LC_MESSAGES/django.mo +0 -0
- modoboa/locale/pl_PL/LC_MESSAGES/django.po +172 -149
- modoboa/locale/pt_BR/LC_MESSAGES/django.mo +0 -0
- modoboa/locale/pt_BR/LC_MESSAGES/django.po +172 -144
- modoboa/locale/pt_PT/LC_MESSAGES/django.po +135 -112
- modoboa/locale/ro_RO/LC_MESSAGES/django.po +87 -75
- modoboa/locale/ru/LC_MESSAGES/django.po +142 -118
- modoboa/locale/si/LC_MESSAGES/django.po +82 -74
- modoboa/locale/sk/LC_MESSAGES/django.po +82 -74
- modoboa/locale/sk_SK/LC_MESSAGES/django.po +84 -76
- modoboa/locale/sl_SI/LC_MESSAGES/django.po +90 -76
- modoboa/locale/sv/LC_MESSAGES/django.mo +0 -0
- modoboa/locale/sv/LC_MESSAGES/django.po +172 -139
- modoboa/locale/tr/LC_MESSAGES/django.po +87 -75
- modoboa/locale/tr_TR/LC_MESSAGES/django.po +84 -74
- modoboa/locale/uk/LC_MESSAGES/django.po +82 -74
- modoboa/locale/zh/LC_MESSAGES/django.po +82 -74
- modoboa/locale/zh_CN/LC_MESSAGES/django.po +82 -74
- modoboa/locale/zh_TW/LC_MESSAGES/django.po +87 -75
- modoboa/parameters/api/v2/tests.py +2 -2
- modoboa/parameters/api/v2/viewsets.py +2 -0
- modoboa/policyd/tests.py +2 -0
- modoboa/urls_api_v2.py +6 -0
- {modoboa-2.4.10.dist-info → modoboa-2.5.0.dist-info}/METADATA +6 -4
- {modoboa-2.4.10.dist-info → modoboa-2.5.0.dist-info}/RECORD +244 -193
- modoboa/frontend_dist/assets/AccountAliasForm-DVXatAhB.js +0 -1
- modoboa/frontend_dist/assets/AccountEditView-DmvQjxpx.js +0 -1
- modoboa/frontend_dist/assets/AccountLayout-OGtZvlHR.js +0 -1
- modoboa/frontend_dist/assets/AccountPasswordSubForm-g3IEGrgM.js +0 -1
- modoboa/frontend_dist/assets/AccountView-DsxYqr3k.js +0 -1
- modoboa/frontend_dist/assets/AddressBook-3RoKiKon.js +0 -1
- modoboa/frontend_dist/assets/AdminLayout-CWfn8zaQ.js +0 -1
- modoboa/frontend_dist/assets/AlarmsView-Bheey-gp.css +0 -1
- modoboa/frontend_dist/assets/AlarmsView-D3Mh8ntf.js +0 -1
- modoboa/frontend_dist/assets/AliasEditView-C15eUZ11.js +0 -1
- modoboa/frontend_dist/assets/AliasView-Cyvc5vMb.js +0 -1
- modoboa/frontend_dist/assets/AuditTrailView-LI2XuLLn.js +0 -1
- modoboa/frontend_dist/assets/CalendarView-BpOlPh3f.js +0 -1
- modoboa/frontend_dist/assets/ComposeEmailForm-CNfI7ept.js +0 -1
- modoboa/frontend_dist/assets/ComposeEmailView-B866Xsrc.js +0 -1
- modoboa/frontend_dist/assets/ConfirmDialog-BvqxQsD1.js +0 -1
- modoboa/frontend_dist/assets/ConnectedLayout-BQ3ug6Jh.js +0 -1
- modoboa/frontend_dist/assets/CreationForm-C9Kh05ax.js +0 -1
- modoboa/frontend_dist/assets/DashboardView-Cw-gIcuB.js +0 -1
- modoboa/frontend_dist/assets/DomainAdminList-CsWUNKVk.js +0 -1
- modoboa/frontend_dist/assets/DomainEditView-DocxeOeW.js +0 -1
- modoboa/frontend_dist/assets/DomainView-Djc_0PsF.js +0 -5
- modoboa/frontend_dist/assets/DomainsView-B-Lxru7P.js +0 -1
- modoboa/frontend_dist/assets/DomainsView-DasJ0NdZ.css +0 -1
- modoboa/frontend_dist/assets/EmailField-BEKxuYni.js +0 -1
- modoboa/frontend_dist/assets/EmailView-BsR1Wes5.js +0 -1
- modoboa/frontend_dist/assets/EmptyLayout-BzPFOeLU.js +0 -1
- modoboa/frontend_dist/assets/FiltersView-5rmpC5cC.js +0 -1
- modoboa/frontend_dist/assets/ForwardEmailView-D7MbetcT.js +0 -1
- modoboa/frontend_dist/assets/IdentitiesView-BqjD9Lue.js +0 -1
- modoboa/frontend_dist/assets/InformationView-CghcvPn2.js +0 -1
- modoboa/frontend_dist/assets/InformationView-U5Ww-Sx1.css +0 -1
- modoboa/frontend_dist/assets/MailboxView-T_p-_ZtJ.js +0 -1
- modoboa/frontend_dist/assets/MenuItems-kHCMzR5E.js +0 -1
- modoboa/frontend_dist/assets/MessagesView-Cerv3xsy.js +0 -1
- modoboa/frontend_dist/assets/MigrationsView-7kjqPyYU.js +0 -1
- modoboa/frontend_dist/assets/ParametersView-DspBxVMV.js +0 -1
- modoboa/frontend_dist/assets/ParametersView-Dy0H5ep1.js +0 -1
- modoboa/frontend_dist/assets/ProviderEditView-DDLMOylC.js +0 -1
- modoboa/frontend_dist/assets/ProviderGeneralForm-BwOSKNHK.js +0 -1
- modoboa/frontend_dist/assets/ProvidersView-C99UD0WB.js +0 -1
- modoboa/frontend_dist/assets/ReplyEmailView-DAPBHldd.js +0 -1
- modoboa/frontend_dist/assets/ResourcesForm-D87PHeH0.js +0 -1
- modoboa/frontend_dist/assets/SettingsView-OxDo9wNd.js +0 -6
- modoboa/frontend_dist/assets/StatisticsView-CM__Eqku.js +0 -1
- modoboa/frontend_dist/assets/TimeSerieChart-C6j0uYR_.js +0 -1
- modoboa/frontend_dist/assets/UserLayout-CckCGnPS.js +0 -1
- modoboa/frontend_dist/assets/VAlert-B7mzOJIO.js +0 -1
- modoboa/frontend_dist/assets/VApp-BKxnjOoi.js +0 -1
- modoboa/frontend_dist/assets/VAutocomplete-Cc4_tcl1.js +0 -1
- modoboa/frontend_dist/assets/VAvatar-BAgTUIvX.js +0 -1
- modoboa/frontend_dist/assets/VCard-DFWiFORP.js +0 -1
- modoboa/frontend_dist/assets/VCheckbox-CKsH_vq3.js +0 -1
- modoboa/frontend_dist/assets/VChip-nZ0uhY7t.js +0 -1
- modoboa/frontend_dist/assets/VColorPicker-Os2aeP6J.js +0 -1
- modoboa/frontend_dist/assets/VContainer-Btam4lk2.js +0 -1
- modoboa/frontend_dist/assets/VDataTable-D_0_xJTl.js +0 -1
- modoboa/frontend_dist/assets/VDataTableServer-BWTt4Mzi.js +0 -1
- modoboa/frontend_dist/assets/VDataTableVirtual-BwVmkt4u.js +0 -1
- modoboa/frontend_dist/assets/VExpansionPanels-B5D6GOa3.js +0 -1
- modoboa/frontend_dist/assets/VFileInput-Cv9DIPki.js +0 -1
- modoboa/frontend_dist/assets/VForm-CpoZf60D.js +0 -1
- modoboa/frontend_dist/assets/VMenu-B_dVqOmo.js +0 -1
- modoboa/frontend_dist/assets/VPicker-CXkIGEze.js +0 -1
- modoboa/frontend_dist/assets/VProgressCircular-CrEXxs7k.js +0 -1
- modoboa/frontend_dist/assets/VRadioGroup-D8ypjYOO.js +0 -1
- modoboa/frontend_dist/assets/VSelect-CS51PDEt.js +0 -1
- modoboa/frontend_dist/assets/VSelectionControl-DiOqtY38.js +0 -1
- modoboa/frontend_dist/assets/VSheet-5VVWtHvs.js +0 -1
- modoboa/frontend_dist/assets/VSpacer-C6PZ3X24.js +0 -1
- modoboa/frontend_dist/assets/VSwitch-Chg5o-Cp.js +0 -1
- modoboa/frontend_dist/assets/VTable-KsiZ3cBz.js +0 -1
- modoboa/frontend_dist/assets/VTabs-tNrJIYO0.js +0 -1
- modoboa/frontend_dist/assets/VTextField-C-J20yj_.css +0 -1
- modoboa/frontend_dist/assets/VTextField-CLwRV0Cb.js +0 -1
- modoboa/frontend_dist/assets/VTextarea-Di8jbl8m.js +0 -1
- modoboa/frontend_dist/assets/VToolbar-CGwhgdmI.js +0 -1
- modoboa/frontend_dist/assets/VWindowItem-wWSFAGI-.js +0 -1
- modoboa/frontend_dist/assets/WebmailLayout-DsYThBrz.js +0 -1
- modoboa/frontend_dist/assets/admin-CJVLMHh9.js +0 -1
- modoboa/frontend_dist/assets/forwardRefs-Cpc3YYl6.js +0 -1
- modoboa/frontend_dist/assets/global.store-DUP26-A5.js +0 -1
- modoboa/frontend_dist/assets/index-DV9Li2cg.js +0 -852
- modoboa/frontend_dist/assets/index-DzL89N4E.js +0 -1
- modoboa/frontend_dist/assets/layout-CHO37cA6.js +0 -1
- modoboa/frontend_dist/assets/permissions-mL5hLHcW.js +0 -1
- modoboa/frontend_dist/assets/transports-TO08iTXJ.js +0 -1
- {modoboa-2.4.10.data → modoboa-2.5.0.data}/scripts/modoboa-admin.py +0 -0
- {modoboa-2.4.10.dist-info → modoboa-2.5.0.dist-info}/WHEEL +0 -0
- {modoboa-2.4.10.dist-info → modoboa-2.5.0.dist-info}/entry_points.txt +0 -0
- {modoboa-2.4.10.dist-info → modoboa-2.5.0.dist-info}/licenses/LICENSE +0 -0
- {modoboa-2.4.10.dist-info → modoboa-2.5.0.dist-info}/top_level.txt +0 -0
|
@@ -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
|
-
|
|
748
|
+
crypt_passwords = serializers.BooleanField()
|
|
735
749
|
|
|
736
750
|
|
|
737
751
|
class AlarmSerializer(serializers.ModelSerializer):
|
modoboa/admin/api/v2/tests.py
CHANGED
|
@@ -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, "
|
|
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["
|
|
141
|
+
account.from_csv(user, row, formopts["crypt_passwords"])
|
|
142
142
|
|
|
143
143
|
|
|
144
144
|
def _import_alias(user, row, **kwargs):
|
modoboa/admin/models/mailbox.py
CHANGED
|
@@ -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
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
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:
|
modoboa/admin/models/mxrecord.py
CHANGED
|
@@ -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, "
|
|
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, "
|
|
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, "
|
|
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, "
|
|
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, "
|
|
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, "
|
|
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, "
|
|
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, "
|
|
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, "
|
|
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, "
|
|
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, "
|
|
318
|
+
{"sourcefile": f, "crypt_passwords": True},
|
|
319
319
|
)
|
|
320
320
|
self.assertEqual(resp.status_code, 200)
|
|
321
321
|
|
|
@@ -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,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
|