mirror of
https://github.com/jwinterm/modoboa.git
synced 2026-10-17 05:41:25 +02:00
Few fixes
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
IMAP Migration for Modoboa
|
||||
===========================
|
||||
|
||||
.. _imap:
|
||||
|
||||
A simple `Modoboa <http://modoboa.org/>`_ extension which provides a
|
||||
way to transfer your account from another provider/server
|
||||
provided that you have IMAP access to it.
|
||||
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
The plugin configuration is done from the admin panel (*Modoboa >
|
||||
Parameters > IMAP Migration*). And you have a specific tab to configure
|
||||
your migration that is visible for SuperAdmins.
|
||||
These settings are only available on the new admin interface.
|
||||
|
||||
.. warning::
|
||||
|
||||
You must have enabled "Auto create domain" in modoboa parameters for this extension to work.
|
||||
|
||||
This extension can create `OfflineIMAP <https://www.offlineimap.org/doc/installation.html>`_
|
||||
configuration file with the command:
|
||||
|
||||
.. sourcecode:: bash
|
||||
|
||||
> python manage.py generate_offlineimap_config
|
||||
|
||||
You then need to setup `OfflineIMAP <https://www.offlineimap.org/doc/quick_start.html>`_.
|
||||
..
|
||||
This will sync back-and-forth user mailbox.
|
||||
|
||||
|
||||
Providers
|
||||
---------
|
||||
|
||||
Mail server can host multiple domains so the provider address
|
||||
may be different from the domain part of your email address.
|
||||
|
||||
Associated domain is the domain part of the email address.
|
||||
You can leave "Local Domain" blank if you keep the same domain.
|
||||
This domain will be auto-created if needed.
|
||||
you must create the domain beforhand if you want to migrate an account
|
||||
from `user@domain1.tld` to `user@domain2.tld`.
|
||||
|
||||
|
||||
Migrations
|
||||
----------
|
||||
|
||||
Migrations are automatically handled when providers have been configured.
|
||||
Users use their old credential to log and the account is automatically created.
|
||||
They shall use the new domain on next logins if a local domain was set in the provider configuration.
|
||||
If you have installed OfflineIMAP, you may delete the migration to stop mailbox sync.
|
||||
SuperAdmins may check migrations in the `IMAP Migration` tab.
|
||||
+4
-4
@@ -34,6 +34,10 @@ It provides built-in features by default :
|
||||
| |containing account | |
|
||||
| |credentials | |
|
||||
+---------------------------------------------------------+-------------------------+------------------------------------------------------+
|
||||
|modoboa-imap-migration |Transfer accounts from |:ref:`modoboa_imap-migration Documentation <imap>` |
|
||||
| |other mail server | |
|
||||
| |automatically | |
|
||||
+---------------------------------------------------------+-------------------------+------------------------------------------------------+
|
||||
|
||||
|
||||
And it is extensible by nature and comes with a lot of additional extensions:
|
||||
@@ -49,10 +53,6 @@ And it is extensible by nature and comes with a lot of additional extensions:
|
||||
| | | |
|
||||
| | | |
|
||||
+---------------------------------------------------------+-------------------------+------------------------------------------------------+
|
||||
|`modoboa-imap-migration |Migrate mailboxes from an|https://github.com/modoboa/modoboa-imap-migration |
|
||||
|<https://github.com/modoboa/modoboa-imap-migration>`_ |existing server using | |
|
||||
| |IMAP (and offlineimap) | |
|
||||
+---------------------------------------------------------+-------------------------+------------------------------------------------------+
|
||||
|`modoboa-pfxadmin-migrate |A tool to migrate from |https://github.com/modoboa/modoboa-pfxadmin-migrate |
|
||||
|<https://github.com/modoboa/modoboa-pfxadmin-migrate>`_ |Postfixadmin | |
|
||||
+---------------------------------------------------------+-------------------------+------------------------------------------------------+
|
||||
|
||||
@@ -1,174 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<v-text-field
|
||||
ref="input"
|
||||
v-model="input"
|
||||
outlined
|
||||
dense
|
||||
v-bind="$attrs"
|
||||
v-on="$listeners"
|
||||
@input="update"
|
||||
@keydown="onKeyDown"
|
||||
/>
|
||||
<div class="menu"
|
||||
v-if="showMenu"
|
||||
:style="`width: ${width}px;`"
|
||||
>
|
||||
<v-list class="list">
|
||||
<v-list-item
|
||||
v-for="(domain, index) in filteredDomains"
|
||||
:key="index"
|
||||
@click="selectDomain(domain)"
|
||||
:class="selectionIndex === index ? 'selected' : ''"
|
||||
>
|
||||
<v-list-item-title>{{ domain.name }}</v-list-item-title>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
value: String,
|
||||
allowAdd: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
domains: 'identities/domains'
|
||||
}),
|
||||
filteredDomains () {
|
||||
return this.domains.filter(domain => domain.name.startsWith(this.input))
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
input: this.value,
|
||||
selectionIndex: 0,
|
||||
showMenu: false,
|
||||
width: 500
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onKeyDown (e) {
|
||||
const keyCode = e.keyCode
|
||||
|
||||
if (keyCode === 40 || keyCode === 34) {
|
||||
// on arrow down or page down
|
||||
if (!this.showMenu) {
|
||||
this.showMenu = true
|
||||
} else {
|
||||
this.increaseSelectionIndex()
|
||||
}
|
||||
e.preventDefault()
|
||||
} else if (keyCode === 38 || keyCode === 33) {
|
||||
// on arrow up or page up
|
||||
if (!this.showMenu) {
|
||||
this.showMenu = true
|
||||
} else {
|
||||
this.decreaseSelectionIndex()
|
||||
}
|
||||
e.preventDefault()
|
||||
} else if (keyCode === 13 || keyCode === 9) {
|
||||
// on enter or tab
|
||||
if (this.filteredDomains.length > 0) {
|
||||
this.selectDomain(this.filteredDomains[this.selectionIndex])
|
||||
} else if (this.allowAdd) {
|
||||
this.selectDomain()
|
||||
}
|
||||
e.preventDefault()
|
||||
} else if (keyCode === 27) {
|
||||
// on escape
|
||||
if (this.filteredDomains.length > 0) {
|
||||
this.showMenu = false
|
||||
e.preventDefault()
|
||||
}
|
||||
}
|
||||
},
|
||||
increaseSelectionIndex () {
|
||||
if (this.selectionIndex >= this.filteredDomains.length - 1) {
|
||||
this.selectionIndex = 0
|
||||
} else {
|
||||
this.selectionIndex += 1
|
||||
}
|
||||
},
|
||||
decreaseSelectionIndex () {
|
||||
if (this.selectionIndex <= 0) {
|
||||
this.selectionIndex = this.filteredDomains.length - 1
|
||||
} else {
|
||||
this.selectionIndex -= 1
|
||||
}
|
||||
},
|
||||
selectDomain (domain) {
|
||||
if (domain !== undefined) {
|
||||
this.input = domain.name
|
||||
}
|
||||
this.showMenu = false
|
||||
this.$emit('input', this.input)
|
||||
this.$emit('domain-selected')
|
||||
},
|
||||
update (value) {
|
||||
if (this.allowAdd) {
|
||||
this.showMenu = this.filteredDomains.length > 0
|
||||
} else {
|
||||
this.showMenu = true
|
||||
}
|
||||
this.$emit('input', this.input)
|
||||
},
|
||||
onResize () {
|
||||
this.updateMinWidthProperty()
|
||||
},
|
||||
updateMinWidthProperty () {
|
||||
this.width = this.$refs.input.$el.clientWidth
|
||||
},
|
||||
reset () {
|
||||
this.input = ''
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
// window.addEventListener('resize', this.onResize)
|
||||
},
|
||||
updated () {
|
||||
this.$nextTick(() => {
|
||||
this.updateMinWidthProperty()
|
||||
})
|
||||
},
|
||||
watch: {
|
||||
value (newValue) {
|
||||
this.input = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.menu {
|
||||
display: inline-block;
|
||||
position: fixed;
|
||||
margin-top: -20px;
|
||||
padding: 5px 0;
|
||||
box-shadow: 0 1px 5px 1px rgba(0,0,0,0.3);
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
contain: content;
|
||||
z-index: 99;
|
||||
}
|
||||
|
||||
.list {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
font-size: 1.1em;
|
||||
overflow-x: hidden;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.selected {
|
||||
background-color: cornsilk;
|
||||
}
|
||||
</style>
|
||||
@@ -31,6 +31,5 @@ class IMAPMigrationConfig(AppConfig):
|
||||
|
||||
def ready(self):
|
||||
from . import checks # noqa
|
||||
from . import handlers # noqa
|
||||
|
||||
load_imapmigration_settings()
|
||||
|
||||
@@ -93,6 +93,11 @@ class IMAPBackend:
|
||||
# No migration => either someone else account, or
|
||||
# migration is done
|
||||
return None
|
||||
if not user.check_password(password):
|
||||
# User may have changed its password on modoboa
|
||||
# Remote password and local now differs
|
||||
# We block the connection in that case
|
||||
return None
|
||||
return user
|
||||
|
||||
def get_user(self, user_pk):
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
"""IMAP migration handlers."""
|
||||
|
||||
from django.urls import reverse
|
||||
from django.dispatch import receiver
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
from modoboa.core import signals as core_signals
|
||||
|
||||
|
||||
@receiver(core_signals.extra_user_menu_entries)
|
||||
def menu(sender, location, user, **kwargs):
|
||||
"""Return extra menu entry."""
|
||||
if location != "top_menu" or not user.is_superuser:
|
||||
return []
|
||||
return [
|
||||
{"name": "imap_migrations",
|
||||
"label": _("IMAP migrations"),
|
||||
"url": reverse("imap_migration:index")}
|
||||
]
|
||||
Reference in New Issue
Block a user