modoboa 2.5.1__py3-none-any.whl → 2.6.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (178) hide show
  1. modoboa/admin/api/v2/serializers.py +1 -1
  2. modoboa/admin/models/domain.py +1 -1
  3. modoboa/admin/models/mixins.py +3 -1
  4. modoboa/autoconfig/__init__.py +0 -0
  5. modoboa/autoconfig/apps.py +6 -0
  6. modoboa/autoconfig/templates/autoconfig/autoconfig.xml +24 -0
  7. modoboa/autoconfig/templates/autoconfig/autodiscover.xml +27 -0
  8. modoboa/autoconfig/tests.py +38 -0
  9. modoboa/autoconfig/urls.py +21 -0
  10. modoboa/autoconfig/views.py +109 -0
  11. modoboa/autoreply/api/v2/tests.py +4 -4
  12. modoboa/calendars/tests.py +8 -8
  13. modoboa/contacts/tests.py +5 -7
  14. modoboa/core/api/v1/tests.py +2 -2
  15. modoboa/core/commands/templates/settings.py.tpl +14 -1
  16. modoboa/core/models.py +1 -1
  17. modoboa/core/tests/test_ldap.py +2 -0
  18. modoboa/core/views/auth.py +27 -8
  19. modoboa/frontend_dist/assets/{AccountAliasForm-C0oHHyZL.js → AccountAliasForm-Bu1I8sLg.js} +1 -1
  20. modoboa/frontend_dist/assets/{AccountEditView-lgSJ2Se8.js → AccountEditView-YOpDW-Ob.js} +1 -1
  21. modoboa/frontend_dist/assets/AccountLayout-Cf_FVfvJ.js +1 -0
  22. modoboa/frontend_dist/assets/{AccountPasswordSubForm-YsaE_cDx.js → AccountPasswordSubForm-B9X5wAXp.js} +1 -1
  23. modoboa/frontend_dist/assets/{AccountView-1jfKFDwb.js → AccountView-DCqbD7oi.js} +1 -1
  24. modoboa/frontend_dist/assets/{AddressBook-CwN64Zls.js → AddressBook-B53LXIsD.js} +1 -1
  25. modoboa/frontend_dist/assets/AdminLayout-DX-RXYiU.js +1 -0
  26. modoboa/frontend_dist/assets/AlarmsView-C0KvogOj.js +1 -0
  27. modoboa/frontend_dist/assets/AliasEditView-BiMPygn9.js +1 -0
  28. modoboa/frontend_dist/assets/AliasEditView-CYDSiX8l.css +1 -0
  29. modoboa/frontend_dist/assets/AliasRecipientForm-BhQfATiS.js +1 -0
  30. modoboa/frontend_dist/assets/{AliasView-DMzA10eD.js → AliasView-eIct4lMV.js} +1 -1
  31. modoboa/frontend_dist/assets/{AuditTrailView-5dnGX5El.js → AuditTrailView-FiUNTjpE.js} +1 -1
  32. modoboa/frontend_dist/assets/{CalendarView-DZONeDA9.js → CalendarView-DbWVDoWq.js} +1 -1
  33. modoboa/frontend_dist/assets/{ChoiceField-DnwXRkht.js → ChoiceField-17aCFpF6.js} +1 -1
  34. modoboa/frontend_dist/assets/{ComposeEmailForm-kghmfNuE.js → ComposeEmailForm-B-BP3lzX.js} +1 -1
  35. modoboa/frontend_dist/assets/ComposeEmailView-6JrFhK1W.js +1 -0
  36. modoboa/frontend_dist/assets/ConfirmDialog-Dr0i2kqK.js +1 -0
  37. modoboa/frontend_dist/assets/ConnectedLayout-irTI2ekJ.js +1 -0
  38. modoboa/frontend_dist/assets/{CreationForm-CW4lxnPg.js → CreationForm-BZMhQOez.js} +1 -1
  39. modoboa/frontend_dist/assets/DashboardView-CBpsEG3P.js +1 -0
  40. modoboa/frontend_dist/assets/{DomainAdminList-C3jcDDc3.js → DomainAdminList-BikGujRD.js} +1 -1
  41. modoboa/frontend_dist/assets/{DomainEditView-ph8AaElX.js → DomainEditView-SOvfeZWo.js} +1 -1
  42. modoboa/frontend_dist/assets/{DomainTransportForm-NCz6Bl-h.js → DomainTransportForm-CmFZ1Cxm.js} +1 -1
  43. modoboa/frontend_dist/assets/{DomainView-BgMSSuU-.js → DomainView-DasOm4jM.js} +3 -3
  44. modoboa/frontend_dist/assets/{DomainsView-CEEU9btK.js → DomainsView-DVJA4-oN.js} +1 -1
  45. modoboa/frontend_dist/assets/{EmailField-DeqDPm5j.js → EmailField-DpBYkqam.js} +1 -1
  46. modoboa/frontend_dist/assets/{EmailView-DczVhVO0.js → EmailView-D9vO7HHB.js} +1 -1
  47. modoboa/frontend_dist/assets/EmptyLayout-Dz3F8c7x.js +1 -0
  48. modoboa/frontend_dist/assets/{FiltersView-nJj_gSCx.js → FiltersView-FjdtF4HL.js} +1 -1
  49. modoboa/frontend_dist/assets/ForwardEmailView-DF5BQJjL.js +1 -0
  50. modoboa/frontend_dist/assets/{HtmlEditor-BWRdelVw.js → HtmlEditor-Zn0wjDog.js} +1 -1
  51. modoboa/frontend_dist/assets/{IdentitiesView-Dld9IloZ.js → IdentitiesView-Cf5eQoKA.js} +1 -1
  52. modoboa/frontend_dist/assets/{IdentitiesView-DPrrRMS5.css → IdentitiesView-D3AAiZLZ.css} +1 -1
  53. modoboa/frontend_dist/assets/{InformationView-BBWKSX8D.js → InformationView-gEYTQ37R.js} +1 -1
  54. modoboa/frontend_dist/assets/{LoadingData-G57nJ_JV.js → LoadingData-Cxd490P6.js} +1 -1
  55. modoboa/frontend_dist/assets/LoginCallbackView-C9zhejHH.js +1 -0
  56. modoboa/frontend_dist/assets/{LoginView-CqCCXYLo.js → LoginView-sWEH834U.js} +1 -1
  57. modoboa/frontend_dist/assets/MailboxView-Dn3iDym9.js +1 -0
  58. modoboa/frontend_dist/assets/{MenuItems-BqIZW5av.js → MenuItems-BRChEdLp.js} +1 -1
  59. modoboa/frontend_dist/assets/{MessageView-D_6tx_gd.js → MessageView-CDbJxbxm.js} +1 -1
  60. modoboa/frontend_dist/assets/{MessagesView-BH7JIR03.js → MessagesView-C_vBxVzN.js} +1 -1
  61. modoboa/frontend_dist/assets/{MigrationsView-Cv_So9T-.js → MigrationsView-D6peiQuR.js} +1 -1
  62. modoboa/frontend_dist/assets/{ParametersForm-3qXttTuQ.js → ParametersForm-Dip8Sobe.js} +1 -1
  63. modoboa/frontend_dist/assets/ParametersView-CwI0-9kV.js +1 -0
  64. modoboa/frontend_dist/assets/ParametersView-wFKfiEVR.js +1 -0
  65. modoboa/frontend_dist/assets/{ProviderEditView-zh7CY832.js → ProviderEditView-EH2NSz3x.js} +1 -1
  66. modoboa/frontend_dist/assets/{ProviderGeneralForm-BQU7t3ma.js → ProviderGeneralForm-CAVNOJp6.js} +1 -1
  67. modoboa/frontend_dist/assets/{ProvidersView-CoF_ZkZA.js → ProvidersView-BoH99d3a.js} +1 -1
  68. modoboa/frontend_dist/assets/QuarantineLayout--4g9urq2.js +1 -0
  69. modoboa/frontend_dist/assets/{QuarantineView-DNvpoycb.js → QuarantineView-BDzDrWwK.js} +1 -1
  70. modoboa/frontend_dist/assets/ReplyEmailView-D0VgYBnw.js +1 -0
  71. modoboa/frontend_dist/assets/ResourcesForm-C1YGkT8i.js +1 -0
  72. modoboa/frontend_dist/assets/SelfServiceLayout-BYkSLc4n.js +1 -0
  73. modoboa/frontend_dist/assets/{SettingsView-gQiJ2NVb.js → SettingsView-Cy5O-88Y.js} +1 -1
  74. modoboa/frontend_dist/assets/{StatisticsView-DYalet_q.js → StatisticsView-DtlBQK9z.js} +1 -1
  75. modoboa/frontend_dist/assets/{TimeSerieChart-BZ2htbFk.js → TimeSerieChart-DNjvmMDE.js} +1 -1
  76. modoboa/frontend_dist/assets/{TimeSerieChart-CxiwMzE8.css → TimeSerieChart-Dn6Wznn8.css} +1 -1
  77. modoboa/frontend_dist/assets/UserLayout-Cgqq7HVi.js +1 -0
  78. modoboa/frontend_dist/assets/{VAlert-4r6LxKtg.js → VAlert-x6n3drjw.js} +1 -1
  79. modoboa/frontend_dist/assets/{VApp-CX_C7AUN.js → VApp-C74A9rX5.js} +1 -1
  80. modoboa/frontend_dist/assets/{VAutocomplete-DNKmBvyZ.js → VAutocomplete-Bg2AlW_n.js} +1 -1
  81. modoboa/frontend_dist/assets/{VAvatar-DbuoZWmf.js → VAvatar-J563boKh.js} +1 -1
  82. modoboa/frontend_dist/assets/{VBadge-Bv2nvUmC.js → VBadge-CtEsXuih.js} +1 -1
  83. modoboa/frontend_dist/assets/{VCard-DzjUT5OP.js → VCard-CJ2oGh_i.js} +1 -1
  84. modoboa/frontend_dist/assets/{VCheckbox-dr7UFjl4.js → VCheckbox-VicuXIJq.js} +1 -1
  85. modoboa/frontend_dist/assets/{VCheckboxBtn-CpFdBnTv.js → VCheckboxBtn-CLh3m66z.js} +1 -1
  86. modoboa/frontend_dist/assets/{VChip-CaQvfmkw.js → VChip-DvxO74Mw.js} +1 -1
  87. modoboa/frontend_dist/assets/VColorPicker-DcVohVWE.js +1 -0
  88. modoboa/frontend_dist/assets/{VContainer-74Dnn8Ux.js → VContainer-L2EoH3dT.js} +1 -1
  89. modoboa/frontend_dist/assets/{VDataTable-CL7yHvG7.js → VDataTable-CAT0QYJz.js} +1 -1
  90. modoboa/frontend_dist/assets/{VDataTableServer-BqvNcIdw.js → VDataTableServer-DP13Fhfa.js} +1 -1
  91. modoboa/frontend_dist/assets/{VDataTableVirtual--KsOP8i6.js → VDataTableVirtual-LVNemzH7.js} +1 -1
  92. modoboa/frontend_dist/assets/{VDialog-DmTGCGR0.js → VDialog-CaQnQSDn.js} +1 -1
  93. modoboa/frontend_dist/assets/{VExpansionPanels-B7sSTCwd.js → VExpansionPanels-Dnwgt5uT.js} +1 -1
  94. modoboa/frontend_dist/assets/{VFileInput-SULIc6F3.js → VFileInput-z44hJIWr.js} +1 -1
  95. modoboa/frontend_dist/assets/{VForm-DsRLc-sa.js → VForm-BwTUBf4u.js} +1 -1
  96. modoboa/frontend_dist/assets/{VInput-DVKUObZe.js → VInput-Dpmea5z9.js} +1 -1
  97. modoboa/frontend_dist/assets/{VMenu-nv0XOgg0.js → VMenu-DDmBlbM8.js} +1 -1
  98. modoboa/frontend_dist/assets/{VPicker-DnDSWJHJ.js → VPicker-Dz7GbXwU.js} +1 -1
  99. modoboa/frontend_dist/assets/{VProgressCircular-qK6p5X_Y.js → VProgressCircular-CtqQON49.js} +1 -1
  100. modoboa/frontend_dist/assets/{VRadioGroup-CbiPLy0t.js → VRadioGroup-WW8uMAv4.js} +1 -1
  101. modoboa/frontend_dist/assets/{VRow-DJ0NB63-.js → VRow-CAbOSEHS.js} +1 -1
  102. modoboa/frontend_dist/assets/{VSelect-CxCFMHyF.js → VSelect-Cdh0qO5m.js} +1 -1
  103. modoboa/frontend_dist/assets/{VSelectionControl-C-6A4us5.js → VSelectionControl-BN-zQ3C6.js} +1 -1
  104. modoboa/frontend_dist/assets/{VSheet-DI6SxLnG.js → VSheet-9RDZpWCf.js} +1 -1
  105. modoboa/frontend_dist/assets/VSpacer-BWZq1PhD.js +1 -0
  106. modoboa/frontend_dist/assets/{VSwitch-DPnjPQuU.js → VSwitch-CixaedmQ.js} +1 -1
  107. modoboa/frontend_dist/assets/{VTable-ldTxgQPW.js → VTable-DO7ofPSj.js} +1 -1
  108. modoboa/frontend_dist/assets/{VTabs-aS8WSL9I.js → VTabs-C1-E1DJE.js} +1 -1
  109. modoboa/frontend_dist/assets/{VTextField-DKbr4H5w.js → VTextField-C9VUP7V9.js} +1 -1
  110. modoboa/frontend_dist/assets/{VTextarea-BttkFsM4.js → VTextarea-B28SmJ2v.js} +1 -1
  111. modoboa/frontend_dist/assets/{VToolbar-BxX3W2kR.js → VToolbar-Cs-fVa4M.js} +1 -1
  112. modoboa/frontend_dist/assets/VWindowItem-C9PFEbE6.js +1 -0
  113. modoboa/frontend_dist/assets/WebmailLayout-Dp6jIazW.js +1 -0
  114. modoboa/frontend_dist/assets/{accounts-CC2F0a0c.js → accounts-BT429O7P.js} +1 -1
  115. modoboa/frontend_dist/assets/{admin-CHCHFGI6.js → admin-Cu5rjY7m.js} +1 -1
  116. modoboa/frontend_dist/assets/{aliases-C9bUD4Ws.js → aliases-Q4uTcEh4.js} +1 -1
  117. modoboa/frontend_dist/assets/{amavis-BhzV4rgf.js → amavis-Be87kCqw.js} +1 -1
  118. modoboa/frontend_dist/assets/{amavis-DCVJxuui.js → amavis-Bx0pxvRn.js} +1 -1
  119. modoboa/frontend_dist/assets/{contacts-Dxz6eWpf.js → contacts-Dx240BT8.js} +1 -1
  120. modoboa/frontend_dist/assets/{domains-C2cornvL.js → domains-CQv8gtbQ.js} +1 -1
  121. modoboa/frontend_dist/assets/{domains.store-BLKRipG8.js → domains.store-CfpnnBGo.js} +1 -1
  122. modoboa/frontend_dist/assets/{filter-rmxrcjKk.js → filter-C_GJovn5.js} +1 -1
  123. modoboa/frontend_dist/assets/{forwardRefs-CpzzjgpX.js → forwardRefs-DPrLRqxP.js} +1 -1
  124. modoboa/frontend_dist/assets/{global.store-DndbMXYb.js → global.store-CNChIxQL.js} +1 -1
  125. modoboa/frontend_dist/assets/importExport-7sRHU_3R.js +1 -0
  126. modoboa/frontend_dist/assets/index-YIGRKgNA.js +982 -0
  127. modoboa/frontend_dist/assets/{layout-DbjDe3Wl.js → layout-BM4C5b2r.js} +1 -1
  128. modoboa/frontend_dist/assets/{layout.store-Vq5mvIp7.js → layout.store-CCOp6tv6.js} +1 -1
  129. modoboa/frontend_dist/assets/{logos-Bvcy0usu.js → logos-CboXciB_.js} +1 -1
  130. modoboa/frontend_dist/assets/{logs-BuItINky.js → logs-BeuijmJA.js} +1 -1
  131. modoboa/frontend_dist/assets/{parameters-C8IYEP7q.js → parameters-DpVw-Zs4.js} +1 -1
  132. modoboa/frontend_dist/assets/{parameters.store-1cwSP2JP.js → parameters.store-DRarkO6b.js} +1 -1
  133. modoboa/frontend_dist/assets/{permissions-DQjAcO9S.js → permissions-C33K6cJv.js} +1 -1
  134. modoboa/frontend_dist/assets/{ssrBoot-BxIQ9ccA.js → ssrBoot-CpJ-7WlH.js} +1 -1
  135. modoboa/frontend_dist/assets/{tag-3cfI1_f7.js → tag-DV1_zOzl.js} +1 -1
  136. modoboa/frontend_dist/assets/theme-BGzdVPko.js +1 -0
  137. modoboa/frontend_dist/assets/transports-B9mWU1W8.js +1 -0
  138. modoboa/frontend_dist/assets/{webmail-B2IUjaxM.js → webmail-DRyN8JIK.js} +1 -1
  139. modoboa/frontend_dist/index.html +1 -1
  140. modoboa/static/css/offline.css +7 -20
  141. modoboa/templates/registration/base.html +18 -0
  142. modoboa/templates/registration/login.html +1 -1
  143. modoboa/urls.py +1 -0
  144. {modoboa-2.5.1.dist-info → modoboa-2.6.0.dist-info}/METADATA +6 -6
  145. {modoboa-2.5.1.dist-info → modoboa-2.6.0.dist-info}/RECORD +150 -142
  146. {modoboa-2.5.1.dist-info → modoboa-2.6.0.dist-info}/licenses/LICENSE +1 -1
  147. modoboa/frontend_dist/assets/AccountLayout-U386K8zy.js +0 -1
  148. modoboa/frontend_dist/assets/AdminLayout-Cxm1lggg.js +0 -1
  149. modoboa/frontend_dist/assets/AlarmsView-Bcjsicac.js +0 -1
  150. modoboa/frontend_dist/assets/AliasEditView-Cx9410JP.css +0 -1
  151. modoboa/frontend_dist/assets/AliasEditView-k3rVt1tG.js +0 -1
  152. modoboa/frontend_dist/assets/AliasRecipientForm-IehUzKok.js +0 -1
  153. modoboa/frontend_dist/assets/ComposeEmailView-DLv3wk1k.js +0 -1
  154. modoboa/frontend_dist/assets/ConfirmDialog-CcPrCKuI.js +0 -1
  155. modoboa/frontend_dist/assets/ConnectedLayout-CWlBK7Hf.js +0 -1
  156. modoboa/frontend_dist/assets/DashboardView-DXVZMbMo.js +0 -1
  157. modoboa/frontend_dist/assets/EmptyLayout-BXgcfMLH.js +0 -1
  158. modoboa/frontend_dist/assets/ForwardEmailView-Bgv3JQb6.js +0 -1
  159. modoboa/frontend_dist/assets/LoginCallbackView-DjyE2SG_.js +0 -1
  160. modoboa/frontend_dist/assets/MailboxView-DRrs9eLO.js +0 -1
  161. modoboa/frontend_dist/assets/ParametersView-3Ns04cpQ.js +0 -1
  162. modoboa/frontend_dist/assets/ParametersView-B5B5Dt6K.js +0 -1
  163. modoboa/frontend_dist/assets/QuarantineLayout-CYBsrbJM.js +0 -1
  164. modoboa/frontend_dist/assets/ReplyEmailView-D1XTcglu.js +0 -1
  165. modoboa/frontend_dist/assets/ResourcesForm-BW8rUGgZ.js +0 -1
  166. modoboa/frontend_dist/assets/SelfServiceLayout-DfDHiYeX.js +0 -1
  167. modoboa/frontend_dist/assets/UserLayout-zUtHi-z-.js +0 -1
  168. modoboa/frontend_dist/assets/VColorPicker-ByGpCW5O.js +0 -1
  169. modoboa/frontend_dist/assets/VSpacer-CoJVmx8k.js +0 -1
  170. modoboa/frontend_dist/assets/VWindowItem-Cvqvdegd.js +0 -1
  171. modoboa/frontend_dist/assets/WebmailLayout-BT2k6U7q.js +0 -1
  172. modoboa/frontend_dist/assets/importExport-C3uqrcok.js +0 -1
  173. modoboa/frontend_dist/assets/index-LhNzkzAh.js +0 -984
  174. modoboa/frontend_dist/assets/transports-D4Jk4-AP.js +0 -1
  175. {modoboa-2.5.1.data → modoboa-2.6.0.data}/scripts/modoboa-admin.py +0 -0
  176. {modoboa-2.5.1.dist-info → modoboa-2.6.0.dist-info}/WHEEL +0 -0
  177. {modoboa-2.5.1.dist-info → modoboa-2.6.0.dist-info}/entry_points.txt +0 -0
  178. {modoboa-2.5.1.dist-info → modoboa-2.6.0.dist-info}/top_level.txt +0 -0
