diff --git a/frontend/src/layouts/dashboard/NavBar.vue b/frontend/src/layouts/dashboard/NavBar.vue index be6152fcc..f7de8d426 100644 --- a/frontend/src/layouts/dashboard/NavBar.vue +++ b/frontend/src/layouts/dashboard/NavBar.vue @@ -111,6 +111,7 @@ import { useRoute, useRouter } from 'vue-router' import { ref, computed } from 'vue' import { useGettext } from 'vue3-gettext' +import { getActivePinia } from "pinia" import parametersApi from '@/api/parameters' import { useAuthStore, useParametersStore } from '@/stores' @@ -128,209 +129,208 @@ const drawer = ref(true) const authUser = computed(() => authStore.authUser) const isAuthenticated = computed(() => authStore.isAuthenticated) const backgroundColor = computed(() => - route.meta.layout === 'account' ? 'bg-grey-darken-1' : 'bg-primary-darken-1' + route.meta.layout === 'account' ? 'bg-grey-darken-1' : 'bg-primary-darken-1' ) const userInitials = computed(() => { - let initials = null - if (authUser.value.first_name) { - initials = authUser.value.first_name[0] - } - if (authUser.value.last_name) { - initials = initials - ? initials + authUser.value.last_name[0] - : authUser.value.last_name[0] - } - if (!initials) { - initials = authUser.value.username[0] - } - return initials + let initials = null + if (authUser.value.first_name) { + initials = authUser.value.first_name[0] + } + if (authUser.value.last_name) { + initials = initials + ? initials + authUser.value.last_name[0] + : authUser.value.last_name[0] + } + if (!initials) { + initials = authUser.value.username[0] + } + return initials }) const displayName = computed(() => { - return authUser.value.first_name || authUser.value.last_name - ? `${authUser.value.first_name} ${authUser.value.last_name}` - : authUser.value.username + return authUser.value.first_name || authUser.value.last_name + ? `${authUser.value.first_name} ${authUser.value.last_name}` + : authUser.value.username }) const menuItems = computed(() => { - if (route.meta.layout === 'account') { - return userSettingsMenuItems - } - return mainMenuItems + if (route.meta.layout === 'account') { + return userSettingsMenuItems + } + return mainMenuItems }) const mainColor = computed(() => { - if (route.meta.layout === 'account') { - return 'grey' - } - return 'primary' + if (route.meta.layout === 'account') { + return 'grey' + } + return 'primary' }) // created parametersApi.getApplications().then((response) => { - response.data.forEach((item) => { - mainMenuItems[6].children.push({ - text: item.label, - to: { name: 'ParametersEdit', params: { app: item.name } }, + response.data.forEach((item) => { + mainMenuItems[6].children.push({ + text: item.label, + to: { name: 'ParametersEdit', params: { app: item.name } }, + }) }) - }) }) const imapMigration = computed(() => parametersStore.imapMigrationEnabled) const mainMenuItems = [ - { - text: $gettext('Dashboard'), - to: { name: 'Dashboard' }, - icon: 'mdi-view-dashboard-outline', - exact: true, - }, - { - text: $gettext('Domains'), - to: { name: 'DomainList' }, - icon: 'mdi-domain', - roles: ['DomainAdmins', 'Resellers', 'SuperAdmins'], - }, - { - text: $gettext('Identities'), - to: { name: 'Identities' }, - icon: 'mdi-account', - roles: ['DomainAdmins', 'Resellers', 'SuperAdmins'], - }, - { - text: $gettext('Alarms'), - to: { name: 'Alarms' }, - icon: 'mdi-bell', - }, - { - icon: 'mdi-history', - text: $gettext('Monitoring'), - roles: ['SuperAdmins', 'Resellers', 'DomainAdmins'], - children: [ - { - text: $gettext('Statistics'), - to: { name: 'Statistics' }, - roles: ['SuperAdmins'], - }, - { - text: $gettext('Audit trail'), - to: { name: 'AuditTrail' }, - roles: ['SuperAdmins'], - }, - { - text: $gettext('Messages'), - to: { name: 'MessageLog' }, + { + text: $gettext('Dashboard'), + to: { name: 'Dashboard' }, + icon: 'mdi-view-dashboard-outline', + exact: true, + }, + { + text: $gettext('Domains'), + to: { name: 'DomainList' }, + icon: 'mdi-domain', roles: ['DomainAdmins', 'Resellers', 'SuperAdmins'], - }, - ], - }, - { - icon: 'mdi-email-sync-outline', - text: $gettext('IMAP Migration'), - roles: ['SuperAdmins', 'Resellers'], - children: [ - { - text: $gettext('Email providers'), - to: { name: 'ProvidersList' }, + }, + { + text: $gettext('Identities'), + to: { name: 'Identities' }, + icon: 'mdi-account', + roles: ['DomainAdmins', 'Resellers', 'SuperAdmins'], + }, + { + text: $gettext('Alarms'), + to: { name: 'Alarms' }, + icon: 'mdi-bell', + }, + { + icon: 'mdi-history', + text: $gettext('Monitoring'), + roles: ['SuperAdmins', 'Resellers', 'DomainAdmins'], + children: [ + { + text: $gettext('Statistics'), + to: { name: 'Statistics' }, + roles: ['SuperAdmins'], + }, + { + text: $gettext('Audit trail'), + to: { name: 'AuditTrail' }, + roles: ['SuperAdmins'], + }, + { + text: $gettext('Messages'), + to: { name: 'MessageLog' }, + roles: ['DomainAdmins', 'Resellers', 'SuperAdmins'], + }, + ], + }, + { + icon: 'mdi-email-sync-outline', + text: $gettext('IMAP Migration'), roles: ['SuperAdmins', 'Resellers'], - }, - { - text: $gettext('Migrations'), - to: { name: 'MigrationsList' }, - roles: ['Resellers', 'SuperAdmins'], - }, - ], - }, - { - icon: 'mdi-cog', - text: $gettext('Settings'), - children: [], - roles: ['SuperAdmins'], - }, - { - icon: 'mdi-information', - text: $gettext('Information'), - roles: ['SuperAdmins'], - to: { name: 'Information' }, - }, + children: [ + { + text: $gettext('Email providers'), + to: { name: 'ProvidersList' }, + roles: ['SuperAdmins', 'Resellers'], + }, + { + text: $gettext('Migrations'), + to: { name: 'MigrationsList' }, + roles: ['Resellers', 'SuperAdmins'], + }, + ], + }, + { + icon: 'mdi-cog', + text: $gettext('Settings'), + children: [], + roles: ['SuperAdmins'], + }, + { + icon: 'mdi-information', + text: $gettext('Information'), + roles: ['SuperAdmins'], + to: { name: 'Information' }, + }, ] const userSettingsMenuItems = [ - { - text: $gettext('API'), - roles: ['SuperAdmins'], - to: { name: 'APISetup' }, - icon: 'mdi-api', - exact: true, - }, - { - text: $gettext('Profile'), - to: { name: 'UserProfile' }, - icon: 'mdi-account-circle-outline', - exact: true, - }, - { - text: $gettext('Security'), - to: { name: 'UserSecurity' }, - icon: 'mdi-lock-outline', - exact: true, - }, - // { - // text: $gettext('Preferences'), - // icon: 'mdi-tune', - // exact: true - // }, - { - text: $gettext('Forward'), - condition: () => authUser.value.mailbox !== null, - to: { name: 'UserForward' }, - icon: 'mdi-forward', - exact: true, - }, + { + text: $gettext('API'), + roles: ['SuperAdmins'], + to: { name: 'APISetup' }, + icon: 'mdi-api', + exact: true, + }, + { + text: $gettext('Profile'), + to: { name: 'UserProfile' }, + icon: 'mdi-account-circle-outline', + exact: true, + }, + { + text: $gettext('Security'), + to: { name: 'UserSecurity' }, + icon: 'mdi-lock-outline', + exact: true, + }, + // { + // text: $gettext('Preferences'), + // icon: 'mdi-tune', + // exact: true + // }, + { + text: $gettext('Forward'), + condition: () => authUser.value.mailbox !== null, + to: { name: 'UserForward' }, + icon: 'mdi-forward', + exact: true, + }, ] const userMenuItems = [ - { - text: $gettext('Profile'), - icon: 'mdi-account-circle-outline', - to: { name: 'UserProfile' }, - click: () => null, - }, - { - text: $gettext('Logout'), - icon: 'mdi-logout', - click: logout, - }, + { + text: $gettext('Profile'), + icon: 'mdi-account-circle-outline', + to: { name: 'UserProfile' }, + click: () => null, + }, + { + text: $gettext('Logout'), + icon: 'mdi-logout', + click: logout, + }, ] function displayMenuItem(item) { - if (isAuthenticated.value) { - const condition = - (item.roles === undefined || - item.roles.indexOf(authUser.value.role) !== -1) && - (item.condition === undefined || item.condition()) && - item.activated !== false - if (item.icon === 'mdi-email-sync-outline') { - // For imapMigration - return condition && imapMigration.value + if (isAuthenticated.value) { + const condition = + (item.roles === undefined || + item.roles.indexOf(authUser.value.role) !== -1) && + (item.condition === undefined || item.condition()) && + item.activated !== false + if (item.icon === 'mdi-email-sync-outline') { + // For imapMigration + return condition && imapMigration.value + } + return condition } - return condition - } } -function logout() { - authStore.$reset().then(() => { +async function logout() { + getActivePinia()._s.forEach(async (store) => await store.$reset()); router.push({ name: 'Login' }) - }) } onMounted(() => { - if (parametersStore.imapMigrationEnabled === null) { - parametersApi.getApplication('imap_migration').then((response) => { - parametersStore.imapMigrationEnabled = - response.data.params.enabled_imapmigration - }) - } + if (parametersStore.imapMigrationEnabled === null) { + parametersApi.getApplication('imap_migration').then((response) => { + parametersStore.imapMigrationEnabled = + response.data.params.enabled_imapmigration + }) + } }) diff --git a/frontend/src/stores/accounts.store.js b/frontend/src/stores/accounts.store.js index 7af49f1fa..51bf86b70 100644 --- a/frontend/src/stores/accounts.store.js +++ b/frontend/src/stores/accounts.store.js @@ -14,6 +14,11 @@ export const useAccountsStore = defineStore('accounts', () => { const accounts = ref({}) const accountsLoaded = ref(false) + async function $reset() { + accounts.value = {} + accountsLoaded.value = false + } + async function getAll() { accounts.value = {} return accountsApi.getAll().then((response) => { @@ -73,5 +78,6 @@ export const useAccountsStore = defineStore('accounts', () => { updateAccount, createAccount, deleteAccount, + $reset } }) diff --git a/frontend/src/stores/aliases.store.js b/frontend/src/stores/aliases.store.js index 83ea1c8ab..91b559054 100644 --- a/frontend/src/stores/aliases.store.js +++ b/frontend/src/stores/aliases.store.js @@ -10,9 +10,13 @@ export const useAliasesStore = defineStore('aliases', () => { const { $gettext } = gettext const aliases = ref({}) - const aliasesLoaded = ref(false) + async function $reset() { + aliases.value = {} + aliasesLoaded.value = false + } + async function getAll() { aliasesLoaded.value = false return aliasesApi @@ -85,5 +89,6 @@ export const useAliasesStore = defineStore('aliases', () => { createAlias, deleteAlias, updateAlias, + $reset } }) diff --git a/frontend/src/stores/bus.store.js b/frontend/src/stores/bus.store.js index d1d88de7d..4e94d73d6 100644 --- a/frontend/src/stores/bus.store.js +++ b/frontend/src/stores/bus.store.js @@ -6,6 +6,11 @@ export const useBusStore = defineStore('bus', () => { const notification = ref({}) const notificationColor = ref('') + async function $reset() { + notification.value = {} + notificationColor.value = '' + } + function displayNotification(options) { notification.value = options.msg notificationColor.value = options.type ? options.type : 'success' @@ -14,5 +19,6 @@ export const useBusStore = defineStore('bus', () => { notification, notificationColor, displayNotification, + $reset } }) diff --git a/frontend/src/stores/domains.store.js b/frontend/src/stores/domains.store.js index 86288e165..e7cbf8a8e 100644 --- a/frontend/src/stores/domains.store.js +++ b/frontend/src/stores/domains.store.js @@ -12,6 +12,12 @@ export const useDomainsStore = defineStore('domains', () => { const domains = ref({}) const domainAliases = ref({}) + async function $reset() { + domainsLoaded.value = false + domains.value = {} + domainAliases.value = {} + } + async function getDomains() { domainsLoaded.value = false return domainApi.getDomains().then((response) => { @@ -144,5 +150,6 @@ export const useDomainsStore = defineStore('domains', () => { getAliases, deleteAlias, updateAlias, + $reset } }) diff --git a/frontend/src/stores/identities.store.js b/frontend/src/stores/identities.store.js index 0d98295fd..5dfaadbc6 100644 --- a/frontend/src/stores/identities.store.js +++ b/frontend/src/stores/identities.store.js @@ -17,6 +17,11 @@ export const useIdentitiesStore = defineStore('identities', () => { const identitiesLoaded = ref(false) const identities = ref([]) + async function $reset() { + identitiesLoaded.value = false + identities.value = [] + } + async function _getIndexByPk(type, pk) { for (let i = 0; i < identities.value.length; i++) { if (identities.value[i].pk === pk && identities.value[i].type === type) { @@ -168,5 +173,6 @@ export const useIdentitiesStore = defineStore('identities', () => { deleteIdentity, createIdentity, updateIdentity, + $reset } }) diff --git a/frontend/src/stores/parameters.store.js b/frontend/src/stores/parameters.store.js index 5961bad05..f07ebe412 100644 --- a/frontend/src/stores/parameters.store.js +++ b/frontend/src/stores/parameters.store.js @@ -4,5 +4,9 @@ import { ref } from 'vue' export const useParametersStore = defineStore('parameters', () => { const imapMigrationEnabled = ref(null) - return { imapMigrationEnabled } + async function $reset() { + imapMigrationEnabled.value = null + } + + return { imapMigrationEnabled, $reset } }) diff --git a/frontend/src/stores/providers.store.js b/frontend/src/stores/providers.store.js index 8c308effd..cbadcdeaf 100644 --- a/frontend/src/stores/providers.store.js +++ b/frontend/src/stores/providers.store.js @@ -11,6 +11,11 @@ export const useProvidersStore = defineStore('providers', () => { const providers = ref({}) const { $gettext } = gettext + async function $reset() { + providersLoaded.value = false + providers.value = {} + } + async function getProviders() { providers.value = {} providersLoaded.value = false @@ -54,5 +59,6 @@ export const useProvidersStore = defineStore('providers', () => { getProviders, createProvider, deleteProvider, + $reset } }) diff --git a/modoboa/core/models.py b/modoboa/core/models.py index 6aa8466f2..ad794881a 100644 --- a/modoboa/core/models.py +++ b/modoboa/core/models.py @@ -80,7 +80,7 @@ class User(AbstractUser): _parameters = models.JSONField(default=dict, encoder=DjangoJSONEncoder) - class Meta(object): + class Meta: ordering = ["username"] indexes = [models.Index(fields=["email", "is_active"])] @@ -88,7 +88,7 @@ class User(AbstractUser): def __init__(self, *args, **kwargs): """Load parameter manager.""" - super(User, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self.parameters = param_tools.Manager("user", self._parameters) def _crypt_password(self, raw_value): @@ -113,7 +113,7 @@ class User(AbstractUser): return get_password_hasher(scheme.upper())().encrypt(raw_value) def set_password(self, raw_value, curvalue=None): - """Password update + """Password update. Update the current mailbox's password with the given clear value. This value is encrypted according to the defined method @@ -185,7 +185,7 @@ class User(AbstractUser): @property def name_or_rcpt(self): if self.first_name != "": - return "%s %s" % (self.first_name, self.last_name) + return f"{self.first_name} {self.last_name}" return "----" @property @@ -201,7 +201,7 @@ class User(AbstractUser): return self.email def is_owner(self, obj): - """Tell is the user is the unique owner of this object + """Tell is the user is the unique owner of this object. :param obj: an object inheriting from ``models.Model`` :return: a boolean @@ -259,7 +259,7 @@ class User(AbstractUser): @role.setter def role(self, role): - """Set administrative role for this account + """Set administrative role for this account. :param string role: the role to set """ @@ -306,7 +306,7 @@ class User(AbstractUser): def save(self, *args, **kwargs): creator = kwargs.pop("creator", None) - super(User, self).save(*args, **kwargs) + super().save(*args, **kwargs) if creator is not None: self.post_create(creator) @@ -334,7 +334,7 @@ class User(AbstractUser): allowed_roles = [role[0] for role in allowed_roles] if desired_role not in allowed_roles: raise PermDeniedException( - _("You can't import an account with a role greater than " "yours") + _("You can't import an account with a role greater than yours") ) self.username = row[1].strip().lower() @@ -435,11 +435,11 @@ class ObjectAccess(models.Model): content_object = GenericForeignKey("content_type", "object_id") is_owner = models.BooleanField(default=False) - class Meta(object): + class Meta: unique_together = (("user", "content_type", "object_id"),) def __str__(self): - return "%s => %s (%s)" % (self.user, self.content_object, self.content_type) + return f"{self.user} => {self.content_object} ({self.content_type})" class Log(models.Model):