modoboa 2.4.11__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/v2/serializers.py +14 -0
- 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 +4 -2
- modoboa/core/api/v2/tests.py +16 -4
- modoboa/core/api/v2/urls.py +5 -5
- modoboa/core/api/v2/views.py +3 -1
- modoboa/core/api/v2/viewsets.py +24 -3
- modoboa/core/handlers.py +6 -2
- modoboa/core/management/commands/add_allowed_hosts.py +33 -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 +24 -0
- modoboa/core/utils.py +3 -0
- 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-IOae6sjF.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-DJ_c78Cm.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-Dvwmicnc.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/DomainAdminList-DVn9x0rB.js +1 -0
- modoboa/frontend_dist/assets/DomainEditView-nAoL64D_.js +1 -0
- modoboa/frontend_dist/assets/{DomainTransportForm-C2xo0Yd7.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-CJ9umKeO.js → HtmlEditor-Bh4c689R.js} +1 -1
- modoboa/frontend_dist/assets/IdentitiesView-BXAuU1YX.js +1 -0
- modoboa/frontend_dist/assets/{IdentitiesView-0ziuQ5s-.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-CYwX3Jpn.js → LoadingData-CdVvm4FI.js} +1 -1
- modoboa/frontend_dist/assets/{LoginCallbackView-E01qkKn0.js → LoginCallbackView-B9hAH4MI.js} +1 -1
- modoboa/frontend_dist/assets/{LoginView-Cy4uFV9h.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-BZM0QSvg.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-BxLJBFY0.js → SettingsView-9iNcDhkI.js} +2 -2
- 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-Dt810gWf.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-DvTbsotR.js → VContainer-B46caNs1.js} +1 -1
- 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-Bk6EWNhz.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-BF35mT1S.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-o-HRGnmT.js → admin-DewTk2H8.js} +1 -1
- modoboa/frontend_dist/assets/{aliases-DDVeehyg.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-C84DY-Q1.js → contacts-BjghrPqZ.js} +1 -1
- modoboa/frontend_dist/assets/{domains-Bgn4ixHL.js → domains-BSawReeu.js} +1 -1
- modoboa/frontend_dist/assets/{domains.store-DTE-V7Y1.js → domains.store-D-vWCEIK.js} +1 -1
- modoboa/frontend_dist/assets/{filter-CnffiQAW.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-BlQYb0NO.js → importExport-DzoL4Mvc.js} +1 -1
- modoboa/frontend_dist/assets/index-BImkz5Jx.js +984 -0
- modoboa/frontend_dist/assets/layout-C5FyYCHK.js +1 -0
- modoboa/frontend_dist/assets/{layout.store-DkjrAoXt.js → layout.store-NXWtFIwL.js} +1 -1
- modoboa/frontend_dist/assets/{logos-q8SEyAa4.js → logos-BswdveCV.js} +1 -1
- modoboa/frontend_dist/assets/{logs-B7IJ7LBa.js → logs-6CbtfaZS.js} +1 -1
- modoboa/frontend_dist/assets/{parameters-A6iBEYQq.js → parameters-aSQiR7kN.js} +1 -1
- modoboa/frontend_dist/assets/{parameters.store-BiXS4_6w.js → parameters.store-CzQqVatx.js} +1 -1
- modoboa/frontend_dist/assets/permissions-DNoefz-n.js +1 -0
- modoboa/frontend_dist/assets/{ssrBoot-AzTdjPjk.js → ssrBoot-CKUX4kcb.js} +1 -1
- modoboa/frontend_dist/assets/{tag-BnSYRTcD.js → tag-B_yWNNJD.js} +1 -1
- modoboa/frontend_dist/assets/transports-BDNB9wR5.js +1 -0
- modoboa/frontend_dist/assets/{webmail-CSH_3l6R.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/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 +80 -72
- 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.11.dist-info → modoboa-2.5.0.dist-info}/METADATA +6 -4
- {modoboa-2.4.11.dist-info → modoboa-2.5.0.dist-info}/RECORD +231 -181
- modoboa/frontend_dist/assets/AccountAliasForm-BV6KvTu6.js +0 -1
- modoboa/frontend_dist/assets/AccountEditView-DDOFyfBD.js +0 -1
- modoboa/frontend_dist/assets/AccountLayout-rX51xgxT.js +0 -1
- modoboa/frontend_dist/assets/AccountPasswordSubForm-D9S6LaeH.js +0 -1
- modoboa/frontend_dist/assets/AccountView-cmvaZNq3.js +0 -1
- modoboa/frontend_dist/assets/AddressBook-DCJxL8SU.js +0 -1
- modoboa/frontend_dist/assets/AdminLayout-r0wfG2lO.js +0 -1
- modoboa/frontend_dist/assets/AlarmsView-Bheey-gp.css +0 -1
- modoboa/frontend_dist/assets/AlarmsView-C0bqC4PA.js +0 -1
- modoboa/frontend_dist/assets/AliasEditView-DVoWoCGY.js +0 -1
- modoboa/frontend_dist/assets/AliasView-DrONZXOh.js +0 -1
- modoboa/frontend_dist/assets/AuditTrailView-OTkoZaMU.js +0 -1
- modoboa/frontend_dist/assets/CalendarView-CqF4_Ui9.js +0 -1
- modoboa/frontend_dist/assets/ComposeEmailForm-DO5_GB3e.js +0 -1
- modoboa/frontend_dist/assets/ComposeEmailView-A91HCBsN.js +0 -1
- modoboa/frontend_dist/assets/ConfirmDialog-BBcgdAnO.js +0 -1
- modoboa/frontend_dist/assets/ConnectedLayout-1oRW-Rql.js +0 -1
- modoboa/frontend_dist/assets/CreationForm-71YJbjsA.js +0 -1
- modoboa/frontend_dist/assets/DashboardView-CdLpSfUl.js +0 -1
- modoboa/frontend_dist/assets/DomainAdminList-BjC4KsqI.js +0 -1
- modoboa/frontend_dist/assets/DomainEditView-CQjKwYxl.js +0 -1
- modoboa/frontend_dist/assets/DomainView-BhhuZI_N.js +0 -5
- modoboa/frontend_dist/assets/DomainsView-Cft4BP8Z.js +0 -1
- modoboa/frontend_dist/assets/DomainsView-DasJ0NdZ.css +0 -1
- modoboa/frontend_dist/assets/EmailField-C8umy0EU.js +0 -1
- modoboa/frontend_dist/assets/EmailView-ki7uEQPD.js +0 -1
- modoboa/frontend_dist/assets/EmptyLayout-DaA1XH9n.js +0 -1
- modoboa/frontend_dist/assets/FiltersView-FYFZxG4B.js +0 -1
- modoboa/frontend_dist/assets/ForwardEmailView-cUbnSYCF.js +0 -1
- modoboa/frontend_dist/assets/IdentitiesView-njNo8N5n.js +0 -1
- modoboa/frontend_dist/assets/InformationView-D1h38POt.js +0 -1
- modoboa/frontend_dist/assets/InformationView-U5Ww-Sx1.css +0 -1
- modoboa/frontend_dist/assets/MailboxView-IlrLWm_H.js +0 -1
- modoboa/frontend_dist/assets/MenuItems-BAtHWzAE.js +0 -1
- modoboa/frontend_dist/assets/MessagesView-OSpjixFq.js +0 -1
- modoboa/frontend_dist/assets/MigrationsView-DKNOsVzF.js +0 -1
- modoboa/frontend_dist/assets/ParametersView-C4bXASiq.js +0 -1
- modoboa/frontend_dist/assets/ParametersView-CYXgNmc1.js +0 -1
- modoboa/frontend_dist/assets/ProviderEditView-CyxCWTST.js +0 -1
- modoboa/frontend_dist/assets/ProviderGeneralForm-BYPjNHqB.js +0 -1
- modoboa/frontend_dist/assets/ProvidersView-CxrMkRyk.js +0 -1
- modoboa/frontend_dist/assets/ReplyEmailView-Dkw9-N26.js +0 -1
- modoboa/frontend_dist/assets/ResourcesForm-CuUvrOdY.js +0 -1
- modoboa/frontend_dist/assets/StatisticsView-BN7QsZMT.js +0 -1
- modoboa/frontend_dist/assets/TimeSerieChart-BMN8BeFZ.js +0 -1
- modoboa/frontend_dist/assets/UserLayout-B6-JQg4F.js +0 -1
- modoboa/frontend_dist/assets/VAlert-DIQTrRif.js +0 -1
- modoboa/frontend_dist/assets/VApp-CpkYA7js.js +0 -1
- modoboa/frontend_dist/assets/VAutocomplete-C4IpXyl8.js +0 -1
- modoboa/frontend_dist/assets/VAvatar-Lpb-Dion.js +0 -1
- modoboa/frontend_dist/assets/VCard-er_isjE_.js +0 -1
- modoboa/frontend_dist/assets/VCheckbox-D-u8JXP1.js +0 -1
- modoboa/frontend_dist/assets/VChip-B4iSpj8_.js +0 -1
- modoboa/frontend_dist/assets/VColorPicker-BAjGDsXv.js +0 -1
- modoboa/frontend_dist/assets/VDataTable-4JRjbtgF.js +0 -1
- modoboa/frontend_dist/assets/VDataTableServer-tIDT1m3-.js +0 -1
- modoboa/frontend_dist/assets/VDataTableVirtual-BlnO18u_.js +0 -1
- modoboa/frontend_dist/assets/VExpansionPanels-CwGtXDhr.js +0 -1
- modoboa/frontend_dist/assets/VFileInput-D1_7ZkO_.js +0 -1
- modoboa/frontend_dist/assets/VForm-DAkW4nfy.js +0 -1
- modoboa/frontend_dist/assets/VMenu-BPFJwj2f.js +0 -1
- modoboa/frontend_dist/assets/VPicker-CfT82M8N.js +0 -1
- modoboa/frontend_dist/assets/VProgressCircular-w75-3ogi.js +0 -1
- modoboa/frontend_dist/assets/VRadioGroup-0j6DNC_k.js +0 -1
- modoboa/frontend_dist/assets/VSelect-Cs4ARbAS.js +0 -1
- modoboa/frontend_dist/assets/VSelectionControl-Dg-XyRRS.js +0 -1
- modoboa/frontend_dist/assets/VSheet-Btq_Mu4s.js +0 -1
- modoboa/frontend_dist/assets/VSpacer-C7xukQmu.js +0 -1
- modoboa/frontend_dist/assets/VSwitch-Cs1NQrmk.js +0 -1
- modoboa/frontend_dist/assets/VTable-CNz2SGk4.js +0 -1
- modoboa/frontend_dist/assets/VTabs-B1fyVn4M.js +0 -1
- modoboa/frontend_dist/assets/VTextField-BdyvgvkG.js +0 -1
- modoboa/frontend_dist/assets/VTextField-C-J20yj_.css +0 -1
- modoboa/frontend_dist/assets/VTextarea-DnOMpe0Q.js +0 -1
- modoboa/frontend_dist/assets/VToolbar-BiCiBxBJ.js +0 -1
- modoboa/frontend_dist/assets/VWindowItem-ChWm_kz3.js +0 -1
- modoboa/frontend_dist/assets/WebmailLayout-o4uEkp9e.js +0 -1
- modoboa/frontend_dist/assets/forwardRefs-Dvjn_Xq4.js +0 -1
- modoboa/frontend_dist/assets/global.store-BaiD63EN.js +0 -1
- modoboa/frontend_dist/assets/index-I1VDlN4g.js +0 -984
- modoboa/frontend_dist/assets/layout-D8ZJPiJ_.js +0 -1
- modoboa/frontend_dist/assets/permissions-CITHLHVg.js +0 -1
- modoboa/frontend_dist/assets/transports-Dz7c6kIy.js +0 -1
- {modoboa-2.4.11.data → modoboa-2.5.0.data}/scripts/modoboa-admin.py +0 -0
- {modoboa-2.4.11.dist-info → modoboa-2.5.0.dist-info}/WHEEL +0 -0
- {modoboa-2.4.11.dist-info → modoboa-2.5.0.dist-info}/entry_points.txt +0 -0
- {modoboa-2.4.11.dist-info → modoboa-2.5.0.dist-info}/licenses/LICENSE +0 -0
- {modoboa-2.4.11.dist-info → modoboa-2.5.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
"""Amavis viewsets."""
|
|
2
|
+
|
|
3
|
+
from django.http import Http404
|
|
4
|
+
from django.shortcuts import get_object_or_404
|
|
5
|
+
from django.utils.translation import gettext as _
|
|
6
|
+
|
|
7
|
+
import django_rq
|
|
8
|
+
from rest_framework import filters, mixins, permissions, response, viewsets
|
|
9
|
+
from rest_framework.decorators import action
|
|
10
|
+
|
|
11
|
+
from modoboa.amavis.sql_connector import SQLconnector
|
|
12
|
+
from modoboa.lib.paginator import Paginator
|
|
13
|
+
from modoboa.lib.permissions import CanViewDomain
|
|
14
|
+
|
|
15
|
+
from modoboa.admin import models as admin_models
|
|
16
|
+
from modoboa.amavis import models, serializers, tasks
|
|
17
|
+
from modoboa.amavis.lib import (
|
|
18
|
+
AMrelease,
|
|
19
|
+
manual_learning_enabled,
|
|
20
|
+
SelfServiceAuthentication,
|
|
21
|
+
)
|
|
22
|
+
from modoboa.amavis.sql_email import SQLemail
|
|
23
|
+
from modoboa.amavis.utils import smart_str
|
|
24
|
+
from modoboa.parameters import tools as param_tools
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
SELFSERVICE_ACTIONS = ["retrieve", "headers", "delete", "release"]
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def get_user_valid_addresses(user):
|
|
31
|
+
"""Retrieve all valid addresses of a user."""
|
|
32
|
+
valid_addresses = []
|
|
33
|
+
if user.role == "SimpleUsers":
|
|
34
|
+
valid_addresses.append(user.email)
|
|
35
|
+
try:
|
|
36
|
+
mb = admin_models.Mailbox.objects.get(user=user)
|
|
37
|
+
except admin_models.Mailbox.DoesNotExist:
|
|
38
|
+
pass
|
|
39
|
+
else:
|
|
40
|
+
valid_addresses += mb.alias_addresses
|
|
41
|
+
return valid_addresses
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class QuarantineViewSet(viewsets.GenericViewSet):
|
|
45
|
+
|
|
46
|
+
filter_backends = (filters.OrderingFilter,)
|
|
47
|
+
ordering_fields = [
|
|
48
|
+
"datetime",
|
|
49
|
+
"from_address",
|
|
50
|
+
"score",
|
|
51
|
+
"subject",
|
|
52
|
+
"to_address",
|
|
53
|
+
"type",
|
|
54
|
+
]
|
|
55
|
+
permission_classes = (permissions.IsAuthenticated,)
|
|
56
|
+
|
|
57
|
+
def get_serializer_class(self):
|
|
58
|
+
if self.action == "retrieve":
|
|
59
|
+
return serializers.MessageSerializer
|
|
60
|
+
if self.action == "headers":
|
|
61
|
+
return serializers.MessageHeadersSerializer
|
|
62
|
+
if self.action == "mark_selection":
|
|
63
|
+
return serializers.MarkMessageSelectionSerializer
|
|
64
|
+
if self.action in ["release_selection", "delete_selection"]:
|
|
65
|
+
return serializers.MessageSelectionSerializer
|
|
66
|
+
if self.action in ["delete", "release"]:
|
|
67
|
+
return serializers.MessageIdentifierSerializer
|
|
68
|
+
return serializers.PaginatedMessageListSerializer
|
|
69
|
+
|
|
70
|
+
def get_authenticators(self):
|
|
71
|
+
result = [auth() for auth in self.authentication_classes]
|
|
72
|
+
if self.request:
|
|
73
|
+
action = self.action_map.get(self.request.method.lower())
|
|
74
|
+
if action in SELFSERVICE_ACTIONS:
|
|
75
|
+
result = [SelfServiceAuthentication()] + result
|
|
76
|
+
return result
|
|
77
|
+
|
|
78
|
+
def get_permissions(self):
|
|
79
|
+
if self.request.auth == "selfservice" and self.action in SELFSERVICE_ACTIONS:
|
|
80
|
+
return []
|
|
81
|
+
return super().get_permissions()
|
|
82
|
+
|
|
83
|
+
def list(self, request):
|
|
84
|
+
ordering = request.GET.get("ordering")
|
|
85
|
+
connector = SQLconnector(user=request.user, ordering=ordering)
|
|
86
|
+
try:
|
|
87
|
+
page_size = int(request.GET.get("page_size"))
|
|
88
|
+
except (TypeError, ValueError):
|
|
89
|
+
page_size = request.user.parameters.get_value("messages_per_page")
|
|
90
|
+
total = connector.messages_count(request)
|
|
91
|
+
paginator = Paginator(total, page_size)
|
|
92
|
+
page_num = int(request.GET.get("page", 1))
|
|
93
|
+
page = paginator.getpage(page_num)
|
|
94
|
+
if not page:
|
|
95
|
+
serializer = self.get_serializer(
|
|
96
|
+
{
|
|
97
|
+
"count": 0,
|
|
98
|
+
"first_index": 0,
|
|
99
|
+
"last_index": 0,
|
|
100
|
+
"prev_page": None,
|
|
101
|
+
"next_page": None,
|
|
102
|
+
"results": [],
|
|
103
|
+
}
|
|
104
|
+
)
|
|
105
|
+
return response.Response(serializer.data)
|
|
106
|
+
email_list = connector.fetch(page.id_start, page.id_stop)
|
|
107
|
+
serializer = self.get_serializer(
|
|
108
|
+
{
|
|
109
|
+
"count": total,
|
|
110
|
+
"first_index": page_num * page_size,
|
|
111
|
+
"last_index": (page_num * page_size) + len(email_list),
|
|
112
|
+
"prev_page": page.previous_page_number if page.has_previous else None,
|
|
113
|
+
"next_page": page.next_page_number if page.has_next else None,
|
|
114
|
+
"results": email_list,
|
|
115
|
+
}
|
|
116
|
+
)
|
|
117
|
+
return response.Response(serializer.data)
|
|
118
|
+
|
|
119
|
+
def retrieve(self, request, pk):
|
|
120
|
+
rcpt = request.GET.get("rcpt")
|
|
121
|
+
if rcpt is None:
|
|
122
|
+
return response.Response({"error": _("Invalid request")}, status=400)
|
|
123
|
+
if request.user:
|
|
124
|
+
if request.user.email == rcpt:
|
|
125
|
+
SQLconnector().set_msgrcpt_status(rcpt, pk, "V")
|
|
126
|
+
elif hasattr(request.user, "mailbox"):
|
|
127
|
+
mb = request.user.mailbox
|
|
128
|
+
if rcpt == mb.full_address or rcpt in mb.alias_addresses:
|
|
129
|
+
SQLconnector().set_msgrcpt_status(rcpt, pk, "V")
|
|
130
|
+
mail = SQLemail(pk.encode("ascii"), dformat="plain")
|
|
131
|
+
serializer = self.get_serializer(mail)
|
|
132
|
+
return response.Response(serializer.data)
|
|
133
|
+
|
|
134
|
+
@action(methods=["get"], detail=True)
|
|
135
|
+
def headers(self, request, pk):
|
|
136
|
+
email = SQLemail(pk.encode("ascii"))
|
|
137
|
+
headers = []
|
|
138
|
+
for name in email.msg.keys():
|
|
139
|
+
headers.append({"name": name, "value": email.get_header(email.msg, name)})
|
|
140
|
+
serializer = self.get_serializer({"headers": headers})
|
|
141
|
+
return response.Response(serializer.data)
|
|
142
|
+
|
|
143
|
+
def _release_selection(self, request, selection):
|
|
144
|
+
connector = SQLconnector()
|
|
145
|
+
valid_addresses = None
|
|
146
|
+
if request.user:
|
|
147
|
+
valid_addresses = get_user_valid_addresses(request.user)
|
|
148
|
+
msgrcpts = []
|
|
149
|
+
for item in selection:
|
|
150
|
+
if valid_addresses and item["rcpt"] not in valid_addresses:
|
|
151
|
+
continue
|
|
152
|
+
msgrcpts += [
|
|
153
|
+
(
|
|
154
|
+
item["mailid"],
|
|
155
|
+
connector.get_recipient_message(item["rcpt"], item["mailid"]),
|
|
156
|
+
)
|
|
157
|
+
]
|
|
158
|
+
if (
|
|
159
|
+
not request.user or request.user.role == "SimpleUsers"
|
|
160
|
+
) and not param_tools.get_global_parameter("user_can_release"):
|
|
161
|
+
for i, msgrcpt in msgrcpts:
|
|
162
|
+
connector.set_msgrcpt_status(smart_str(msgrcpt.rid.email), i, "p")
|
|
163
|
+
return response.Response({"status": "pending"})
|
|
164
|
+
|
|
165
|
+
amr = AMrelease()
|
|
166
|
+
error = None
|
|
167
|
+
for mid, rcpt in msgrcpts:
|
|
168
|
+
# we can't use the .mail relation on rcpt because it leads to
|
|
169
|
+
# an error on Postgres (memoryview pickle error).
|
|
170
|
+
mail = models.Msgs.objects.get(pk=mid.encode("ascii"))
|
|
171
|
+
result = amr.sendreq(mid, mail.secret_id, rcpt.rid.email)
|
|
172
|
+
if result:
|
|
173
|
+
connector.set_msgrcpt_status(smart_str(rcpt.rid.email), mid, "R")
|
|
174
|
+
else:
|
|
175
|
+
error = result
|
|
176
|
+
break
|
|
177
|
+
|
|
178
|
+
if error:
|
|
179
|
+
return response.Response({"status": error})
|
|
180
|
+
|
|
181
|
+
return response.Response({"status": "released"})
|
|
182
|
+
|
|
183
|
+
@action(methods=["post"], detail=False)
|
|
184
|
+
def release_selection(self, request):
|
|
185
|
+
serializer = self.get_serializer(data=request.data)
|
|
186
|
+
serializer.is_valid(raise_exception=True)
|
|
187
|
+
return self._release_selection(request, serializer.validated_data["selection"])
|
|
188
|
+
|
|
189
|
+
@action(methods=["post"], detail=True)
|
|
190
|
+
def release(self, request, pk):
|
|
191
|
+
serializer = self.get_serializer(data=request.data)
|
|
192
|
+
serializer.is_valid(raise_exception=True)
|
|
193
|
+
return self._release_selection(request, [serializer.validated_data])
|
|
194
|
+
|
|
195
|
+
def _delete_selection(self, request, selection):
|
|
196
|
+
connector = SQLconnector()
|
|
197
|
+
valid_addresses = None
|
|
198
|
+
if request.user:
|
|
199
|
+
valid_addresses = get_user_valid_addresses(request.user)
|
|
200
|
+
for item in selection:
|
|
201
|
+
if valid_addresses and item["rcpt"] not in valid_addresses:
|
|
202
|
+
continue
|
|
203
|
+
connector.set_msgrcpt_status(item["rcpt"], item["mailid"], "D")
|
|
204
|
+
return response.Response(status=204)
|
|
205
|
+
|
|
206
|
+
@action(methods=["post"], detail=False)
|
|
207
|
+
def delete_selection(self, request):
|
|
208
|
+
serializer = self.get_serializer(data=request.data)
|
|
209
|
+
serializer.is_valid(raise_exception=True)
|
|
210
|
+
return self._delete_selection(request, serializer.validated_data["selection"])
|
|
211
|
+
|
|
212
|
+
@action(methods=["post"], detail=True)
|
|
213
|
+
def delete(self, request, pk):
|
|
214
|
+
serializer = self.get_serializer(data=request.data)
|
|
215
|
+
serializer.is_valid(raise_exception=True)
|
|
216
|
+
return self._delete_selection(request, [serializer.validated_data])
|
|
217
|
+
|
|
218
|
+
@action(methods=["post"], detail=False)
|
|
219
|
+
def mark_selection(self, request):
|
|
220
|
+
serializer = self.get_serializer(data=request.data)
|
|
221
|
+
serializer.is_valid(raise_exception=True)
|
|
222
|
+
if not manual_learning_enabled(request.user):
|
|
223
|
+
return response.Response({"status": "ok"})
|
|
224
|
+
recipient_db = serializer.validated_data.get("database")
|
|
225
|
+
if not recipient_db:
|
|
226
|
+
recipient_db = "user" if request.user.role == "SimpleUsers" else "global"
|
|
227
|
+
queue = django_rq.get_queue("modoboa")
|
|
228
|
+
queue.enqueue(
|
|
229
|
+
tasks.manual_learning,
|
|
230
|
+
request.user.pk,
|
|
231
|
+
serializer.validated_data["type"],
|
|
232
|
+
serializer.validated_data["selection"],
|
|
233
|
+
recipient_db,
|
|
234
|
+
)
|
|
235
|
+
return response.Response(status=204)
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
class PolicyViewSet(
|
|
239
|
+
mixins.RetrieveModelMixin, mixins.UpdateModelMixin, viewsets.GenericViewSet
|
|
240
|
+
):
|
|
241
|
+
|
|
242
|
+
permission_classes = (permissions.IsAuthenticated, CanViewDomain)
|
|
243
|
+
serializer_class = serializers.PolicySerializer
|
|
244
|
+
|
|
245
|
+
def get_queryset(self):
|
|
246
|
+
domains = [
|
|
247
|
+
f"@{name}"
|
|
248
|
+
for name in admin_models.Domain.objects.get_for_admin(
|
|
249
|
+
self.request.user
|
|
250
|
+
).values_list("name", flat=True)
|
|
251
|
+
]
|
|
252
|
+
return models.Policy.objects.filter(users__email__in=domains)
|
|
253
|
+
|
|
254
|
+
def get_object(self):
|
|
255
|
+
"""Return the object the view is displaying."""
|
|
256
|
+
domain = get_object_or_404(admin_models.Domain, pk=self.kwargs["pk"])
|
|
257
|
+
queryset = self.filter_queryset(self.get_queryset())
|
|
258
|
+
obj = queryset.filter(users__email=f"@{domain.name}").first()
|
|
259
|
+
if obj is None:
|
|
260
|
+
raise Http404
|
|
261
|
+
|
|
262
|
+
# May raise a permission denied
|
|
263
|
+
self.check_object_permissions(self.request, obj)
|
|
264
|
+
|
|
265
|
+
return obj
|
|
@@ -83,10 +83,12 @@ class CheckPasswordTFASerializer(serializers.Serializer):
|
|
|
83
83
|
def validate_password(self, value):
|
|
84
84
|
user = self.context["user"]
|
|
85
85
|
if not user.check_password(value):
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
86
|
+
msg = _(
|
|
87
|
+
"Failed TFA settings editing attempt from '%(addr)s' as user '%(user)s'"
|
|
88
|
+
) % {
|
|
89
|
+
"addr": self.context["remote_addr"],
|
|
90
|
+
"user": escape(user.username),
|
|
91
|
+
}
|
|
92
|
+
logger.warning(msg)
|
|
91
93
|
raise serializers.ValidationError(_("Invalid password"))
|
|
92
94
|
return value
|
|
@@ -664,9 +664,11 @@ class NotificationSerializer(serializers.Serializer):
|
|
|
664
664
|
"""Serializer used to render a notification."""
|
|
665
665
|
|
|
666
666
|
id = serializers.CharField()
|
|
667
|
-
url = serializers.CharField(required=False)
|
|
668
667
|
text = serializers.CharField()
|
|
669
|
-
|
|
668
|
+
color = serializers.CharField()
|
|
669
|
+
target = serializers.CharField()
|
|
670
|
+
url = serializers.CharField(required=False)
|
|
671
|
+
counter = serializers.IntegerField(required=False)
|
|
670
672
|
|
|
671
673
|
|
|
672
674
|
class ModoboaApplicationSerializer(serializers.Serializer):
|
modoboa/core/api/v2/tests.py
CHANGED
|
@@ -196,6 +196,18 @@ class AccountViewSetTestCase(ModoAPITestCase):
|
|
|
196
196
|
me = resp.json()
|
|
197
197
|
self.assertEqual(me["username"], "admin")
|
|
198
198
|
|
|
199
|
+
def test_update_me(self):
|
|
200
|
+
url = reverse("v2:account-me")
|
|
201
|
+
data = {
|
|
202
|
+
"first_name": "First name",
|
|
203
|
+
"secondary_email": "toto@iti.com",
|
|
204
|
+
"language": "fr",
|
|
205
|
+
}
|
|
206
|
+
resp = self.client.put(url, data, format="json")
|
|
207
|
+
self.assertEqual(resp.status_code, 200)
|
|
208
|
+
me = resp.json()
|
|
209
|
+
self.assertEqual(me["secondary_email"], data["secondary_email"])
|
|
210
|
+
|
|
199
211
|
def test_me_password(self, password_ko="Toto1234", password_ok="password"):
|
|
200
212
|
url = reverse("v2:account-check-me-password")
|
|
201
213
|
resp = self.client.post(url, {"password": password_ko}, format="json")
|
|
@@ -271,22 +283,22 @@ class AccountViewSetTestCase(ModoAPITestCase):
|
|
|
271
283
|
url = reverse("v2:account-available-applications")
|
|
272
284
|
resp = self.client.get(url)
|
|
273
285
|
self.assertEqual(resp.status_code, 200)
|
|
274
|
-
# admin -> only
|
|
275
|
-
self.assertEqual(len(resp.json()),
|
|
286
|
+
# admin -> only 2 apps.
|
|
287
|
+
self.assertEqual(len(resp.json()), 2)
|
|
276
288
|
|
|
277
289
|
# Domain admin with mailbox
|
|
278
290
|
dadmin = models.User.objects.get(username="admin@test.com")
|
|
279
291
|
self.authenticate_user(dadmin)
|
|
280
292
|
resp = self.client.get(url)
|
|
281
293
|
self.assertEqual(resp.status_code, 200)
|
|
282
|
-
self.assertEqual(len(resp.json()),
|
|
294
|
+
self.assertEqual(len(resp.json()), 5)
|
|
283
295
|
|
|
284
296
|
# Simple user
|
|
285
297
|
user = models.User.objects.get(username="user@test.com")
|
|
286
298
|
self.authenticate_user(user)
|
|
287
299
|
resp = self.client.get(url)
|
|
288
300
|
self.assertEqual(resp.status_code, 200)
|
|
289
|
-
self.assertEqual(len(resp.json()),
|
|
301
|
+
self.assertEqual(len(resp.json()), 4)
|
|
290
302
|
|
|
291
303
|
@override_settings(
|
|
292
304
|
MODOBOA_APPS=[
|
modoboa/core/api/v2/urls.py
CHANGED
|
@@ -32,11 +32,6 @@ urlpatterns += [
|
|
|
32
32
|
views.ComponentsInformationAPIView.as_view(),
|
|
33
33
|
name="components_information",
|
|
34
34
|
),
|
|
35
|
-
path(
|
|
36
|
-
"admin/notifications/",
|
|
37
|
-
views.NotificationsAPIView.as_view(),
|
|
38
|
-
name="notifications",
|
|
39
|
-
),
|
|
40
35
|
path(
|
|
41
36
|
"admin/news_feed/",
|
|
42
37
|
views.NewsFeedAPIView.as_view(),
|
|
@@ -48,5 +43,10 @@ urlpatterns += [
|
|
|
48
43
|
name="statistics",
|
|
49
44
|
),
|
|
50
45
|
path("capabilities/", views.CapabilitiesAPIView.as_view(), name="capabilities"),
|
|
46
|
+
path(
|
|
47
|
+
"notifications/",
|
|
48
|
+
views.NotificationsAPIView.as_view(),
|
|
49
|
+
name="notifications",
|
|
50
|
+
),
|
|
51
51
|
path("theme/", views.ThemeAPIView.as_view(), name="theme"),
|
|
52
52
|
]
|
modoboa/core/api/v2/views.py
CHANGED
|
@@ -142,7 +142,9 @@ class PasswordResetConfirmView(APIView):
|
|
|
142
142
|
class ComponentsInformationAPIView(APIView):
|
|
143
143
|
"""Retrieve information about installed components."""
|
|
144
144
|
|
|
145
|
-
permission_classes = [
|
|
145
|
+
permission_classes = [
|
|
146
|
+
permissions.IsAuthenticated,
|
|
147
|
+
]
|
|
146
148
|
throttle_classes = [UserLesserDdosUser]
|
|
147
149
|
|
|
148
150
|
@extend_schema(responses=serializers.ModoboaComponentSerializer(many=True))
|
modoboa/core/api/v2/viewsets.py
CHANGED
|
@@ -48,11 +48,22 @@ def create_static_tokens(request):
|
|
|
48
48
|
class AccountViewSet(core_v1_viewsets.AccountViewSet):
|
|
49
49
|
"""Account viewset."""
|
|
50
50
|
|
|
51
|
-
@extend_schema(
|
|
52
|
-
|
|
51
|
+
@extend_schema(
|
|
52
|
+
responses=admin_v2_serializers.AccountMeSerializer,
|
|
53
|
+
request=admin_v2_serializers.AccountMeUpdateSerializer,
|
|
54
|
+
)
|
|
55
|
+
@action(methods=["get", "put"], detail=False)
|
|
53
56
|
def me(self, request):
|
|
54
57
|
"""Return information about connected user."""
|
|
55
|
-
|
|
58
|
+
if request.method == "PUT":
|
|
59
|
+
serializer = admin_v2_serializers.AccountMeUpdateSerializer(
|
|
60
|
+
data=request.data, instance=request.user
|
|
61
|
+
)
|
|
62
|
+
serializer.is_valid(raise_exception=True)
|
|
63
|
+
instance = serializer.save()
|
|
64
|
+
serializer = admin_v1_serializers.AccountSerializer(instance)
|
|
65
|
+
else:
|
|
66
|
+
serializer = admin_v1_serializers.AccountSerializer(request.user)
|
|
56
67
|
return response.Response(serializer.data)
|
|
57
68
|
|
|
58
69
|
@action(
|
|
@@ -166,6 +177,16 @@ class AccountViewSet(core_v1_viewsets.AccountViewSet):
|
|
|
166
177
|
"url": "/admin",
|
|
167
178
|
}
|
|
168
179
|
)
|
|
180
|
+
if "modoboa.amavis" in settings.MODOBOA_APPS:
|
|
181
|
+
apps.append(
|
|
182
|
+
{
|
|
183
|
+
"name": "amavis",
|
|
184
|
+
"label": _("Quarantine"),
|
|
185
|
+
"icon": "mdi-server-security",
|
|
186
|
+
"description": _("Amavis quarantine"),
|
|
187
|
+
"url": "/user/quarantine",
|
|
188
|
+
}
|
|
189
|
+
)
|
|
169
190
|
if hasattr(request.user, "mailbox"):
|
|
170
191
|
if "modoboa.contacts" in settings.MODOBOA_APPS:
|
|
171
192
|
apps += [
|
modoboa/core/handlers.py
CHANGED
|
@@ -125,7 +125,9 @@ def check_for_new_versions(sender, include_all: bool, **kwargs) -> list:
|
|
|
125
125
|
{
|
|
126
126
|
"id": "newversionavailable",
|
|
127
127
|
"text": _("One or more updates are available"),
|
|
128
|
-
"
|
|
128
|
+
"color": "info",
|
|
129
|
+
"url": "/admin/information",
|
|
130
|
+
"target": "admin",
|
|
129
131
|
}
|
|
130
132
|
]
|
|
131
133
|
elif include_all:
|
|
@@ -137,7 +139,9 @@ def check_for_new_versions(sender, include_all: bool, **kwargs) -> list:
|
|
|
137
139
|
"id": "deprecatedpasswordscheme",
|
|
138
140
|
"text": _("You are still using a deprecated password scheme (%s)")
|
|
139
141
|
% hasher.name,
|
|
140
|
-
"
|
|
142
|
+
"color": "warning",
|
|
143
|
+
"url": "/admin/information",
|
|
144
|
+
"target": "admin",
|
|
141
145
|
}
|
|
142
146
|
]
|
|
143
147
|
return result
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
from django.core.management.base import BaseCommand, CommandError
|
|
2
|
+
|
|
3
|
+
from oauth2_provider.models import get_application_model
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Command(BaseCommand):
|
|
7
|
+
"""Command class."""
|
|
8
|
+
|
|
9
|
+
help = "Add new allowed hosts to frontend Oauth2 application."
|
|
10
|
+
|
|
11
|
+
def add_arguments(self, parser):
|
|
12
|
+
parser.add_argument("hostnames", type=str, nargs="+")
|
|
13
|
+
|
|
14
|
+
def handle(self, *args, **options):
|
|
15
|
+
app_model = get_application_model()
|
|
16
|
+
app = app_model.objects.filter(name="modoboa_frontend").first()
|
|
17
|
+
if not app:
|
|
18
|
+
raise CommandError(
|
|
19
|
+
"Application modoboa_frontend not found. "
|
|
20
|
+
"Make sure you ran load_initial_data first."
|
|
21
|
+
)
|
|
22
|
+
redirect_uris = app.redirect_uris.split(" ")
|
|
23
|
+
post_logout_redirect_uris = app.post_logout_redirect_uris.split(" ")
|
|
24
|
+
for hostname in options["hostnames"]:
|
|
25
|
+
uri = f"https://{hostname}/login/logged"
|
|
26
|
+
if uri not in redirect_uris:
|
|
27
|
+
redirect_uris.append(uri)
|
|
28
|
+
uri = f"https://{hostname}"
|
|
29
|
+
if uri not in post_logout_redirect_uris:
|
|
30
|
+
post_logout_redirect_uris.append(uri)
|
|
31
|
+
app.redirect_uris = " ".join(redirect_uris)
|
|
32
|
+
app.post_logout_redirect_uris = " ".join(post_logout_redirect_uris)
|
|
33
|
+
app.save()
|
|
@@ -192,3 +192,13 @@ class Command(BaseCommand):
|
|
|
192
192
|
}}
|
|
193
193
|
"""
|
|
194
194
|
)
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
# ADD SIGNAL FOR THAT
|
|
198
|
+
# def load_initial_data(self):
|
|
199
|
+
# """Create records for existing domains and co."""
|
|
200
|
+
# for dom in Domain.objects.all():
|
|
201
|
+
# policy = create_user_and_policy("@{0}".format(dom.name))
|
|
202
|
+
# for domalias in dom.domainalias_set.all():
|
|
203
|
+
# domalias_pattern = "@{0}".format(domalias.name)
|
|
204
|
+
# create_user_and_use_policy(domalias_pattern, policy)
|
|
@@ -3,15 +3,33 @@
|
|
|
3
3
|
from django.db import migrations
|
|
4
4
|
|
|
5
5
|
|
|
6
|
+
class RenameIndexIfExists(migrations.RenameIndex):
|
|
7
|
+
|
|
8
|
+
def database_forwards(self, app_label, schema_editor, from_state, to_state):
|
|
9
|
+
from_model = from_state.apps.get_model(app_label, self.model_name)
|
|
10
|
+
columns = [
|
|
11
|
+
from_model._meta.get_field(field).column for field in ["email", "is_active"]
|
|
12
|
+
]
|
|
13
|
+
matching_index_name = schema_editor._constraint_names(
|
|
14
|
+
from_model,
|
|
15
|
+
column_names=columns,
|
|
16
|
+
index=True,
|
|
17
|
+
unique=False,
|
|
18
|
+
)
|
|
19
|
+
if len(matching_index_name) != 1:
|
|
20
|
+
return
|
|
21
|
+
super().database_forwards(app_label, schema_editor, from_state, to_state)
|
|
22
|
+
|
|
23
|
+
|
|
6
24
|
class Migration(migrations.Migration):
|
|
7
25
|
dependencies = [
|
|
8
26
|
("core", "0024_alter_user_language"),
|
|
9
27
|
]
|
|
10
28
|
|
|
11
29
|
operations = [
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
30
|
+
RenameIndexIfExists(
|
|
31
|
+
model_name="user",
|
|
32
|
+
new_name="core_user_email_c0c03f_idx",
|
|
33
|
+
old_fields=("email", "is_active"),
|
|
34
|
+
),
|
|
17
35
|
]
|
modoboa/core/tests/test_core.py
CHANGED
|
@@ -186,6 +186,30 @@ class ManagementCommandsTestCase(SimpleModoTestCase):
|
|
|
186
186
|
with self.assertRaises(models.User.DoesNotExist):
|
|
187
187
|
account.refresh_from_db()
|
|
188
188
|
|
|
189
|
+
def test_add_allowed_hosts(self):
|
|
190
|
+
with self.assertRaises(management.CommandError):
|
|
191
|
+
management.call_command(
|
|
192
|
+
"add_allowed_hosts", "app1.domain.tld", "app2.domain.tld"
|
|
193
|
+
)
|
|
194
|
+
management.call_command("load_initial_data")
|
|
195
|
+
management.call_command(
|
|
196
|
+
"add_allowed_hosts", "app1.domain.tld", "app2.domain.tld"
|
|
197
|
+
)
|
|
198
|
+
app_model = get_application_model()
|
|
199
|
+
app = app_model.objects.filter(name="modoboa_frontend").first()
|
|
200
|
+
self.assertIn("app1.domain.tld", app.redirect_uris)
|
|
201
|
+
self.assertIn("app2.domain.tld", app.redirect_uris)
|
|
202
|
+
self.assertIn("app1.domain.tld", app.post_logout_redirect_uris)
|
|
203
|
+
self.assertIn("app2.domain.tld", app.post_logout_redirect_uris)
|
|
204
|
+
|
|
205
|
+
# Check if same hostname is not added more than once
|
|
206
|
+
uri_count = len(app.redirect_uris.split(" "))
|
|
207
|
+
management.call_command(
|
|
208
|
+
"add_allowed_hosts", "app1.domain.tld", "app2.domain.tld"
|
|
209
|
+
)
|
|
210
|
+
app.refresh_from_db()
|
|
211
|
+
self.assertEqual(len(app.redirect_uris.split(" ")), uri_count)
|
|
212
|
+
|
|
189
213
|
|
|
190
214
|
class APICommunicationTestCase(ModoTestCase):
|
|
191
215
|
"""Check communication with the API."""
|
modoboa/core/utils.py
CHANGED
|
@@ -111,6 +111,9 @@ def get_capabilities():
|
|
|
111
111
|
if is_rspamd_installed:
|
|
112
112
|
rspamd_options = get_rspamd_options()
|
|
113
113
|
capabilities.update({"rspamd": rspamd_options})
|
|
114
|
+
# Amavis
|
|
115
|
+
if "modoboa.amavis" in settings.MODOBOA_APPS:
|
|
116
|
+
capabilities.update({"amavis": {}})
|
|
114
117
|
# IMAP migration
|
|
115
118
|
if "modoboa.imap_migration" in settings.MODOBOA_APPS:
|
|
116
119
|
capabilities.update({"imap_migration": {}})
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{C as q}from"./ChoiceField-B3ReQHVe.js";import{u as M,b as S,g as V,h as f,i as _,c as R,o as y,w as h,a as d,f as s,e as U,t as x,k as N,_ as C,z as D,m as j,l as Q,F as T,s as G}from"./index-BImkz5Jx.js";import{V as H,a as L}from"./VRow-ozg66L7j.js";import{V as B}from"./VAlert-BuaaYN2h.js";import{c as P}from"./VAvatar-Cmga0vj6.js";import{V as k,r as F}from"./VForm-D5iPGkde.js";import{_ as z}from"./AccountPasswordSubForm-DZGt_Xgq.js";import{E as I}from"./EmailField-CwcwI5xW.js";import{V as E}from"./VTextField-XoGTj1KG.js";import{V as $}from"./VSwitch-DwxdeAEq.js";import{u as K}from"./domains.store-D-vWCEIK.js";import{a as Y}from"./accounts-DUzbx6k8.js";import{V as J}from"./VChip-D_styETR.js";const W={class:"headline"},X={class:"mt-4"},Ve={__name:"AccountRoleForm",props:{modelValue:{type:Object,default:null}},setup(g,{expose:w}){const i=M(),{$gettext:l}=S(),v=g,m=V(),c=f(()=>v.modelValue),a=f(()=>i.authUser),p=f(()=>{const r=b.value.find(n=>n.value===c.value.role);return r!==void 0?r.label:""}),e=f(()=>{const r=b.value.find(n=>n.value===c.value.role);return r!==void 0?r.help:""}),b=f(()=>a.value.role===_.SUPER_ADMIN?[...A,...o,...u,...t]:a.value.role===_.DOMAIN_ADMIN?[...A]:a.value.role===_.RESELLER?[...o,...A]:[]),A=[{label:l("Simple user"),value:_.USER,help:l("A user with no privileges but with a mailbox. He will be allowed to use all end-user features: webmail, calendar, contacts, filters.")}],o=[{label:l("Domain administrator"),value:_.DOMAIN_ADMIN,help:l("A user with privileges on one or more domain. He will be allowed to administer mailboxes and he can also have a mailbox.")}],u=[{label:l("Reseller"),value:_.RESELLER,help:l("An intermediate user who has the same privileges than a Domain administrator, plus the possibility to create domains and to assign resources.")}],t=[{label:l("Super administrator"),value:_.SUPER_ADMIN,help:l("A user with all privileges, can do anything. By default, such a user does not have a mailbox so he can't access end-user features.")}];return w({vFormRef:m,roleLabel:p}),(r,n)=>(y(),R(k,{ref_key:"vFormRef",ref:m},{default:h(()=>[d(H,null,{default:h(()=>[d(L,{cols:"7"},{default:h(()=>[d(s(q),{modelValue:c.value.role,"onUpdate:modelValue":n[0]||(n[0]=O=>c.value.role=O),label:s(l)("Choose a role"),choices:b.value,"choices-per-line":2},null,8,["modelValue","label","choices"])]),_:1}),d(L,{cols:"5"},{default:h(()=>[d(B,{color:"primary",class:"mt-11 ml-4 rounded-lg"},{default:h(()=>[U("h3",W,x(p.value),1),U("p",X,x(e.value),1),d(P,{color:"white",class:"float-right",size:"large",icon:"mdi-help-circle-outline"})]),_:1})]),_:1})]),_:1})]),_:1},512))}},Z={class:"m-label"},ee={class:"m-label"},ae={class:"m-label"},he={__name:"AccountGeneralForm",props:{modelValue:{type:Object,default:null},editing:{type:Boolean,default:!1}},setup(g,{expose:w}){const{$gettext:i}=S(),l=M(),v=g,m=V(),c=V({}),a=f(()=>v.modelValue),p=f(()=>a.value.role===_.USER?i("Enter an email address"):i("Enter a simple username or an email address")),e=f(()=>a.value.role===_.USER?"email":"text");function b(){a.value.username.indexOf("@")!==-1&&(a.value.mailbox.full_address=a.value.username,a.value.mailbox.message_limit=null)}return w({vFormRef:m,formErrors:c}),(A,o)=>(y(),R(k,{ref_key:"vFormRef",ref:m},{default:h(()=>[U("label",Z,x(s(i)("Username")),1),d(I,{ref:"username",modelValue:a.value.username,"onUpdate:modelValue":[o[0]||(o[0]=u=>a.value.username=u),b],placeholder:p.value,type:e.value,rules:e.value==="email"?[s(F).required,s(F).email]:[s(F).required],role:a.value.role,"error-msg":c.value.value?c.value.value.username:[]},null,8,["modelValue","placeholder","type","rules","role","error-msg"]),U("label",ee,x(s(i)("First name")),1),d(E,{modelValue:a.value.first_name,"onUpdate:modelValue":o[1]||(o[1]=u=>a.value.first_name=u),autocomplete:"new-password",variant:"outlined",density:"compact"},null,8,["modelValue"]),U("label",ae,x(s(i)("Last name")),1),d(E,{modelValue:a.value.last_name,"onUpdate:modelValue":o[2]||(o[2]=u=>a.value.last_name=u),autocomplete:"new-password",variant:"outlined",density:"compact"},null,8,["modelValue"]),s(l).authUser.pk!==a.value.pk?(y(),R(z,{key:0,ref:"passwordForm",modelValue:a.value,"onUpdate:modelValue":o[3]||(o[3]=u=>a.value=u),editing:g.editing,"form-errors":c.value},null,8,["modelValue","editing","form-errors"])):(y(),R(B,{key:1,type:"info",variant:"tonal",class:"py-2"},{default:h(()=>[N(x(s(i)("You can update your password from the Account section")),1)]),_:1})),d($,{modelValue:a.value.is_active,"onUpdate:modelValue":o[4]||(o[4]=u=>a.value.is_active=u),label:s(i)("Enabled"),density:"compact",color:"primary"},null,8,["modelValue","label"])]),_:1},512))}},le={class:"m-label"},oe={class:"m-label"},te={__name:"AccountMailboxForm",props:{modelValue:{type:Object,default:null}},emits:["update:modelValue"],setup(g,{expose:w,emit:i}){const{$gettext:l}=S(),v=i,m=g,c=K(),a=f(()=>c.domains),p=V(),e=V({mailbox:{}});D(m.modelValue,t=>{t?(e.value={...t},e.value.role===_.USER&&(e.value.mailbox||(e.value.mailbox={}),e.value.mailbox.full_address=e.value.username),e.value.mailbox.message_limit===""&&(e.value.mailbox.message_limit=null)):e.value={mailbox:{}}},{immediate:!0}),D(e,t=>{v("update:modelValue",t)},{deep:!0});const b=f(()=>{const t=e.value.mailbox.full_address;if(t&&t.indexOf("@")!==-1){const r=a.value.find(n=>n.name===t.split("@")[1]);if(r)return parseInt(r.default_mailbox_quota)}}),A=f(()=>{let t=l("Use domain default value");return b.value!==void 0&&(b.value===0?t+=` (${l("unlimited")})`:t+=` (${b.value} MB)`),t});function o(t){t&&delete e.value.mailbox.quota}function u(t){t===""&&(e.value.mailbox.message_limit=null)}return w({vFormRef:p}),(t,r)=>(y(),R(k,{ref_key:"vFormRef",ref:p},{default:h(()=>[U("label",le,x(s(l)("Quota")),1),d($,{modelValue:e.value.mailbox.use_domain_quota,"onUpdate:modelValue":[r[0]||(r[0]=n=>e.value.mailbox.use_domain_quota=n),o],label:A.value,color:"primary"},null,8,["modelValue","label"]),e.value.mailbox.use_domain_quota?j("",!0):(y(),R(E,{key:0,modelValue:e.value.mailbox.quota,"onUpdate:modelValue":r[1]||(r[1]=n=>e.value.mailbox.quota=n),placeholder:s(l)("Ex: 10MB. Leave empty for no limit"),hint:s(l)("Quota for this mailbox, can be expressed in KB, MB (default) or GB. Define a custom value or use domain's default one. Leave empty to define an unlimited value (not allowed for domain administrators)."),"persistent-hint":"",variant:"outlined",density:"compact"},null,8,["modelValue","placeholder","hint"])),U("label",oe,x(s(l)("Daily message sending limit")),1),d(E,{modelValue:e.value.mailbox.message_limit,"onUpdate:modelValue":[r[2]||(r[2]=n=>e.value.mailbox.message_limit=n),u],placeholder:s(l)("Leave empty for no limit"),hint:s(l)("Number of messages this mailbox can send per day. Leave empty for no limit."),"persistent-hint":"",variant:"outlined",density:"compact",rules:[s(F).numericOrNull]},null,8,["modelValue","placeholder","hint","rules"]),d($,{modelValue:e.value.mailbox.is_send_only,"onUpdate:modelValue":r[3]||(r[3]=n=>e.value.mailbox.is_send_only=n),label:s(l)("Send only account"),density:"compact",color:"primary"},null,8,["modelValue","label"])]),_:1},512))}},xe=C(te,[["__scopeId","data-v-41714779"]]),ye={__name:"AccountAliasForm",props:{modelValue:{type:Object,default:null}},setup(g,{expose:w}){const{$gettext:i}=S(),l=g,v=f(()=>l.modelValue),m=V(""),c=V(),a=V(),p=V(!1),e=V([]);async function b(){if(e.value=[],p.value=!0,v.value.aliases.indexOf(m.value)!==-1){e.value=[i("Alias already added")],m.value="",p.value=!1;return}try{await Y.validate({aliases:[m.value],mailbox:v.value.mailbox}),v.value.aliases.push(m.value),m.value="",a.value.resetValidation()}catch(o){o.response.data.aliases?o.response.data.aliases:o.response.data.non_field_errors&&o.response.data.non_field_errors}finally{p.value=!1}}function A(o){v.value.aliases.splice(o,1)}return w({vFormRef:a}),(o,u)=>(y(),R(k,{ref_key:"vFormRef",ref:a},{default:h(()=>[d(s(I),{ref_key:"aliasField",ref:c,modelValue:m.value,"onUpdate:modelValue":u[0]||(u[0]=t=>m.value=t),placeholder:s(i)("Start typing a name here..."),hint:s(i)("Alias(es) of this mailbox. To create a catchall alias, just enter the domain name (@domain.tld)."),"persistent-hint":"",loading:p.value,"error-msg":e.value,onDomainSelected:b},null,8,["modelValue","placeholder","hint","loading","error-msg"]),(y(!0),Q(T,null,G(v.value.aliases,(t,r)=>(y(),R(J,{key:r,class:"mr-2 mt-2",closable:"","onClick:close":n=>A(r)},{default:h(()=>[N(x(t),1)]),_:2},1032,["onClick:close"]))),128))]),_:1},512))}};export{xe as A,ye as _,he as a,Ve as b};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{b as j,u as H,B as J,q as K,g as c,h as P,i as T,d as W,z as X,c as d,l as p,o,a,e as v,w as e,m as k,f as m,k as f,t as r,F as B,_ as Y}from"./index-BImkz5Jx.js";import{b as Z,a as ee,A as ae,_ as le}from"./AccountAliasForm-BuSy_1n9.js";import{_ as te}from"./ResourcesForm-OaqdRYVs.js";import{L as oe}from"./LoadingData-CdVvm4FI.js";import{a as q}from"./accounts-DUzbx6k8.js";import{p as se}from"./parameters-aSQiR7kN.js";import{u as ue}from"./permissions-DNoefz-n.js";import{d as V,c as U,V as L}from"./VAvatar-Cmga0vj6.js";import{V as g,c as b,a as x,b as re}from"./VExpansionPanels-BmH5Jl2Z.js";import{V as _,a as n}from"./VRow-ozg66L7j.js";import{V as ne,a as ie}from"./VToolbar-CFZfqeOr.js";import"./ChoiceField-B3ReQHVe.js";import"./VAlert-BuaaYN2h.js";import"./VProgressCircular-BtOPiGCg.js";import"./tag-B_yWNNJD.js";import"./VForm-D5iPGkde.js";import"./VInput-CoDJzvaW.js";import"./forwardRefs-cvcnlhoK.js";import"./ssrBoot-CKUX4kcb.js";import"./AccountPasswordSubForm-DZGt_Xgq.js";import"./VTextField-XoGTj1KG.js";import"./VSwitch-DwxdeAEq.js";import"./VSelectionControl-zyz-fJvC.js";import"./EmailField-CwcwI5xW.js";import"./domains.store-D-vWCEIK.js";import"./domains-BSawReeu.js";import"./VChip-D_styETR.js";import"./VContainer-B46caNs1.js";/* empty css */const de={key:1},me={key:0},fe={key:0},ce={class:"mr-2"},_e={class:"mr-2"},ve={key:0},pe={class:"mr-2"},ke={class:"mr-2"},ye={class:"mr-2"},Ve={key:0},ge={class:"mr-2"},be={key:0},xe={class:"mr-2"},Fe={class:"mr-2"},we={class:"mt-4 d-flex justify-end"},he={__name:"AccountEditForm",setup(G){const{$gettext:i}=j(),N=H(),F=J(),O=K(),{canSetRole:S}=ue(),l=c({pk:F.params.id}),w=P(()=>l.value.username&&l.value.username.indexOf("@")!==-1),$=P(()=>{const t=C.value.params&&C.value.params.enable_admin_limits&&l.value.role!==T.USER&&l.value.role!==T.SUPER_ADMIN;return t===void 0?!1:t}),C=c({}),h=c(0),y=c(!1),E=c(!1),A=c(),I=c(),M=c(),D=c(),R=c(),Q=P(()=>{const t={roleForm:A,generalForm:I};return w.value&&(t.aliasForm=D,t.mailboxForm=M),$.value&&(t.resourcesForm=R),t});async function z(){E.value=!1,h.value=[];for(const[t,s]of Object.entries(Q.value))if(s.value!=null){const{valid:u}=await s.value.vFormRef.validate();u||(E.value=!0,h.value.push(t))}if(!E.value){y.value=!0;try{const t={...l.value};w.value?t.mailbox.full_address=t.username:t.mailbox&&delete t.mailbox.full_address,t.new_password&&(t.password=t.new_password,delete t.new_password,delete t.password_confirmation),$.value&&R.value!==null&&(t.resources=R.value.getPayload()),t.aliases===null&&delete t.aliases,S.value||(l.value.pk===N.authUser.pk?t.role=N.authUser.role:t.role=T.USER),await q.patch(l.value.pk,t),O.push({name:"AccountDetail",params:{id:F.params.id}})}finally{y.value=!1}}}return W(()=>{q.get(F.params.id).then(t=>{l.value=t.data,y.value=!1}),S.value&&se.getGlobalApplication("limits").then(t=>{C.value=t.data})}),X(l,()=>{delete l.value.domains,l.value.mailbox===null&&delete l.value.mailbox},{deep:!0}),(t,s)=>y.value?(o(),d(oe,{key:0})):(o(),p("div",de,[a(re,{modelValue:h.value,"onUpdate:modelValue":s[5]||(s[5]=u=>h.value=u),multiple:E.value},{default:e(()=>[m(S)?(o(),d(g,{key:0,eager:"",value:"roleForm"},{default:e(()=>[a(b,null,{default:e(({expanded:u})=>[a(_,{"no-gutters":""},{default:e(()=>[a(n,{cols:"4"},{default:e(()=>[f(r(m(i)("Role")),1)]),_:1}),a(n,{cols:"8",class:"text-medium-emphasis"},{default:e(()=>[a(V,{"leave-absolute":""},{default:e(()=>[u?(o(),p("span",me)):(o(),d(_,{key:1,"no-gutters":"",style:{width:"100%"}},{default:e(()=>[A.value?(o(),d(n,{key:0,cols:"6"},{default:e(()=>[f(r(A.value.roleLabel),1)]),_:1})):k("",!0)]),_:1}))]),_:2},1024)]),_:2},1024)]),_:2},1024)]),_:1}),a(x,null,{default:e(()=>[a(Z,{ref_key:"roleForm",ref:A,modelValue:l.value,"onUpdate:modelValue":s[0]||(s[0]=u=>l.value=u)},null,8,["modelValue"])]),_:1})]),_:1})):k("",!0),a(g,{eager:"",value:"generalForm"},{default:e(()=>[a(b,null,{default:e(({expanded:u})=>[a(_,{"no-gutters":""},{default:e(()=>[a(n,{cols:"4"},{default:e(()=>[f(r(m(i)("Identification")),1)]),_:1}),a(n,{cols:"8",class:""},{default:e(()=>[a(V,{"leave-absolute":""},{default:e(()=>[u?(o(),p("span",fe)):(o(),d(_,{key:1,"no-gutters":"",style:{width:"100%"}},{default:e(()=>[a(n,{cols:"6"},{default:e(()=>[v("div",ce,r(m(i)("Username:"))+" "+r(l.value.username),1)]),_:1}),a(n,{cols:"6"},{default:e(()=>[v("div",_e,[f(r(m(i)("Enabled"))+" ",1),l.value.is_active?(o(),d(U,{key:0,color:"success"},{default:e(()=>s[7]||(s[7]=[f("mdi-check-circle-outline")])),_:1,__:[7]})):(o(),d(U,{key:1},{default:e(()=>s[8]||(s[8]=[f("mdi-close-circle-outline")])),_:1,__:[8]}))])]),_:1})]),_:1}))]),_:2},1024)]),_:2},1024)]),_:2},1024)]),_:1}),a(x,null,{default:e(()=>[a(ee,{ref_key:"generalForm",ref:I,modelValue:l.value,"onUpdate:modelValue":s[1]||(s[1]=u=>l.value=u),editing:""},null,8,["modelValue"])]),_:1})]),_:1}),w.value?(o(),d(g,{key:1,eager:"",value:"mailboxForm"},{default:e(()=>[a(b,null,{default:e(({expanded:u})=>[a(_,{"no-gutters":""},{default:e(()=>[a(n,{cols:"4"},{default:e(()=>[f(r(m(i)("Mailbox")),1)]),_:1}),a(n,{cols:"8",class:""},{default:e(()=>[a(V,{"leave-absolute":""},{default:e(()=>[u?(o(),p("span",ve)):(o(),d(_,{key:1,"no-gutters":"",style:{width:"100%"}},{default:e(()=>[l.value.mailbox?(o(),p(B,{key:0},[l.value.mailbox.use_domain_quota?(o(),d(n,{key:0,cols:"6"},{default:e(()=>[v("div",pe,r(m(i)("Quota: "))+" "+r(m(i)("domain's default value")),1)]),_:1})):(o(),d(n,{key:1,cols:"6"},{default:e(()=>[v("div",ke,r(m(i)("Quota: "))+" "+r(l.value.mailbox.quota),1)]),_:1})),a(n,{cols:"6"},{default:e(()=>[v("div",ye,[f(r(m(i)("Send only"))+" ",1),l.value.mailbox.is_send_only?(o(),d(U,{key:0,color:"success"},{default:e(()=>s[9]||(s[9]=[f("mdi-check-circle-outline")])),_:1,__:[9]})):(o(),d(U,{key:1},{default:e(()=>s[10]||(s[10]=[f("mdi-close-circle-outline")])),_:1,__:[10]}))])]),_:1})],64)):k("",!0)]),_:1}))]),_:2},1024)]),_:2},1024)]),_:2},1024)]),_:1}),a(x,null,{default:e(()=>[a(ae,{ref_key:"mailboxForm",ref:M,modelValue:l.value,"onUpdate:modelValue":s[2]||(s[2]=u=>l.value=u)},null,8,["modelValue"])]),_:1})]),_:1})):k("",!0),w.value?(o(),d(g,{key:2,eager:"",value:"aliasForm"},{default:e(()=>[a(b,null,{default:e(({expanded:u})=>[a(_,{"no-gutters":""},{default:e(()=>[a(n,{cols:"4"},{default:e(()=>[f(r(m(i)("Alias")),1)]),_:1}),a(n,{cols:"8",class:""},{default:e(()=>[a(V,{"leave-absolute":""},{default:e(()=>[u?(o(),p("span",Ve)):(o(),d(_,{key:1,"no-gutters":"",style:{width:"100%"}},{default:e(()=>[l.value.aliases?(o(),d(n,{key:0,cols:"6"},{default:e(()=>[v("div",ge,r(m(i)("Number of associated alias:"))+" "+r(l.value.aliases.length),1)]),_:1})):k("",!0)]),_:1}))]),_:2},1024)]),_:2},1024)]),_:2},1024)]),_:1}),a(x,null,{default:e(()=>[a(le,{ref_key:"aliasForm",ref:D,modelValue:l.value,"onUpdate:modelValue":s[3]||(s[3]=u=>l.value=u)},null,8,["modelValue"])]),_:1})]),_:1})):k("",!0),$.value?(o(),d(g,{key:3,eager:"",value:"resourcesForm"},{default:e(()=>[a(b,null,{default:e(({expanded:u})=>[a(_,{"no-gutters":""},{default:e(()=>[a(n,{cols:"4"},{default:e(()=>[f(r(m(i)("Resources")),1)]),_:1}),a(n,{cols:"8",class:""},{default:e(()=>[a(V,{"leave-absolute":""},{default:e(()=>[u?(o(),p("span",be)):(o(),d(_,{key:1,"no-gutters":"",style:{width:"100%"}},{default:e(()=>[l.value.resources!=null&&l.value.resources.length==2?(o(),p(B,{key:0},[a(n,{cols:"6"},{default:e(()=>[v("div",xe,r(m(i)("Number of allowed mailboxes:"))+" "+r(l.value.resources[0].max_value),1)]),_:1}),a(n,{cols:"6"},{default:e(()=>[v("div",Fe,r(m(i)("Number of allowed mailbox aliases:"))+" "+r(l.value.resources[1].max_value),1)]),_:1})],64)):k("",!0)]),_:1}))]),_:2},1024)]),_:2},1024)]),_:2},1024)]),_:1}),a(x,null,{default:e(()=>[a(te,{ref_key:"resourcesForm",ref:R,modelValue:l.value.resources,"onUpdate:modelValue":s[4]||(s[4]=u=>l.value.resources=u)},null,8,["modelValue"])]),_:1})]),_:1})):k("",!0)]),_:1},8,["modelValue","multiple"]),v("div",we,[a(L,{loading:y.value,variant:"outlined",onClick:s[6]||(s[6]=u=>t.$router.go(-1))},{default:e(()=>[f(r(m(i)("Cancel")),1)]),_:1},8,["loading"]),a(L,{class:"ml-4",color:"primary darken-1",variant:"outlined",loading:y.value,onClick:z},{default:e(()=>[f(r(m(i)("Save")),1)]),_:1},8,["loading"])])]))}},Ee={__name:"AccountEditView",setup(G){const{$gettext:i}=j();return(N,F)=>(o(),p(B,null,[a(ne,{flat:""},{default:e(()=>[a(ie,null,{default:e(()=>[f(r(m(i)("Edit account")),1)]),_:1})]),_:1}),a(he)],64))}},la=Y(Ee,[["__scopeId","data-v-4019c7f8"]]);export{la as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{u as n,b as c,g as u,h as s,c as l,o as f}from"./index-BImkz5Jx.js";import{_}from"./ConnectedLayout-UWjiYBNw.js";import{p as h}from"./parameters-aSQiR7kN.js";import"./VContainer-B46caNs1.js";/* empty css */import"./tag-B_yWNNJD.js";import"./VApp-CKP-6zGP.js";import"./layout-C5FyYCHK.js";import"./ssrBoot-CKUX4kcb.js";import"./logos-BswdveCV.js";import"./layout.store-NXWtFIwL.js";import"./VAvatar-Cmga0vj6.js";import"./VProgressCircular-BtOPiGCg.js";import"./forwardRefs-cvcnlhoK.js";import"./VMenu-gUG70-zD.js";import"./global.store-DbkcI5o2.js";import"./VBadge-CixeK87a.js";import"./VCard-CxH9DWoK.js";import"./VRow-ozg66L7j.js";import"./VSheet-BNx2X4Mk.js";const q={__name:"AccountLayout",setup(d){const i=n(),{$gettext:e}=c(),r=u([]),m=s(()=>i.authUser),p=s(()=>{const t=[],a={contacts:"mdi-contacts-outline",webmail:"mdi-at"};if(t.push({text:e("Settings"),to:{name:"AccountSettings"},icon:"mdi-cog"}),m.value.mailbox){t.push({text:e("Filters"),to:{name:"AccountFilters"},icon:"mdi-filter"});for(const o of r.value)t.push({icon:a[o.name],text:o.label,to:{name:"AccountParametersEdit",params:{app:o.name}}})}return t});return h.getUserApplications().then(t=>{r.value=t.data}),(t,a)=>(f(),l(_,{color:"grey","menu-items":p.value},null,8,["menu-items"]))}};export{q as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{b as P,n as _,h as b,l as m,o as n,m as p,a as o,F as V,e as v,t as y,f as l,w as u,c as h}from"./index-BImkz5Jx.js";import{a as E}from"./accounts-DUzbx6k8.js";import{r as c}from"./VForm-D5iPGkde.js";import{V as w}from"./VTextField-XoGTj1KG.js";import{a as g,V as R}from"./VRow-ozg66L7j.js";import{V as S}from"./VSwitch-DwxdeAEq.js";import{V as q}from"./VAlert-BuaaYN2h.js";import{V as A}from"./VAvatar-Cmga0vj6.js";const F={class:"m-label"},N={class:"text-white mr-4"},H={__name:"AccountPasswordSubForm",props:{modelValue:{type:Object,default:null},withPasswordCheck:{type:Boolean,default:!1},disabled:{type:Boolean,default:!1},editing:{type:Boolean,default:!1},formErrors:{type:Object,required:!1,default:()=>{}}},setup(s){const{$gettext:t}=P(),k=_(),r=s,e=b(()=>r.modelValue),f=b(()=>!r.disabled&&(!r.editing||e.value.password)),x=i=>i===e.value.new_password||t("Password mismatch");function C(){navigator.clipboard.writeText(e.value.new_password).then(()=>{k.displayNotification({msg:t("Password copied to clipboard")})})}function B(i){i?E.getRandomPassword().then(a=>{e.value.new_password=a.data.password}):(e.value.new_password=null,e.value.password_confirmation=null)}return(i,a)=>(n(),m("div",null,[s.withPasswordCheck?(n(),m(V,{key:0},[v("label",F,y(l(t)("Current password")),1),o(w,{modelValue:e.value.password,"onUpdate:modelValue":a[0]||(a[0]=d=>e.value.password=d),autocomplete:"new-password",variant:"outlined",type:"password",density:"compact","validate-on":"submit",rules:s.disabled?[]:[l(c).required],disabled:s.disabled,"error-messages":r.formErrors?r.formErrors.password:[]},null,8,["modelValue","rules","disabled","error-messages"])],64)):p("",!0),o(R,{justify:"center"},{default:u(()=>[o(g,{cols:"auto"},{default:u(()=>[o(S,{modelValue:e.value.random_password,"onUpdate:modelValue":[a[1]||(a[1]=d=>e.value.random_password=d),B],label:l(t)("Random password"),density:"compact",color:"primary",class:"text-center",disabled:s.disabled},null,8,["modelValue","label","disabled"])]),_:1}),o(g,null,{default:u(()=>[e.value.random_password?(n(),h(q,{key:0,style:{"background-color":"#515d78"},class:"ml-6",density:"compact"},{default:u(()=>[v("span",N,y(e.value.new_password),1),o(A,{size:"small",color:"white",variant:"text",density:"compact",disabled:s.disabled,icon:"mdi-clipboard-multiple-outline",title:l(t)("Copy to clipboard"),onClick:C},null,8,["disabled","title"])]),_:1})):p("",!0)]),_:1})]),_:1}),e.value.random_password?p("",!0):(n(),m(V,{key:1},[o(w,{modelValue:e.value.new_password,"onUpdate:modelValue":a[2]||(a[2]=d=>e.value.new_password=d),variant:"outlined",label:l(t)("Password"),type:"password",autocomplete:"new-password",density:"compact",disabled:s.disabled,rules:f.value?[l(c).required]:[]},null,8,["modelValue","label","disabled","rules"]),o(w,{modelValue:e.value.password_confirmation,"onUpdate:modelValue":a[3]||(a[3]=d=>e.value.password_confirmation=d),variant:"outlined",type:"password",label:l(t)("Confirmation"),density:"compact",disabled:s.disabled,rules:f.value?[l(c).required,x]:[],"error-messages":r.formErrors?r.formErrors.new_password:[]},null,8,["modelValue","label","disabled","rules","error-messages"])],64))]))}};export{H as _};
|