@@ -745,7 +745,7 @@ class CSVImportSerializer(serializers.Serializer):
745
745
  class CSVIdentityImportSerializer(CSVImportSerializer):
746
746
  """Custom serializer for identity import."""
747
747
 
748
- crypt_passwords = serializers.BooleanField()
748
+ crypt_passwords = serializers.BooleanField(default=False)
749
749
 
750
750
 
751
751
  class AlarmSerializer(serializers.ModelSerializer):
@@ -202,7 +202,7 @@ class Domain(mixins.MessageLimitMixin, AdminObject):
202
202
  return self.dnsrecord_set.filter(type="autodiscover").first()
203
203
 
204
204
  @cached_property
205
- def allocated_quota(self):
205
+ def allocated_quota(self) -> int:
206
206
  """Return current quota allocation."""
207
207
  if not self.quota:
208
208
  return 0
@@ -1,5 +1,7 @@
1
1
  """Admin model mixins."""
2
2
 
3
+ from typing import Optional
4
+
3
5
  from modoboa.policyd.utils import get_message_counter
4
6
 
5
7
 
@@ -18,7 +20,7 @@ class MessageLimitMixin:
18
20
  return self.message_limit - get_message_counter(self.message_counter_key)
19
21
 
20
22
  @property
21
- def sent_messages_in_percent(self):
23
+ def sent_messages_in_percent(self) -> Optional[int]: # noqa
22
24
  """Return number of sent messages as a percentage."""
23
25
  if not self.message_limit:
24
26
  return None
File without changes
@@ -0,0 +1,6 @@
1
+ from django.apps import AppConfig
2
+
3
+
4
+ class AutoconfigConfig(AppConfig):
5
+ default_auto_field = "django.db.models.BigAutoField"
6
+ name = "modoboa.autoconfig"
@@ -0,0 +1,24 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <clientConfig version="1.1">
3
+ <emailProvider id="{{ domain }}">
4
+ <domain>{{ domain }}</domain>
5
+ <displayName>Modoboa</displayName>
6
+ <displayShortName>Mail</displayShortName>
7
+
8
+ <incomingServer type="imap">
9
+ <hostname>{{ connection_settings.imap.HOSTNAME }}</hostname>
10
+ <port>{{ connection_settings.imap.PORT }}</port>
11
+ <socketType>{{ connection_settings.imap.SOCKET_TYPE }}</socketType>
12
+ <authentication>plain</authentication>
13
+ <username>{{ emailaddress }}</username>
14
+ </incomingServer>
15
+
16
+ <outgoingServer type="smtp">
17
+ <hostname>{{ connection_settings.smtp.HOSTNAME }}</hostname>
18
+ <port>{{ connection_settings.smtp.PORT }}</port>
19
+ <socketType>{{ connection_settings.smtp.SOCKET_TYPE }}</socketType>
20
+ <authentication>plain</authentication>
21
+ <username>{{ emailaddress }}</username>
22
+ </outgoingServer>
23
+ </emailProvider>
24
+ </clientConfig>
@@ -0,0 +1,27 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <Autodiscover xmlns="http://schemas.microsoft.com/exchange/autodiscover/responseschema/2006">
3
+ <Response xmlns="http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a">
4
+ <Account>
5
+ <AccountType>email</AccountType>
6
+ <Action>settings</Action>
7
+
8
+ <Protocol>
9
+ <Type>IMAP</Type>
10
+ <Server>{{ connection_settings.imap.HOSTNAME }}</Server>
11
+ <Port>{{ connection_settings.imap.PORT }}</Port>
12
+ <SSL>true</SSL>
13
+ <LoginName>{{ emailaddress }}</LoginName>
14
+ </Protocol>
15
+
16
+ <Protocol>
17
+ <Type>SMTP</Type>
18
+ <Server>{{ connection_settings.smtp.HOSTNAME }}</Server>
19
+ <Port>{{ connection_settings.smtp.PORT }}</Port>
20
+ <SSL>false</SSL>
21
+ <TLS>true</TLS>
22
+ <LoginName>{{ emailaddress }}</LoginName>
23
+ </Protocol>
24
+
25
+ </Account>
26
+ </Response>
27
+ </Autodiscover>
@@ -0,0 +1,38 @@
1
+ from django.test import TestCase
2
+ from django.urls import reverse
3
+
4
+
5
+ class ViewsTestCase(TestCase):
6
+
7
+ databases = "__all__"
8
+
9
+ def test_autoconfig(self):
10
+ url = reverse("autoconfig:autoconfig")
11
+
12
+ resp = self.client.get(url)
13
+ self.assertEqual(resp.status_code, 404)
14
+
15
+ resp = self.client.get(f"{url}?emailaddress=test@test.com")
16
+ self.assertEqual(resp.status_code, 200)
17
+
18
+ self.assertIn(b'<emailProvider id="test.com">', resp.content)
19
+
20
+ def test_autodiscover(self):
21
+ url = reverse("autoconfig:autodiscover")
22
+
23
+ resp = self.client.post(url)
24
+ self.assertEqual(resp.status_code, 404)
25
+
26
+ resp = self.client.post(url, {"EmailAddress": "test@test.com"})
27
+ self.assertEqual(resp.status_code, 200)
28
+
29
+ self.assertIn(b"<LoginName>test@test.com", resp.content)
30
+
31
+ def test_mobileconfig(self):
32
+ url = reverse("autoconfig:mobileconfig")
33
+
34
+ resp = self.client.get(url)
35
+ self.assertEqual(resp.status_code, 404)
36
+
37
+ resp = self.client.get(f"{url}?emailaddress=test@test.com")
38
+ self.assertEqual(resp.status_code, 200)
@@ -0,0 +1,21 @@
1
+ """Autoconfig urls."""
2
+
3
+ from django.urls import path
4
+
5
+ from modoboa.autoconfig import views
6
+
7
+ app_name = "autoconfig"
8
+
9
+ urlpatterns = [
10
+ path(
11
+ "mail/config-v1.1.xml",
12
+ views.AutoConfigView.as_view(),
13
+ name="autoconfig",
14
+ ),
15
+ path(
16
+ "autodiscover/autodiscover.xml",
17
+ views.AutoDiscoverView.as_view(),
18
+ name="autodiscover",
19
+ ),
20
+ path("mobileconfig", views.MobileConfigView.as_view(), name="mobileconfig"),
21
+ ]
@@ -0,0 +1,109 @@
1
+ import plistlib
2
+ import uuid
3
+
4
+ from django.conf import settings
5
+ from django.http import Http404, HttpResponse
6
+ from django.utils.decorators import method_decorator
7
+ from django.views import generic
8
+ from django.views.decorators.csrf import csrf_exempt
9
+
10
+ from modoboa.lib.email_utils import split_address
11
+
12
+
13
+ class ConfigBaseMixin:
14
+
15
+ content_type = "application/xml"
16
+
17
+ def get_common_context(self, emailaddress: str) -> dict:
18
+ local_part, domain = split_address(emailaddress)
19
+ return {
20
+ "emailaddress": emailaddress,
21
+ "domain": domain,
22
+ "connection_settings": settings.EMAIL_CLIENT_CONNECTION_SETTINGS,
23
+ }
24
+
25
+
26
+ class AutoConfigView(ConfigBaseMixin, generic.TemplateView):
27
+
28
+ http_method_names = ["get"]
29
+ template_name = "autoconfig/autoconfig.xml"
30
+
31
+ def get_context_data(self, **kwargs):
32
+ emailaddress = self.request.GET.get("emailaddress")
33
+ if not emailaddress:
34
+ raise Http404
35
+ context = super().get_context_data(**kwargs)
36
+ context.update(self.get_common_context(emailaddress))
37
+ return context
38
+
39
+
40
+ @method_decorator(csrf_exempt, name="dispatch")
41
+ class AutoDiscoverView(ConfigBaseMixin, generic.TemplateView):
42
+
43
+ http_method_names = ["post"]
44
+ template_name = "autoconfig/autodiscover.xml"
45
+
46
+ def get_context_data(self, **kwargs):
47
+ emailaddress = self.request.POST.get("EmailAddress")
48
+ if not emailaddress:
49
+ raise Http404
50
+ context = super().get_context_data(**kwargs)
51
+ context.update(self.get_common_context(emailaddress))
52
+ return context
53
+
54
+ def post(self, request, *args, **kwargs):
55
+ context = self.get_context_data(**kwargs)
56
+ return self.render_to_response(context)
57
+
58
+
59
+ class MobileConfigView(generic.View):
60
+
61
+ http_method_names = ["get"]
62
+
63
+ def get(self, request, *args, **kwargs):
64
+ emailaddress = self.request.GET.get("emailaddress")
65
+ if not emailaddress:
66
+ raise Http404
67
+ local_part, domain = split_address(emailaddress)
68
+ parts = domain.split(".")
69
+ parts.reverse()
70
+ reverse_domain = ".".join(parts)
71
+ imap_settings = settings.EMAIL_CLIENT_CONNECTION_SETTINGS["imap"]
72
+ smtp_settings = settings.EMAIL_CLIENT_CONNECTION_SETTINGS["smtp"]
73
+ profile = {
74
+ "PayloadType": "Configuration",
75
+ "PayloadVersion": 1,
76
+ "PayloadIdentifier": f"{reverse_domain}.mailprofile",
77
+ "PayloadUUID": str(uuid.uuid4()),
78
+ "PayloadDisplayName": f"{domain} Mail Configuration",
79
+ "PayloadOrganization": domain,
80
+ "PayloadContent": [
81
+ {
82
+ "PayloadType": "com.apple.mail.managed",
83
+ "PayloadVersion": 1,
84
+ "PayloadIdentifier": f"{reverse_domain}.mailprofile.mail",
85
+ "PayloadUUID": str(uuid.uuid4()),
86
+ "PayloadDisplayName": "Mail",
87
+ "EmailAccountDescription": f"{domain} Mail",
88
+ "EmailAccountType": "EmailTypeIMAP",
89
+ "EmailAddress": emailaddress,
90
+ # incoming
91
+ "IncomingMailServerHostName": imap_settings["HOSTNAME"],
92
+ "IncomingMailServerPortNumber": imap_settings["PORT"],
93
+ "IncomingMailServerUseSSL": True,
94
+ "IncomingMailServerUsername": emailaddress,
95
+ # outgoing
96
+ "OutgoingMailServerHostName": smtp_settings["HOSTNAME"],
97
+ "OutgoingMailServerPortNumber": smtp_settings["PORT"],
98
+ "OutgoingMailServerUseSSL": True,
99
+ "OutgoingMailServerUsername": emailaddress,
100
+ "OutgoingPasswordSameAsIncomingPassword": True,
101
+ }
102
+ ],
103
+ }
104
+ plist_bytes = plistlib.dumps(profile, fmt=plistlib.FMT_XML)
105
+ resp = HttpResponse(
106
+ plist_bytes, content_type="application/x-apple-aspen-config"
107
+ )
108
+ resp["Content-Disposition"] = 'attachment; filename="mail.mobileconfig"'
109
+ return resp
@@ -62,7 +62,7 @@ class ARMessageViewSetTestCase(PatcherMixin, ModoAPITestCase):
62
62
  def test_retrieve_armessage_domadmin(self):
63
63
  admin = User.objects.get(username="admin@test.com")
64
64
  self.client.logout()
65
- self.client.force_login(admin)
65
+ self.client.force_authenticate(admin)
66
66
  url = reverse("api:armessage-list")
67
67
  response = self.client.get(url)
68
68
  self.assertEqual(response.status_code, 200)
@@ -74,7 +74,7 @@ class ARMessageViewSetTestCase(PatcherMixin, ModoAPITestCase):
74
74
 
75
75
  def test_retrieve_armessage_simpleuser(self):
76
76
  self.client.logout()
77
- self.client.force_login(self.account)
77
+ self.client.force_authenticate(self.account)
78
78
  url = reverse("api:armessage-list")
79
79
  response = self.client.get(url)
80
80
  self.assertEqual(response.status_code, 200)
@@ -156,7 +156,7 @@ class AccountARMessageViewSetTestCase(PatcherMixin, ModoAPITestCase):
156
156
  response = self.client.get(url)
157
157
  self.assertEqual(response.status_code, 403)
158
158
  self.client.logout()
159
- self.client.force_login(self.account)
159
+ self.client.force_authenticate(self.account)
160
160
  self.assertFalse(
161
161
  models.ARmessage.objects.filter(mbox=self.account.mailbox).exists()
162
162
  )
@@ -168,7 +168,7 @@ class AccountARMessageViewSetTestCase(PatcherMixin, ModoAPITestCase):
168
168
 
169
169
  def test_set_armessage(self):
170
170
  self.client.logout()
171
- self.client.force_login(self.account)
171
+ self.client.force_authenticate(self.account)
172
172
  url = reverse("api:account_armessage-armessage")
173
173
  fromdate = timezone.now()
174
174
  todate = fromdate + relativedelta(days=4)
@@ -13,7 +13,7 @@ from modoboa.admin import factories as admin_factories
13
13
  from modoboa.admin import models as admin_models
14
14
  from modoboa.core import factories as core_factories
15
15
  from modoboa.core import models as core_models
16
- from modoboa.lib.tests import ModoTestCase, ModoAPITestCase
16
+ from modoboa.lib.tests import ModoAPITestCase
17
17
 
18
18
  from modoboa.admin.factories import populate_database
19
19
 
@@ -53,7 +53,7 @@ class TestDataMixin:
53
53
  cls.scalendar2 = factories.SharedCalendarFactory(domain=cls.domain2)
54
54
 
55
55
 
56
- class AccessRuleTestCase(ModoTestCase):
56
+ class AccessRuleTestCase(ModoAPITestCase):
57
57
 
58
58
  @classmethod
59
59
  def setUpTestData(cls):
@@ -139,7 +139,7 @@ class UserCalendarViewSetTestCase(TestDataMixin, ModoAPITestCase):
139
139
 
140
140
  def setUp(self):
141
141
  """Initiate test context."""
142
- self.client.force_login(self.account)
142
+ self.client.force_authenticate(self.account)
143
143
  self.set_global_parameter("server_location", "http://localhost:5232")
144
144
 
145
145
  def test_get_calendars(self):
@@ -214,7 +214,7 @@ class SharedCalendarViewSetTestCase(TestDataMixin, ModoAPITestCase):
214
214
 
215
215
  def setUp(self):
216
216
  """Initiate test context."""
217
- self.client.force_login(self.admin_account)
217
+ self.client.force_authenticate(self.admin_account)
218
218
  self.set_global_parameter("server_location", "http://localhost:5232")
219
219
 
220
220
  def test_get_calendars(self):
@@ -304,7 +304,7 @@ class AccessRuleViewSetTestCase(TestDataMixin, ModoAPITestCase):
304
304
 
305
305
  def setUp(self):
306
306
  """Initiate test context."""
307
- self.client.force_login(self.account)
307
+ self.client.force_authenticate(self.account)
308
308
 
309
309
  def test_get_accessrules(self):
310
310
  """Test access rule retrieval."""
@@ -383,7 +383,7 @@ class EventViewSetTestCase(TestDataMixin, ModoAPITestCase):
383
383
  self.cal_mock.return_value = mocks.Calendar(client=self.client_mock)
384
384
  self.addCleanup(patcher2.stop)
385
385
 
386
- self.client.force_login(self.account)
386
+ self.client.force_authenticate(self.account)
387
387
  self.set_global_parameter("server_location", "http://localhost")
388
388
 
389
389
  def test_get_user_events(self):
@@ -550,7 +550,7 @@ class AttendeeViewSetTestCase(ModoAPITestCase):
550
550
 
551
551
  def setUp(self):
552
552
  """Initiate test context."""
553
- self.client.force_login(self.account)
553
+ self.client.force_authenticate(self.account)
554
554
 
555
555
  def test_get_attendees(self):
556
556
  """Test attendees retrieval."""
@@ -571,7 +571,7 @@ class MailboxViewSetTestCase(ModoAPITestCase):
571
571
 
572
572
  def setUp(self):
573
573
  """Initiate test context."""
574
- self.client.force_login(self.account)
574
+ self.client.force_authenticate(self.account)
575
575
 
576
576
  def test_get_mailboxes(self):
577
577
  """Test mailbox retrieval."""
modoboa/contacts/tests.py CHANGED
@@ -11,7 +11,7 @@ from django.utils import timezone
11
11
 
12
12
  from modoboa.admin import factories as admin_factories
13
13
  from modoboa.core import models as core_models
14
- from modoboa.lib.tests import ModoAPITestCase, ModoTestCase
14
+ from modoboa.lib.tests import ModoAPITestCase
15
15
 
16
16
  from . import factories
17
17
  from . import mocks
@@ -46,7 +46,7 @@ class TestDataMixin:
46
46
 
47
47
  def setUp(self, *args, **kwargs):
48
48
  """Initiate test context."""
49
- self.client.force_login(self.user)
49
+ self.client.force_authenticate(self.user)
50
50
  self.set_global_parameter(
51
51
  "server_location", "http://example.test/radicale/", app="calendars"
52
52
  )
@@ -72,8 +72,7 @@ class ViewsTestCase(TestDataMixin, ModoAPITestCase):
72
72
  def test_user_settings(self):
73
73
  """Check that remote collection creation request is sent."""
74
74
  # 1. Addressbook with contacts must be synced manually
75
- data = {"username": self.user.username, "password": "toto"}
76
- self.client.post(reverse("core:login"), data)
75
+ self.client.force_authenticate(self.user)
77
76
  self.enable_cdav_sync()
78
77
  self.addressbook.refresh_from_db()
79
78
  self.assertIs(self.addressbook.last_sync, None)
@@ -81,8 +80,7 @@ class ViewsTestCase(TestDataMixin, ModoAPITestCase):
81
80
  # 2. Addressbook with no contacts can be considered synced
82
81
  user = core_models.User.objects.get(username="user@test2.com")
83
82
  abook = user.addressbook_set.first()
84
- data = {"username": user.username, "password": "toto"}
85
- self.client.post(reverse("core:login"), data)
83
+ self.client.force_authenticate(user)
86
84
  abook.refresh_from_db()
87
85
  self.assertIs(abook.last_sync, None)
88
86
  # Now enable sync.
@@ -312,7 +310,7 @@ class EmailAddressViewSetTestCase(TestDataMixin, ModoAPITestCase):
312
310
  self.assertEqual(len(response.data), 3)
313
311
 
314
312
 
315
- class ImportTestCase(TestDataMixin, ModoTestCase):
313
+ class ImportTestCase(TestDataMixin, ModoAPITestCase):
316
314
 
317
315
  def setUp(self):
318
316
  super().setUp()
@@ -1,9 +1,9 @@
1
1
  from django.urls import reverse_lazy
2
2
 
3
- from modoboa.lib.tests import ModoTestCase
3
+ from modoboa.lib.tests import ModoAPITestCase
4
4
 
5
5
 
6
- class OpenAPITestCase(ModoTestCase):
6
+ class OpenAPITestCase(ModoAPITestCase):
7
7
  openapi_schema_url = reverse_lazy("schema-v1-legacy")
8
8
 
9
9
  def test_unauthorized(self):
@@ -38,6 +38,19 @@ SITE_ID = 1
38
38
  # The email address that error messages come from, such as those sent to ADMINS
39
39
  #SERVER_EMAIL = 'webmaster@example.net'
40
40
 
41
+ EMAIL_CLIENT_CONNECTION_SETTINGS = {
42
+ 'imap': {
43
+ 'HOSTNAME': '{{ allowed_host }}',
44
+ 'SOCKET_TYPE': 'SSL',
45
+ 'PORT': 993,
46
+ },
47
+ 'smtp': {
48
+ 'HOSTNAME': '{{ allowed_host }}',
49
+ 'SOCKET_TYPE': 'STARTTLS',
50
+ 'PORT': 587
51
+ }
52
+ }
53
+
41
54
  # Security settings
42
55
 
43
56
  X_FRAME_OPTIONS = "SAMEORIGIN"
@@ -75,6 +88,7 @@ MODOBOA_APPS = (
75
88
  'modoboa.core',
76
89
  'modoboa.lib',
77
90
  'modoboa.admin',
91
+ 'modoboa.autoconfig',
78
92
  'modoboa.transport',
79
93
  'modoboa.relaydomains',
80
94
  'modoboa.limits',
@@ -221,7 +235,6 @@ REST_FRAMEWORK = {
221
235
  'DEFAULT_AUTHENTICATION_CLASSES': (
222
236
  'oauth2_provider.contrib.rest_framework.OAuth2Authentication',
223
237
  'rest_framework.authentication.TokenAuthentication',
224
- 'rest_framework.authentication.SessionAuthentication',
225
238
  ),
226
239
  'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema',
227
240
  'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.NamespaceVersioning',
modoboa/core/models.py CHANGED
@@ -220,7 +220,7 @@ class User(AbstractUser):
220
220
  return self.email
221
221
 
222
222
  @property
223
- def tfa_enabled(self):
223
+ def tfa_enabled(self) -> bool:
224
224
  return self.totp_enabled or self.webauthn_enabled
225
225
 
226
226
  def is_owner(self, obj):
@@ -158,6 +158,8 @@ class ProfileTestCase(LDAPTestCaseMixin, ModoAPITestCase):
158
158
 
159
159
  username = "testuser@example.com"
160
160
  self.authenticate(username, "test")
161
+ user = models.User.objects.get(username=username)
162
+ self.client.force_authenticate(user)
161
163
  response = self.client.post(
162
164
  reverse("v2:account-set-me-password"),
163
165
  {"password": "test", "new_password": "Toto1234"},
@@ -37,6 +37,23 @@ from .. import signals
37
37
  logger = logging.getLogger("modoboa.auth")
38
38
 
39
39
 
40
+ class ModoboaThemeMixin:
41
+
42
+ def dispatch(self, request, *args, **kwargs):
43
+ self.parameters = dict(param_tools.get_global_parameters(app="core"))
44
+ return super().dispatch(request, *args, **kwargs)
45
+
46
+ def get_context_data(self, **kwargs):
47
+ context = super().get_context_data(**kwargs)
48
+ context.update(
49
+ {
50
+ "theme_primary_color": self.parameters["theme_primary_color"],
51
+ "theme_primary_color_dark": self.parameters["theme_primary_color_dark"],
52
+ }
53
+ )
54
+ return context
55
+
56
+
40
57
  class LoginViewMixin:
41
58
  @cached_property
42
59
  def logger(self):
@@ -74,7 +91,7 @@ class LoginViewMixin:
74
91
  return response
75
92
 
76
93
 
77
- class LoginView(LoginViewMixin, auth_views.LoginView):
94
+ class LoginView(ModoboaThemeMixin, LoginViewMixin, auth_views.LoginView):
78
95
  """Login view with 2FA support."""
79
96
 
80
97
  form_class = forms.AuthenticationForm
@@ -86,13 +103,15 @@ class LoginView(LoginViewMixin, auth_views.LoginView):
86
103
  sender="login", location="loginpage"
87
104
  )
88
105
  announcements = [announcement[1] for announcement in announcements]
89
- context.update({"announcements": announcements})
106
+ context.update(
107
+ {
108
+ "announcements": announcements,
109
+ }
110
+ )
90
111
  return context
91
112
 
92
113
  def check_password_hash(self, user, form):
93
- condition = user.is_local and param_tools.get_global_parameter(
94
- "update_scheme", raise_exception=False
95
- )
114
+ condition = user.is_local and self.parameters.get("update_scheme")
96
115
  if not condition:
97
116
  return
98
117
  # check if password scheme is correct
@@ -157,7 +176,7 @@ def dologout(request):
157
176
  return HttpResponseRedirect(reverse("core:login"))
158
177
 
159
178
 
160
- class PasswordResetView(auth_views.PasswordResetView):
179
+ class PasswordResetView(ModoboaThemeMixin, auth_views.PasswordResetView):
161
180
  """Custom view to override form."""
162
181
 
163
182
  form_class = forms.PasswordResetForm
@@ -202,7 +221,7 @@ class PasswordResetView(auth_views.PasswordResetView):
202
221
  return HttpResponseRedirect(reverse("password_reset_confirm_code"))
203
222
 
204
223
 
205
- class VerifySMSCodeView(generic.FormView):
224
+ class VerifySMSCodeView(ModoboaThemeMixin, generic.FormView):
206
225
  """View to verify a code received by SMS."""
207
226
 
208
227
  form_class = forms.VerifySMSCodeForm
@@ -254,7 +273,7 @@ class ResendSMSCodeView(generic.View):
254
273
  return JsonResponse({"status": "ok"})
255
274
 
256
275
 
257
- class TwoFactorCodeVerifyView(LoginViewMixin, generic.FormView):
276
+ class TwoFactorCodeVerifyView(ModoboaThemeMixin, LoginViewMixin, generic.FormView):
258
277
  """View to verify a 2FA code after login."""
259
278
 
260
279
  form_class = forms.Verify2FACodeForm
@@ -1 +1 @@
1
- import{C as q}from"./ChoiceField-DnwXRkht.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-LhNzkzAh.js";import{V as H,a as L}from"./VRow-DJ0NB63-.js";import{V as B}from"./VAlert-4r6LxKtg.js";import{c as P}from"./VAvatar-DbuoZWmf.js";import{V as k,r as F}from"./VForm-DsRLc-sa.js";import{_ as z}from"./AccountPasswordSubForm-YsaE_cDx.js";import{E as I}from"./EmailField-DeqDPm5j.js";import{V as E}from"./VTextField-DKbr4H5w.js";import{V as $}from"./VSwitch-DPnjPQuU.js";import{u as K}from"./domains.store-BLKRipG8.js";import{a as Y}from"./accounts-CC2F0a0c.js";import{V as J}from"./VChip-CaQvfmkw.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};
1
+ import{C as q}from"./ChoiceField-17aCFpF6.js";import{u as M,b as S,h as V,i as f,j 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-YIGRKgNA.js";import{V as H,a as L}from"./VRow-CAbOSEHS.js";import{V as B}from"./VAlert-x6n3drjw.js";import{c as P}from"./VAvatar-J563boKh.js";import{V as k,r as F}from"./VForm-BwTUBf4u.js";import{_ as z}from"./AccountPasswordSubForm-B9X5wAXp.js";import{E as I}from"./EmailField-DpBYkqam.js";import{V as E}from"./VTextField-C9VUP7V9.js";import{V as $}from"./VSwitch-CixaedmQ.js";import{u as K}from"./domains.store-CfpnnBGo.js";import{a as Y}from"./accounts-BT429O7P.js";import{V as J}from"./VChip-DvxO74Mw.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};