mirror of
https://github.com/jwinterm/modoboa.git
synced 2026-10-17 05:41:25 +02:00
Merge branch '1.1.x'
Conflicts: modoboa/locale/cs_CZ/LC_MESSAGES/django.po modoboa/locale/de/LC_MESSAGES/django.po modoboa/locale/en/LC_MESSAGES/django.po modoboa/locale/es/LC_MESSAGES/django.po modoboa/locale/fr/LC_MESSAGES/django.po modoboa/locale/it/LC_MESSAGES/django.po modoboa/locale/nl_NL/LC_MESSAGES/django.po modoboa/locale/pt_PT/LC_MESSAGES/django.po modoboa/locale/ru/LC_MESSAGES/django.po modoboa/locale/sv/LC_MESSAGES/django.po
This commit is contained in:
@@ -6,7 +6,7 @@ Migrating from other software
|
||||
PostfixAdmin
|
||||
************
|
||||
|
||||
Since version 0.8.5, Modoboa provides a simple script to migrate an
|
||||
Modoboa provides a simple script to migrate an
|
||||
existing `PostfixAdmin (version 2.3.3+)
|
||||
<http://postfixadmin.sourceforge.net/>`_ database to a Modoboa one.
|
||||
|
||||
@@ -21,40 +21,37 @@ connection named ``pfxadmin`` into the ``DATABASES`` variable
|
||||
corresponding to your PostfixAdmin setup::
|
||||
|
||||
DATABASES = {
|
||||
"default" : {
|
||||
# default connection definition
|
||||
},
|
||||
"pfxadmin" : {
|
||||
"ENGINE" : "<engine>",
|
||||
"NAME" : "<database name>",
|
||||
"USER" : "<database user>",
|
||||
"PASSWORD" : "<user password>",
|
||||
}
|
||||
"default" : {
|
||||
# default connection definition
|
||||
},
|
||||
"pfxadmin" : {
|
||||
"ENGINE" : "<engine>",
|
||||
"NAME" : "<database name>",
|
||||
"USER" : "<database user>",
|
||||
"PASSWORD" : "<user password>",
|
||||
}
|
||||
}
|
||||
|
||||
This connection should correspond to the one defined in PostfixAdmin's
|
||||
configuration file.
|
||||
|
||||
You are now ready to start the migration. Enter Modoboa's root
|
||||
directory and execute the following command::
|
||||
Then, uncomment the line containing
|
||||
``'modoboa.tools.pfxadmin_migrate'`` inside the ``MODOBOA_APPS``
|
||||
variable and save your changes.
|
||||
|
||||
$ PYTHONPATH=$PWD/.. DJANGO_SETTINGS_MODULE=modoboa.settings \
|
||||
./tools/pfxadmin_migrate/migrate.py -r -p <directory that stores mailboxes>
|
||||
You are now ready to start the migration so run the following commands::
|
||||
|
||||
$ cd <modoboa_site>
|
||||
$ python manage.py migrate_from_postfixadmin -r -p <directory that stores mailboxes>
|
||||
|
||||
Depending on how many domains/mailboxes your existing setup contains,
|
||||
the migration can be long. Just wait for the script's ending.
|
||||
|
||||
Once the migration has succeed, go the *Admin > Configuration* panel,
|
||||
click on the *admin* row and modify the value of ``MAILDIR_ROOT`` as
|
||||
follow::
|
||||
.. note::
|
||||
|
||||
MAILDIR_ROOT =
|
||||
|
||||
The corresponding field must be empty. Don't touch other fields except
|
||||
``PASSWORD_SCHEME``, if needed. (set it to the same method as the one
|
||||
used by PostfixAdmin, check its configuration file if you're not sure)
|
||||
|
||||
Click on the *Save* button.
|
||||
The rename operation is not synchronous. If you choose this option,
|
||||
you must enable **Handle mailboxes on filesystem** option. See
|
||||
:ref:`fs_operations`.
|
||||
|
||||
The procedure is over, edit the :file:`settings.py` file and:
|
||||
|
||||
|
||||
@@ -274,6 +274,14 @@ Modoboa should connect to your LDAP server. They are described just below:
|
||||
| | | |
|
||||
| | | |
|
||||
+--------------------+---------------------------------+--------------------+
|
||||
|Domain/mailbox |Automatically create a domain and|yes |
|
||||
|creation |a mailbox when a new user is | |
|
||||
| |created just after the first | |
|
||||
| |successful authentication. You | |
|
||||
| |will generally want to disable | |
|
||||
| |this feature when the relay | |
|
||||
| |domains extension is in use | |
|
||||
+--------------------+---------------------------------+--------------------+
|
||||
|
||||
|
||||
If you need additional parameters, you will find a detailled
|
||||
|
||||
@@ -48,6 +48,17 @@ Finally, follow the common procedure::
|
||||
$ python manage.py syncdb --migrate
|
||||
$ python manage.py collectstatic
|
||||
|
||||
1.1.6: Few bugfixes
|
||||
===================
|
||||
|
||||
Catchall aliases were not really functional until this version as they
|
||||
were eating all domain traffic.
|
||||
|
||||
To fix them, a postfix map file (``sql-mailboxes-self-aliases.cf``)
|
||||
has been re-introduced and must be listed into the
|
||||
``virtual_alias_maps`` setting. See :ref:`postfix_config` for the
|
||||
order.
|
||||
|
||||
1.1.2: Audit trail issues
|
||||
=========================
|
||||
|
||||
|
||||
@@ -443,6 +443,7 @@ Use the following configuration in the :file:`/etc/postfix/main.cf` file
|
||||
virtual_alias_domains = <driver>:/etc/postfix/sql-domain-aliases.cf
|
||||
virtual_alias_maps = <driver>:/etc/postfix/sql-aliases.cf,
|
||||
<driver>:/etc/postfix/sql-domain-aliases-mailboxes.cf,
|
||||
<driver>:/etc/postfix/sql-mailboxes-self-aliases.cf,
|
||||
<driver>:/etc/postfix/sql-catchall-aliases.cf
|
||||
|
||||
smtpd_recipient_restrictions =
|
||||
|
||||
@@ -31,6 +31,9 @@ def enabled_applications():
|
||||
|
||||
|
||||
class GeneralParametersForm(parameters.AdminParametersForm):
|
||||
|
||||
"""General parameters."""
|
||||
|
||||
app = "core"
|
||||
|
||||
sep1 = SeparatorField(label=ugettext_lazy("Authentication"))
|
||||
@@ -78,7 +81,8 @@ class GeneralParametersForm(parameters.AdminParametersForm):
|
||||
ldap_server_address = forms.CharField(
|
||||
label=ugettext_lazy("Server address"),
|
||||
initial="localhost",
|
||||
help_text=ugettext_lazy("The IP address of the DNS name of the LDAP server")
|
||||
help_text=ugettext_lazy(
|
||||
"The IP address of the DNS name of the LDAP server")
|
||||
)
|
||||
|
||||
ldap_server_port = forms.IntegerField(
|
||||
@@ -90,7 +94,8 @@ class GeneralParametersForm(parameters.AdminParametersForm):
|
||||
ldap_secured = YesNoField(
|
||||
label=ugettext_lazy("Use a secured connection"),
|
||||
initial="no",
|
||||
help_text=ugettext_lazy("Use an SSL/TLS connection to access the LDAP server")
|
||||
help_text=ugettext_lazy(
|
||||
"Use an SSL/TLS connection to access the LDAP server")
|
||||
)
|
||||
|
||||
ldap_auth_method = forms.ChoiceField(
|
||||
@@ -105,14 +110,20 @@ class GeneralParametersForm(parameters.AdminParametersForm):
|
||||
ldap_bind_dn = forms.CharField(
|
||||
label=ugettext_lazy("Bind DN"),
|
||||
initial='',
|
||||
help_text=ugettext_lazy("The distinguished name to use when binding to the LDAP server. Leave empty for an anonymous bind"),
|
||||
help_text=ugettext_lazy(
|
||||
"The distinguished name to use when binding to the LDAP server. "
|
||||
"Leave empty for an anonymous bind"
|
||||
),
|
||||
required=False
|
||||
)
|
||||
|
||||
ldap_bind_password = forms.CharField(
|
||||
label=ugettext_lazy("Bind password"),
|
||||
initial='',
|
||||
help_text=ugettext_lazy("The password to use when binding to the LDAP server (with 'Bind DN')"),
|
||||
help_text=ugettext_lazy(
|
||||
"The password to use when binding to the LDAP server "
|
||||
"(with 'Bind DN')"
|
||||
),
|
||||
widget=forms.PasswordInput(render_value=True),
|
||||
required=False
|
||||
)
|
||||
@@ -120,21 +131,29 @@ class GeneralParametersForm(parameters.AdminParametersForm):
|
||||
ldap_search_base = forms.CharField(
|
||||
label=ugettext_lazy("Users search base"),
|
||||
initial="",
|
||||
help_text=ugettext_lazy("The distinguished name of the search base used to find users"),
|
||||
help_text=ugettext_lazy(
|
||||
"The distinguished name of the search base used to find users"
|
||||
),
|
||||
required=False
|
||||
)
|
||||
|
||||
ldap_search_filter = forms.CharField(
|
||||
label=ugettext_lazy("Search filter"),
|
||||
initial="(mail=%(user)s)",
|
||||
help_text=ugettext_lazy("An optional filter string (e.g. '(objectClass=person)'). In order to be valid, it must be enclosed in parentheses."),
|
||||
help_text=ugettext_lazy(
|
||||
"An optional filter string (e.g. '(objectClass=person)'). "
|
||||
"In order to be valid, it must be enclosed in parentheses."
|
||||
),
|
||||
required=False
|
||||
)
|
||||
|
||||
ldap_user_dn_template = forms.CharField(
|
||||
label=ugettext_lazy("User DN template"),
|
||||
initial="",
|
||||
help_text=ugettext_lazy("The template used to construct a user's DN. It should contain one placeholder (ie. %(user)s)"),
|
||||
help_text=ugettext_lazy(
|
||||
"The template used to construct a user's DN. It should contain "
|
||||
"one placeholder (ie. %(user)s)"
|
||||
),
|
||||
required=False
|
||||
)
|
||||
|
||||
@@ -147,7 +166,8 @@ class GeneralParametersForm(parameters.AdminParametersForm):
|
||||
ldap_is_active_directory = YesNoField(
|
||||
label=ugettext_lazy("Active Directory"),
|
||||
initial="no",
|
||||
help_text=ugettext_lazy("Tell if the LDAP server is an Active Directory one")
|
||||
help_text=ugettext_lazy(
|
||||
"Tell if the LDAP server is an Active Directory one")
|
||||
)
|
||||
|
||||
ldap_admin_groups = forms.CharField(
|
||||
@@ -169,6 +189,15 @@ class GeneralParametersForm(parameters.AdminParametersForm):
|
||||
required=False
|
||||
)
|
||||
|
||||
ldap_create_domain_mailbox = YesNoField(
|
||||
label=ugettext_lazy("Domain/mailbox creation"),
|
||||
initial="yes",
|
||||
help_text=ugettext_lazy(
|
||||
"Automatically create a domain and a mailbox when a new user "
|
||||
"is created just after the first successful authentication."
|
||||
)
|
||||
)
|
||||
|
||||
sep3 = SeparatorField(label=ugettext_lazy("Miscellaneous"))
|
||||
|
||||
top_notifications_check_interval = forms.IntegerField(
|
||||
@@ -200,7 +229,9 @@ class GeneralParametersForm(parameters.AdminParametersForm):
|
||||
label=ugettext_lazy("Default top redirection"),
|
||||
choices=[],
|
||||
initial="user",
|
||||
help_text=ugettext_lazy("The default redirection used when no application is specified")
|
||||
help_text=ugettext_lazy(
|
||||
"The default redirection used when no application is specified"
|
||||
)
|
||||
)
|
||||
|
||||
# Visibility rules
|
||||
@@ -292,7 +323,8 @@ class GeneralParametersForm(parameters.AdminParametersForm):
|
||||
"last_name": "sn"
|
||||
})
|
||||
ldap_uri = 'ldaps://' if values["ldap_secured"] == "yes" else "ldap://"
|
||||
ldap_uri += "%s:%s" % (values["ldap_server_address"], values["ldap_server_port"])
|
||||
ldap_uri += "%s:%s" % (
|
||||
values["ldap_server_address"], values["ldap_server_port"])
|
||||
setattr(settings, "AUTH_LDAP_SERVER_URI", ldap_uri)
|
||||
setattr(settings, "AUTH_LDAP_GROUP_TYPE", PosixGroupType())
|
||||
setattr(settings, "AUTH_LDAP_GROUP_SEARCH", LDAPSearch(
|
||||
|
||||
@@ -133,6 +133,13 @@ class DomainAliasesMailboxesMap(MapFile):
|
||||
sqlite = "(SELECT mb.address || '@' || dom.name FROM admin_domainalias domal INNER JOIN admin_domain dom ON domal.target_id=dom.id INNER JOIN admin_mailbox mb ON mb.domain_id=dom.id WHERE domal.name='%d' AND dom.enabled=1 AND mb.address='%u') UNION (SELECT al.address || '@' || dom.name FROM admin_domainalias domal INNER JOIN admin_domain dom ON domal.target_id=dom.id INNER JOIN admin_alias al ON al.domain_id=dom.id WHERE domal.name='%d' AND dom.enabled=1 AND al.address='%u')"
|
||||
|
||||
|
||||
class MailboxesSelfAliasesMap(MapFile):
|
||||
filename = "sql-mailboxes-self-aliases.cf"
|
||||
mysql = "SELECT email FROM core_user u INNER JOIN admin_mailbox mb ON mb.user_id=u.id WHERE u.email='%s' AND u.is_active=1"
|
||||
postgres = "SELECT email FROM core_user u INNER JOIN admin_mailbox mb ON mb.user_id=u.id WHERE u.email='%s' AND u.is_active"
|
||||
sqlite = "SELECT email FROM core_user u INNER JOIN admin_mailbox mb ON mb.user_id=u.id WHERE u.email='%s' AND u.is_active=1"
|
||||
|
||||
|
||||
class CatchallAliasesMap(MapFile):
|
||||
filename = 'sql-catchall-aliases.cf'
|
||||
mysql = "(SELECT concat(mb.address, '@', dom.name) FROM admin_mailbox mb INNER JOIN admin_domain dom ON mb.domain_id=dom.id WHERE mb.id IN (SELECT al_mb.mailbox_id FROM admin_alias al INNER JOIN admin_domain dom ON al.domain_id=dom.id INNER JOIN admin_alias_mboxes al_mb ON al.id=al_mb.alias_id WHERE al.enabled=1 AND al.address='*' AND dom.name='%d' AND dom.enabled=1)) UNION (SELECT al.extmboxes FROM admin_alias al INNER JOIN admin_domain dom ON al.domain_id=dom.id WHERE al.enabled='1' AND al.extmboxes<>'' AND al.address='*' AND dom.name='%d' AND dom.enabled=1)"
|
||||
|
||||
@@ -175,6 +175,9 @@ MODOBOA_APPS = (
|
||||
'modoboa.extensions.sievefilters',
|
||||
'modoboa.extensions.radicale',
|
||||
{% if not amavis_conn %}#{% endif %}'modoboa.extensions.amavis',
|
||||
|
||||
# Tools from here
|
||||
#'modoboa.tools.pfxadmin_migrate',
|
||||
)
|
||||
|
||||
INSTALLED_APPS += MODOBOA_APPS
|
||||
|
||||
@@ -196,8 +196,11 @@ def import_account_mailbox(user, account, row):
|
||||
@events.observe("AccountAutoCreated")
|
||||
def account_auto_created(user):
|
||||
from modoboa.core.models import User
|
||||
from modoboa.extensions.admin.lib import check_if_domain_exists
|
||||
from modoboa.lib.permissions import grant_access_to_object
|
||||
|
||||
if parameters.get_admin("LDAP_CREATE_DOMAIN_MAILBOX", app="core") == "no":
|
||||
return
|
||||
localpart, domname = split_mailbox(user.username)
|
||||
if user.group != 'SimpleUsers' and domname is None:
|
||||
return
|
||||
@@ -205,6 +208,10 @@ def account_auto_created(user):
|
||||
try:
|
||||
domain = Domain.objects.get(name=domname)
|
||||
except Domain.DoesNotExist:
|
||||
label = check_if_domain_exists(
|
||||
domname, [(DomainAlias, _('domain alias'))])
|
||||
if label is not None:
|
||||
return
|
||||
domain = Domain(name=domname, enabled=True, quota=0)
|
||||
domain.save(creator=sadmins[0])
|
||||
for su in sadmins[1:]:
|
||||
|
||||
@@ -10,6 +10,7 @@ from modoboa.lib.formutils import (
|
||||
)
|
||||
from modoboa.lib.webutils import render_to_json_response
|
||||
from modoboa.core.models import User
|
||||
from modoboa.extensions.admin.lib import check_if_domain_exists
|
||||
from modoboa.extensions.admin.models import (
|
||||
Domain, DomainAlias, Mailbox, Alias, Quota
|
||||
)
|
||||
@@ -74,34 +75,27 @@ class DomainFormGeneral(forms.ModelForm, DynamicForm):
|
||||
|
||||
cleaned_data = self.cleaned_data
|
||||
name = cleaned_data["name"]
|
||||
dtypes = events.raiseQueryEvent('CheckDomainName')
|
||||
for dtype, label in [(DomainAlias, _('domain alias'))] + dtypes:
|
||||
try:
|
||||
dtype.objects.get(name=name)
|
||||
except dtype.DoesNotExist:
|
||||
pass
|
||||
else:
|
||||
self._errors["name"] = self.error_class(
|
||||
[_("A %s with this name already exists" % unicode(label))]
|
||||
)
|
||||
del cleaned_data["name"]
|
||||
break
|
||||
label = check_if_domain_exists(name, [(DomainAlias, _('domain alias'))])
|
||||
if label is not None:
|
||||
self._errors["name"] = self.error_class(
|
||||
[_("A %s with this name already exists" % unicode(label))]
|
||||
)
|
||||
del cleaned_data["name"]
|
||||
|
||||
for k in cleaned_data.keys():
|
||||
if not k.startswith("aliases"):
|
||||
continue
|
||||
if cleaned_data[k] == "":
|
||||
del cleaned_data[k]
|
||||
continue
|
||||
for dtype, label in [(Domain, _('domain'))] + dtypes:
|
||||
try:
|
||||
dtype.objects.get(name=cleaned_data[k])
|
||||
except dtype.DoesNotExist:
|
||||
continue
|
||||
label = check_if_domain_exists(
|
||||
cleaned_data[k], [(Domain, _('domain'))])
|
||||
if label is not None:
|
||||
self._errors[k] = self.error_class(
|
||||
[_("A %s with this name already exists" % unicode(label))]
|
||||
)
|
||||
del cleaned_data[k]
|
||||
break
|
||||
|
||||
return cleaned_data
|
||||
|
||||
def update_mailbox_quotas(self, domain):
|
||||
|
||||
@@ -96,3 +96,23 @@ def get_domains(user, domfilter=None, searchquery=None, **extrafilters):
|
||||
'ExtraDomainEntries', user, domfilter, searchquery, **extrafilters
|
||||
)
|
||||
return chain(domains, extra_domain_entries)
|
||||
|
||||
|
||||
def check_if_domain_exists(name, extra_checks=None):
|
||||
"""Check if a domain already exists.
|
||||
|
||||
We not only look for domains, we also look for every object that
|
||||
could conflict with a domain (domain alias, etc.)
|
||||
|
||||
"""
|
||||
dtypes = events.raiseQueryEvent('CheckDomainName')
|
||||
if extra_checks is not None:
|
||||
dtypes = extra_checks + dtypes
|
||||
for dtype, label in dtypes:
|
||||
try:
|
||||
dtype.objects.get(name=name)
|
||||
except dtype.DoesNotExist:
|
||||
pass
|
||||
else:
|
||||
return label
|
||||
return None
|
||||
|
||||
@@ -61,13 +61,15 @@ class SQLconnector(object):
|
||||
|
||||
"""
|
||||
if self.user.group == 'SimpleUsers':
|
||||
rcpts = [self.user.email] \
|
||||
+ self.user.mailbox_set.all()[0].alias_addresses
|
||||
flt &= Q(rid__email__in=rcpts)
|
||||
rcpts = [self.user.email]
|
||||
if self.user.mailbox_set.count():
|
||||
rcpts += self.user.mailbox_set.all()[0].alias_addresses
|
||||
return flt & Q(rid__email__in=rcpts)
|
||||
elif not self.user.is_superuser:
|
||||
doms = Domain.objects.get_for_admin(self.user)
|
||||
regexp = "(%s)" % '|'.join([dom.name for dom in doms])
|
||||
flt &= Q(rid__email__regex=regexp)
|
||||
return flt & Q(rid__email__regex=regexp)
|
||||
return flt
|
||||
|
||||
def _apply_extra_search_filter(self, crit, pattern):
|
||||
"""Apply search filters using additional criterias.
|
||||
@@ -90,7 +92,7 @@ class SQLconnector(object):
|
||||
"""
|
||||
flt = Q(rs__in=[' ', 'V', 'R', 'p']) \
|
||||
if self.navparams.get('viewrequests', '0') != '1' else Q(rs='p')
|
||||
self._apply_msgrcpt_filters(flt)
|
||||
flt = self._apply_msgrcpt_filters(flt)
|
||||
pattern = self.navparams.get("pattern", "")
|
||||
if pattern:
|
||||
criteria = self.navparams.get('criteria')
|
||||
@@ -226,8 +228,9 @@ class PgSQLconnector(SQLconnector):
|
||||
"""
|
||||
self._where = []
|
||||
if self.user.group == 'SimpleUsers':
|
||||
rcpts = [self.user.email] \
|
||||
+ self.user.mailbox_set.all()[0].alias_addresses
|
||||
rcpts = [self.user.email]
|
||||
if self.user.mailbox_set.count():
|
||||
rcpts += self.user.mailbox_set.all()[0].alias_addresses
|
||||
self._where.append(
|
||||
"convert_from(maddr.email, 'UTF8') IN (%s)" \
|
||||
% (','.join(["'%s'" % rcpt for rcpt in rcpts])))
|
||||
@@ -236,6 +239,7 @@ class PgSQLconnector(SQLconnector):
|
||||
regexp = "(%s)" % '|'.join([dom.name for dom in doms])
|
||||
self._where.append(
|
||||
"convert_from(maddr.email, 'UTF8') ~ '%s'" % regexp)
|
||||
return flt
|
||||
|
||||
def _apply_extra_search_filter(self, crit, pattern):
|
||||
"""Apply search filters using additional criterias.
|
||||
|
||||
@@ -136,9 +136,16 @@ Quarantine.prototype = {
|
||||
* Filter listing by message type (spam, virus, etc.)
|
||||
*/
|
||||
filter_by_type: function(evt) {
|
||||
var msgtype = $(evt.target).html().trim();
|
||||
var $target = $(evt.target);
|
||||
var msgtype;
|
||||
|
||||
if (msgtype != 'All') {
|
||||
if ($target.is("span")) {
|
||||
msgtype = $target.html().trim();
|
||||
} else {
|
||||
msgtype = $target.attr("name").replace("msgtype_", "");
|
||||
}
|
||||
|
||||
if (msgtype != 'all') {
|
||||
this.navobj.setparam('msgtype', msgtype);
|
||||
} else {
|
||||
this.navobj.delparam('msgtype');
|
||||
|
||||
@@ -102,7 +102,9 @@ def viewmail_selfservice(request, mail_id,
|
||||
@selfservice(viewmail_selfservice)
|
||||
def viewmail(request, mail_id):
|
||||
rcpt = request.GET["rcpt"]
|
||||
if request.user.mailbox_set.count():
|
||||
if request.user.email == rcpt:
|
||||
get_connector().set_msgrcpt_status(rcpt, mail_id, 'V')
|
||||
elif request.user.mailbox_set.count():
|
||||
mb = Mailbox.objects.get(user=request.user)
|
||||
if rcpt == mb.full_address or rcpt in mb.alias_addresses:
|
||||
get_connector().set_msgrcpt_status(rcpt, mail_id, 'V')
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: modoboa-1.0.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2013-12-15 10:16+0100\n"
|
||||
"POT-Creation-Date: 2014-08-24 12:33+0200\n"
|
||||
"PO-Revision-Date: 2013-09-29 22:13+0100\n"
|
||||
"Last-Translator: Miroslav Abrahám <miris@kunago.com>\n"
|
||||
"Language-Team: CS <miris@kunago.com>\n"
|
||||
@@ -108,11 +108,11 @@ msgstr "Odstranit vybranou schránku?"
|
||||
msgid "Mailbox removed"
|
||||
msgstr "Schránka odstraněna"
|
||||
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:746
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:752
|
||||
msgid "Message sent"
|
||||
msgstr "Zpráva odeslána"
|
||||
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:997
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:1003
|
||||
#, c-format
|
||||
msgid "%s file"
|
||||
msgid_plural "%s files"
|
||||
@@ -120,7 +120,7 @@ msgstr[0] "%s soubor"
|
||||
msgstr[1] "%s soubory"
|
||||
msgstr[2] "%s souborů"
|
||||
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:1031
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:1037
|
||||
#, c-format
|
||||
msgid "Moving %s message"
|
||||
msgid_plural "Moving %s messages"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Modoboa-0.9\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2013-12-15 10:16+0100\n"
|
||||
"POT-Creation-Date: 2014-08-24 12:33+0200\n"
|
||||
"PO-Revision-Date: 2013-12-08 23:34+0100\n"
|
||||
"Last-Translator: Patrick Ben Koetter <p@sys4.de>\n"
|
||||
"Language-Team: DE <p@state-of-mind.fr>\n"
|
||||
@@ -108,18 +108,18 @@ msgstr "Ausgewählte Mailbox entfernen?"
|
||||
msgid "Mailbox removed"
|
||||
msgstr "Mailbox entfernt"
|
||||
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:746
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:752
|
||||
msgid "Message sent"
|
||||
msgstr "Nachricht gesendet"
|
||||
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:997
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:1003
|
||||
#, c-format
|
||||
msgid "%s file"
|
||||
msgid_plural "%s files"
|
||||
msgstr[0] "%s Datei"
|
||||
msgstr[1] "%s Dateien"
|
||||
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:1031
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:1037
|
||||
#, c-format
|
||||
msgid "Moving %s message"
|
||||
msgid_plural "Moving %s messages"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2013-12-15 10:16+0100\n"
|
||||
"POT-Creation-Date: 2014-08-24 12:32+0200\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@@ -106,18 +106,18 @@ msgstr ""
|
||||
msgid "Mailbox removed"
|
||||
msgstr ""
|
||||
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:746
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:752
|
||||
msgid "Message sent"
|
||||
msgstr ""
|
||||
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:997
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:1003
|
||||
#, c-format
|
||||
msgid "%s file"
|
||||
msgid_plural "%s files"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:1031
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:1037
|
||||
#, c-format
|
||||
msgid "Moving %s message"
|
||||
msgid_plural "Moving %s messages"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -9,7 +9,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Modoboa\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2013-12-15 10:16+0100\n"
|
||||
"POT-Creation-Date: 2014-08-24 12:32+0200\n"
|
||||
"PO-Revision-Date: 2013-06-03 16:32+0000\n"
|
||||
"Last-Translator: Pedro Gracia <lasarux@neuroomante.com>\n"
|
||||
"Language-Team: Spanish (http://www.transifex.com/projects/p/modoboa/language/"
|
||||
@@ -111,18 +111,18 @@ msgstr "¿Eliminar el mailbox seleccionado?"
|
||||
msgid "Mailbox removed"
|
||||
msgstr "Mailbox eliminado"
|
||||
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:746
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:752
|
||||
msgid "Message sent"
|
||||
msgstr "Mensaje enviado"
|
||||
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:997
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:1003
|
||||
#, c-format
|
||||
msgid "%s file"
|
||||
msgid_plural "%s files"
|
||||
msgstr[0] "%s fichero"
|
||||
msgstr[1] "%s ficheros"
|
||||
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:1031
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:1037
|
||||
#, c-format
|
||||
msgid "Moving %s message"
|
||||
msgid_plural "Moving %s messages"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: modoboa-1.0.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2013-12-15 10:16+0100\n"
|
||||
"POT-Creation-Date: 2014-08-24 12:33+0200\n"
|
||||
"PO-Revision-Date: 2011-01-07 10:00+0100\n"
|
||||
"Last-Translator: Antoine Nguyen <tonio@ngyn.org>\n"
|
||||
"Language-Team: FR <tonio@ngyn.org>\n"
|
||||
@@ -108,18 +108,18 @@ msgstr "Supprimer la boite sélectionnée?"
|
||||
msgid "Mailbox removed"
|
||||
msgstr "Boite aux lettres supprimées"
|
||||
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:746
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:752
|
||||
msgid "Message sent"
|
||||
msgstr "Message envoyé"
|
||||
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:997
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:1003
|
||||
#, c-format
|
||||
msgid "%s file"
|
||||
msgid_plural "%s files"
|
||||
msgstr[0] "%s fichier"
|
||||
msgstr[1] "%s fichiers"
|
||||
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:1031
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:1037
|
||||
#, c-format
|
||||
msgid "Moving %s message"
|
||||
msgid_plural "Moving %s messages"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Modoboa\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2013-12-22 23:16+0100\n"
|
||||
"POT-Creation-Date: 2014-08-24 12:32+0200\n"
|
||||
"PO-Revision-Date: 2013-12-23 00:58+0100\n"
|
||||
"Last-Translator: Alberto Berti <alberto@metapensiero.it>\n"
|
||||
"Language-Team: Italiano <LL@li.org>\n"
|
||||
@@ -108,18 +108,18 @@ msgstr "Rimuovere la casella di posta selezionata?"
|
||||
msgid "Mailbox removed"
|
||||
msgstr "Casella di posta rimossa"
|
||||
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:746
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:752
|
||||
msgid "Message sent"
|
||||
msgstr "Messaggio inviato"
|
||||
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:997
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:1003
|
||||
#, c-format
|
||||
msgid "%s file"
|
||||
msgid_plural "%s files"
|
||||
msgstr[0] "%s file"
|
||||
msgstr[1] "%s file"
|
||||
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:1031
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:1037
|
||||
#, c-format
|
||||
msgid "Moving %s message"
|
||||
msgid_plural "Moving %s messages"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,21 +1,22 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
#
|
||||
#
|
||||
# Translators:
|
||||
# TuxBrother <vrijwilligerswerk@wij.rs>, 2014
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Modoboa\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2013-12-15 10:16+0100\n"
|
||||
"POT-Creation-Date: 2014-08-24 12:33+0200\n"
|
||||
"PO-Revision-Date: 2014-04-16 12:58+0000\n"
|
||||
"Last-Translator: TuxBrother <vrijwilligerswerk@wij.rs>\n"
|
||||
"Language-Team: Dutch (Netherlands) (http://www.transifex.com/projects/p/modoboa/language/nl_NL/)\n"
|
||||
"Language-Team: Dutch (Netherlands) (http://www.transifex.com/projects/p/"
|
||||
"modoboa/language/nl_NL/)\n"
|
||||
"Language: nl_NL\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: nl_NL\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: extensions/admin/static/admin/js/admin.js:32
|
||||
@@ -28,13 +29,17 @@ msgstr "De map van dit domein niet verwijderen (Zal de data ervan behouden)"
|
||||
|
||||
#: extensions/admin/static/admin/js/admin.js:173
|
||||
msgid "This operation will remove ALL data associated to this domain."
|
||||
msgstr "Deze actie zal ALLE data verwijderen die bij dit domein hoort. Wilt u doorgaan?"
|
||||
msgstr ""
|
||||
"Deze actie zal ALLE data verwijderen die bij dit domein hoort. Wilt u "
|
||||
"doorgaan?"
|
||||
|
||||
#: extensions/admin/static/admin/js/admin.js:174
|
||||
msgid ""
|
||||
"This operation will remove all data associated to this domain, excepting "
|
||||
"accounts."
|
||||
msgstr "Deze actie zal alle data die bij dit domein hoort verwijderen, behalve de gebruikers."
|
||||
msgstr ""
|
||||
"Deze actie zal alle data die bij dit domein hoort verwijderen, behalve de "
|
||||
"gebruikers."
|
||||
|
||||
#: extensions/admin/static/admin/js/admin.js:222
|
||||
msgid "No domain administrator defined"
|
||||
@@ -107,18 +112,18 @@ msgstr "Geselecteerde mailbox verwijderen?"
|
||||
msgid "Mailbox removed"
|
||||
msgstr "Mailbox verwijderd"
|
||||
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:746
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:752
|
||||
msgid "Message sent"
|
||||
msgstr "Bericht verstuurd"
|
||||
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:997
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:1003
|
||||
#, c-format
|
||||
msgid "%s file"
|
||||
msgid_plural "%s files"
|
||||
msgstr[0] "%s bestanden"
|
||||
msgstr[1] "%s bestanden"
|
||||
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:1031
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:1037
|
||||
#, c-format
|
||||
msgid "Moving %s message"
|
||||
msgid_plural "Moving %s messages"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
#
|
||||
#
|
||||
# Translators:
|
||||
# lusitan <lusitan@hushmail.com>, 2013
|
||||
# mikec <miguelmclara@gmail.com>, 2012
|
||||
@@ -10,14 +10,15 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Modoboa\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2013-12-15 10:16+0100\n"
|
||||
"POT-Creation-Date: 2014-08-24 12:33+0200\n"
|
||||
"PO-Revision-Date: 2013-12-23 11:22+0000\n"
|
||||
"Last-Translator: lusitan <lusitan@hushmail.com>\n"
|
||||
"Language-Team: Portuguese (Portugal) (http://www.transifex.com/projects/p/modoboa/language/pt_PT/)\n"
|
||||
"Language-Team: Portuguese (Portugal) (http://www.transifex.com/projects/p/"
|
||||
"modoboa/language/pt_PT/)\n"
|
||||
"Language: pt_PT\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: pt_PT\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: extensions/admin/static/admin/js/admin.js:32
|
||||
@@ -36,7 +37,9 @@ msgstr "Esta operação irá remover TODOS os dados associados a este domínio."
|
||||
msgid ""
|
||||
"This operation will remove all data associated to this domain, excepting "
|
||||
"accounts."
|
||||
msgstr "Esta operação irá remover TODOS os dados associados a este domínio, excepto as contas."
|
||||
msgstr ""
|
||||
"Esta operação irá remover TODOS os dados associados a este domínio, excepto "
|
||||
"as contas."
|
||||
|
||||
#: extensions/admin/static/admin/js/admin.js:222
|
||||
msgid "No domain administrator defined"
|
||||
@@ -109,18 +112,18 @@ msgstr "Remover a caixa de correio seleccionada"
|
||||
msgid "Mailbox removed"
|
||||
msgstr "Caixa removida"
|
||||
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:746
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:752
|
||||
msgid "Message sent"
|
||||
msgstr "Mensagem enviada"
|
||||
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:997
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:1003
|
||||
#, c-format
|
||||
msgid "%s file"
|
||||
msgid_plural "%s files"
|
||||
msgstr[0] "%s ficheiro"
|
||||
msgstr[1] "%s ficheiros"
|
||||
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:1031
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:1037
|
||||
#, c-format
|
||||
msgid "Moving %s message"
|
||||
msgid_plural "Moving %s messages"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Modoboa\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2013-12-15 10:16+0100\n"
|
||||
"POT-Creation-Date: 2014-08-24 12:32+0200\n"
|
||||
"PO-Revision-Date: 2013-12-03 09:35+0000\n"
|
||||
"Last-Translator: Antoine Nguyen <tonio@ngyn.org>\n"
|
||||
"Language-Team: Russian (http://www.transifex.com/projects/p/modoboa/language/"
|
||||
@@ -110,11 +110,11 @@ msgstr "Удалить выбранные почтовые ящики?"
|
||||
msgid "Mailbox removed"
|
||||
msgstr "Почтовый ящик удален"
|
||||
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:746
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:752
|
||||
msgid "Message sent"
|
||||
msgstr "Сообщение послано"
|
||||
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:997
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:1003
|
||||
#, c-format
|
||||
msgid "%s file"
|
||||
msgid_plural "%s files"
|
||||
@@ -122,7 +122,7 @@ msgstr[0] "%s файл"
|
||||
msgstr[1] "%s файла"
|
||||
msgstr[2] "%s файлов"
|
||||
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:1031
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:1037
|
||||
#, c-format
|
||||
msgid "Moving %s message"
|
||||
msgid_plural "Moving %s messages"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Modoboa\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2013-12-15 10:16+0100\n"
|
||||
"POT-Creation-Date: 2014-08-24 12:32+0200\n"
|
||||
"PO-Revision-Date: 2013-05-23 08:22+0000\n"
|
||||
"Last-Translator: ogg1980 <olle@dalnix.se>\n"
|
||||
"Language-Team: Swedish (http://www.transifex.com/projects/p/modoboa/language/"
|
||||
@@ -110,18 +110,18 @@ msgstr "Radera markerad brevlåda?"
|
||||
msgid "Mailbox removed"
|
||||
msgstr "Brevlåda raderad"
|
||||
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:746
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:752
|
||||
msgid "Message sent"
|
||||
msgstr "Meddelande sänt"
|
||||
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:997
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:1003
|
||||
#, c-format
|
||||
msgid "%s file"
|
||||
msgid_plural "%s files"
|
||||
msgstr[0] "%s fil"
|
||||
msgstr[1] "%s filer"
|
||||
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:1031
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:1037
|
||||
#, c-format
|
||||
msgid "Moving %s message"
|
||||
msgid_plural "Moving %s messages"
|
||||
|
||||
@@ -0,0 +1,296 @@
|
||||
# coding: utf-8
|
||||
|
||||
"""Automatic postfixadmin to Modoboa migration
|
||||
===========================================
|
||||
|
||||
This script provides an easy way to migrate an existing postfixadmin
|
||||
database to a Modoboa one.
|
||||
|
||||
As the two products do not share the same schema, some information
|
||||
will be lost on the new database. Here is the list:
|
||||
* Description, transport and backup MX for domains
|
||||
* Logs
|
||||
* Fetchmail table
|
||||
|
||||
Mailboxes organisation on the filesystem will change. PostfixAdmin
|
||||
uses the following layout::
|
||||
|
||||
<topdir>/domain.tld/user@domain.tld/
|
||||
|
||||
Whereas Modoboa uses the following::
|
||||
|
||||
<topdir>/domain.tld/user/
|
||||
|
||||
To rename directories, you'll need the appropriate permissions.
|
||||
|
||||
Domain administrators that manage more than one domain will not be
|
||||
completly restored. They will only be administrator of the domain that
|
||||
corresponds to their username.
|
||||
|
||||
Vacation messages will not be migrated by this script. Users will have
|
||||
to define new messages by using Modoboa's autoreply plugin. (if
|
||||
activated)
|
||||
|
||||
One last point about passwords. PostfixAdmin uses different hash
|
||||
algorithms to store passwords so this script does not modify
|
||||
them. They are copied "as is" in the new database. In order to use
|
||||
them, use the ``--passwords-scheme`` to tell Modoboa which scheme was
|
||||
used by PostfixAdmin.
|
||||
|
||||
.. note::
|
||||
|
||||
Actually, it appears that the ``crypt`` scheme is compatible with
|
||||
PostfixAdmin ``md5crypt`` algorithm...
|
||||
|
||||
"""
|
||||
from optparse import make_option
|
||||
import os
|
||||
import sys
|
||||
|
||||
from django.contrib.auth.models import Group
|
||||
from django.core.management.base import BaseCommand
|
||||
from django.db import transaction
|
||||
from django.db.models import Q
|
||||
|
||||
import modoboa.core.models as core_models
|
||||
import modoboa.extensions.admin.models as admin_models
|
||||
from modoboa.lib.emailutils import split_mailbox
|
||||
|
||||
from modoboa.tools.pfxadmin_migrate import models as pf_models
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
|
||||
"""Migration from postfixadmin."""
|
||||
|
||||
help = "This command eases the migration from a postfixadmin database."
|
||||
option_list = BaseCommand.option_list + (
|
||||
make_option(
|
||||
"-f", "--from", dest="_from", default="pfxadmin",
|
||||
help="Name of postfixadmin db connection declared in settings.py"
|
||||
"(default is pfxadmin)"
|
||||
),
|
||||
make_option(
|
||||
"-t", "--to", dest="_to", default="default",
|
||||
help="Name of the Modoboa db connection declared in settings.py"
|
||||
" (default is default)"
|
||||
),
|
||||
make_option(
|
||||
"-r", "--rename-dirs", action="store_true", default=False,
|
||||
help="Rename mailbox directories (default is no)"
|
||||
),
|
||||
make_option(
|
||||
"-p", "--mboxes-path", default=None,
|
||||
help="Path where directories are stored on the filesystem"
|
||||
"(used only if --rename-dirs)"
|
||||
),
|
||||
make_option(
|
||||
"-s", "--passwords-scheme", default="crypt",
|
||||
help="Scheme used to crypt user passwords"
|
||||
)
|
||||
)
|
||||
|
||||
def _migrate_dates(self, oldobj):
|
||||
"""Creates a new ObjectDates instance.
|
||||
|
||||
Due to Django limitations, we only retrieve the creation date. The
|
||||
modification date will be set to 'now' because the corresponding
|
||||
field in Modoboa's schema has the 'auto_now' attribute to True.
|
||||
(see https://docs.djangoproject.com/en/dev/ref/models/fields/#django.db.models.DateField).
|
||||
|
||||
:param oldobj: the PostfixAdmin record which is beeing migrated.
|
||||
"""
|
||||
dates = admin_models.base.ObjectDates()
|
||||
dates.save()
|
||||
|
||||
dates.creation = oldobj.created
|
||||
dates.save()
|
||||
return dates
|
||||
|
||||
def _migrate_domain_aliases(self, domain, options, creator):
|
||||
"""Migrate aliases of a single domain."""
|
||||
print "\tMigrating domain aliases"
|
||||
old_domain_aliases = pf_models.AliasDomain.objects.using(
|
||||
options["_from"]
|
||||
).filter(target_domain=domain.name)
|
||||
for old_da in old_domain_aliases:
|
||||
try:
|
||||
target_domain = admin_models.Domain.objects \
|
||||
.using(options["_to"]).get(name=old_da.target_domain)
|
||||
new_da = admin_models.DomainAlias()
|
||||
new_da.name = old_da.alias_domain
|
||||
new_da.target = target_domain
|
||||
new_da.enabled = old_da.active
|
||||
new_da.dates = self._migrate_dates(old_da)
|
||||
new_da.save(using=options["_to"])
|
||||
new_da.post_create(creator)
|
||||
except core_models.Domain.DoesNotExist:
|
||||
print ("Warning: target domain %s does not exists, "
|
||||
"not creating alias domain %s"
|
||||
% old_da.target_domain, old_da.alias_domain)
|
||||
continue
|
||||
|
||||
def _migrate_mailbox_aliases(self, domain, options, creator):
|
||||
"""Migrate mailbox aliases of a single domain."""
|
||||
print "\tMigrating mailbox aliases"
|
||||
old_aliases = pf_models.Alias.objects \
|
||||
.using(options["_from"]).filter(domain=domain.name)
|
||||
for old_al in old_aliases:
|
||||
if old_al.address == old_al.goto:
|
||||
continue
|
||||
new_al = admin_models.Alias()
|
||||
local_part, tmp = split_mailbox(old_al.address)
|
||||
if local_part is None or not len(local_part):
|
||||
if tmp is None or not len(tmp):
|
||||
print (
|
||||
"Warning: skipping alias %s (cannot retrieve local "
|
||||
"part). You will need to recreate it manually."
|
||||
% old_al.address
|
||||
)
|
||||
continue
|
||||
new_al.address = "*"
|
||||
else:
|
||||
new_al.address = local_part
|
||||
new_al.domain = domain
|
||||
new_al.enabled = old_al.active
|
||||
extmboxes = []
|
||||
intmboxes = []
|
||||
for goto in old_al.goto.split(","):
|
||||
try:
|
||||
mb = admin_models.Mailbox.objects \
|
||||
.using(options["_to"]).get(user__username=goto)
|
||||
except admin_models.Mailbox.DoesNotExist:
|
||||
extmboxes += [goto]
|
||||
else:
|
||||
intmboxes += [mb]
|
||||
new_al.dates = self._migrate_dates(old_al)
|
||||
new_al.save(
|
||||
int_rcpts=intmboxes, ext_rcpts=extmboxes, creator=creator,
|
||||
using=options["_to"]
|
||||
)
|
||||
|
||||
def _migrate_mailboxes(self, domain, options, creator):
|
||||
"""Migrate mailboxes of a single domain.
|
||||
|
||||
.. note::
|
||||
|
||||
Mailboxes rename will be done later by the
|
||||
handle_mailboxes_operation script.
|
||||
|
||||
"""
|
||||
print "\tMigrating mailboxes"
|
||||
old_mboxes = pf_models.Mailbox.objects \
|
||||
.using(options["_from"]).filter(domain=domain.name)
|
||||
for old_mb in old_mboxes:
|
||||
new_user = core_models.User()
|
||||
new_user.username = old_mb.username
|
||||
new_user.first_name = old_mb.name.partition(' ')[0]
|
||||
new_user.last_name = old_mb.name.partition(' ')[2]
|
||||
new_user.email = old_mb.username
|
||||
new_user.is_active = old_mb.active
|
||||
new_user.date_joined = old_mb.created
|
||||
new_user.password = "{%s}%s" % \
|
||||
(options["passwords_scheme"].upper(), old_mb.password)
|
||||
new_user.dates = self._migrate_dates(old_mb)
|
||||
new_user.save(creator=creator, using=options["_to"])
|
||||
new_user.set_role("SimpleUsers")
|
||||
|
||||
new_mb = admin_models.Mailbox()
|
||||
new_mb.user = new_user
|
||||
new_mb.address = old_mb.local_part
|
||||
new_mb.domain = domain
|
||||
new_mb.dates = self._migrate_dates(old_mb)
|
||||
new_mb.set_quota(old_mb.quota / 1024000, override_rules=True)
|
||||
new_mb.save(creator=creator, using=options["_to"])
|
||||
|
||||
if options["rename_dirs"]:
|
||||
oldpath = os.path.join(
|
||||
options["mboxes_path"], domain.name, old_mb.maildir)
|
||||
new_mb.rename_dir(oldpath)
|
||||
|
||||
def _migrate_domain(self, old_dom, options, creator):
|
||||
"""Single domain migration."""
|
||||
print "Migrating domain %s" % old_dom.domain
|
||||
newdom = admin_models.Domain()
|
||||
newdom.name = old_dom.domain
|
||||
newdom.enabled = old_dom.active
|
||||
newdom.quota = old_dom.maxquota
|
||||
newdom.dates = self._migrate_dates(old_dom)
|
||||
newdom.save(creator=creator, using=options["_to"])
|
||||
self._migrate_mailboxes(newdom, options, creator)
|
||||
self._migrate_mailbox_aliases(newdom, options, creator)
|
||||
self._migrate_domain_aliases(newdom, options, creator)
|
||||
|
||||
def _migrate_admins(self, options, creator):
|
||||
"""Administrators migration."""
|
||||
print "Migrating administrators"
|
||||
|
||||
dagroup = Group.objects.using(options["_to"]).get(name="DomainAdmins")
|
||||
for old_admin in pf_models.Admin.objects.using(options["_from"]).all():
|
||||
local_part, domname = split_mailbox(old_admin.username)
|
||||
try:
|
||||
query = Q(username=old_admin.username) & \
|
||||
(Q(domain="ALL") | Q(domain=domname))
|
||||
creds = pf_models.DomainAdmins.objects \
|
||||
.using(options["_from"]).get(query)
|
||||
except pf_models.DomainAdmins.DoesNotExist:
|
||||
print (
|
||||
"Warning: skipping useless admin %s" % (old_admin.username)
|
||||
)
|
||||
continue
|
||||
try:
|
||||
domain = admin_models.Domain.objects \
|
||||
.using(options["_to"]).get(name=domname)
|
||||
except admin_models.Domain.DoesNotExist:
|
||||
print (
|
||||
"Warning: skipping domain admin %s, domain not found"
|
||||
% old_admin.username
|
||||
)
|
||||
continue
|
||||
try:
|
||||
user = core_models.User.objects \
|
||||
.using(options["_to"]).get(username=old_admin.username)
|
||||
except core_models.User.DoesNotExist:
|
||||
user = core_models.User()
|
||||
user.username = old_admin.username
|
||||
user.email = old_admin.username
|
||||
user.password = old_admin.password
|
||||
user.is_active = old_admin.active
|
||||
user.save(creator=creator, using=options["_to"])
|
||||
|
||||
user.date_joined = old_admin.modified
|
||||
if creds.domain == "ALL":
|
||||
user.is_superuser = True
|
||||
else:
|
||||
user.groups.add(dagroup)
|
||||
domain.add_admin(user)
|
||||
user.save(using=options["_to"])
|
||||
|
||||
def _do_migration(self, options, creator_username='admin'):
|
||||
"""Run the complete migration."""
|
||||
pf_domains = pf_models.Domain.objects.using(options["_from"]).all()
|
||||
creator = core_models.User.objects.using(options["_to"]).get(
|
||||
username=creator_username)
|
||||
for pf_domain in pf_domains:
|
||||
|
||||
if pf_domain.domain == "ALL":
|
||||
continue
|
||||
|
||||
try:
|
||||
# Skip this domain if it's an alias
|
||||
is_domain_alias = pf_models.AliasDomain.objects \
|
||||
.using(options["_from"]).get(alias_domain=pf_domain.domain)
|
||||
print "Info: %s looks like an alias domain, skipping it" \
|
||||
% pf_domain.domain
|
||||
continue
|
||||
except pf_models.AliasDomain.DoesNotExist:
|
||||
self._migrate_domain(pf_domain, options, creator)
|
||||
self._migrate_admins(options, creator)
|
||||
|
||||
def handle(self, *args, **options):
|
||||
"""Command entry point."""
|
||||
if options["rename_dirs"] and options["mboxes_path"] is None:
|
||||
print "Error: you must provide the --mboxes-path option"
|
||||
sys.exit(1)
|
||||
with transaction.commit_on_success():
|
||||
self._do_migration(options)
|
||||
@@ -1,214 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# coding: utf-8
|
||||
|
||||
"""
|
||||
Automatic postfixadmin to Modoboa migration
|
||||
===========================================
|
||||
|
||||
This script provides an easy way to migrate an existing postfixadmin
|
||||
database to a Modoboa one.
|
||||
|
||||
As the two products do not share the same schema, some informations
|
||||
will be lost on the new database. Here is the list:
|
||||
* Description, transport and backup MX for domains
|
||||
* Logs
|
||||
* Fetchmail table
|
||||
|
||||
Mailboxes organisation on the filesystem will change. PostfixAdmin
|
||||
uses the following layout::
|
||||
|
||||
<topdir>/domain.tld/user@domain.tld/
|
||||
|
||||
Whereas Modoboa uses the following::
|
||||
|
||||
<topdir>/domain.tld/user/
|
||||
|
||||
To rename directories, you'll need the appropriate permissions.
|
||||
|
||||
Domain administrators that manage more than one domain will not be
|
||||
completly restored. They will only be administrator of the domain that
|
||||
corresponds to their username.
|
||||
|
||||
Vacation messages will not be migrated by this script. Users will have
|
||||
to define new messages by using Modoboa's autoreply plugin. (if
|
||||
activated)
|
||||
|
||||
One last point about passwords. PostfixAdmin uses different hash
|
||||
algorithms to store passwords so this script does not modify
|
||||
them. They are copied "as is" is the new database. In order to use
|
||||
them, you'll have to adapt the ``PASSWORD_SCHEME`` parameter in the
|
||||
admin. panel. Currently, it appears that the ``crypt`` scheme is
|
||||
compatible with PostfixAdmin ``md5crypt`` algorithm...
|
||||
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
from django.db.utils import ConnectionDoesNotExist
|
||||
from django.db.models import Q
|
||||
from django.contrib.auth.models import Group
|
||||
import modoboa.admin.models as md_models
|
||||
import models as pf_models
|
||||
from modoboa.lib.sysutils import exec_cmd
|
||||
from modoboa.lib.emailutils import split_mailbox
|
||||
|
||||
def migrate_dates(oldobj):
|
||||
"""Creates a new ObjectDates instance
|
||||
|
||||
Due to Django limitations, we only retrieve the creation date. The
|
||||
modification date will be set to 'now' because the corresponding
|
||||
field in Modoboa's schema has the 'auto_now' attribute to True.
|
||||
(see https://docs.djangoproject.com/en/dev/ref/models/fields/#django.db.models.DateField).
|
||||
|
||||
:param oldobj: the PostfixAdmin record which is beeing migrated.
|
||||
"""
|
||||
dates = md_models.ObjectDates()
|
||||
dates.save()
|
||||
|
||||
dates.creation = oldobj.created
|
||||
dates.save()
|
||||
return dates
|
||||
|
||||
def migrate_domain_aliases(domain, options):
|
||||
print "\tMigrating domain aliases"
|
||||
old_domain_aliases = pf_models.AliasDomain.objects.using(options._from).filter(target_domain=domain.name)
|
||||
for old_da in old_domain_aliases:
|
||||
new_da = md_models.DomainAlias()
|
||||
new_da.name = old_da.alias_domain
|
||||
new_da.target = old_da.target_domain
|
||||
new_da.enabled = new_da.active
|
||||
new_da.dates = migrate_dates(old_da)
|
||||
new_da.save(using=options.to)
|
||||
|
||||
def migrate_mailbox_aliases(domain, options):
|
||||
print "\tMigrating mailbox aliases"
|
||||
old_aliases = pf_models.Alias.objects.using(options._from).filter(domain=domain.name)
|
||||
for old_al in old_aliases:
|
||||
if old_al.address == old_al.goto:
|
||||
continue
|
||||
new_al = md_models.Alias()
|
||||
local_part, tmp = split_mailbox(old_al.address)
|
||||
if local_part is None or not len(local_part):
|
||||
if tmp is None or not len(tmp):
|
||||
print """Warning: skipping alias %s (cannot retrieve local part).
|
||||
You will need to recreate it manually.
|
||||
""" % old_al.address
|
||||
continue
|
||||
new_al.address = "*"
|
||||
else:
|
||||
new_al.address = local_part
|
||||
new_al.domain = domain
|
||||
new_al.enabled = old_al.active
|
||||
extmboxes = []
|
||||
intmboxes = []
|
||||
for goto in old_al.goto.split(","):
|
||||
try:
|
||||
mb = md_models.Mailbox.objects.using(options.to).get(user__username=goto)
|
||||
except md_models.Mailbox.DoesNotExist:
|
||||
extmboxes += [goto]
|
||||
else:
|
||||
intmboxes += [mb]
|
||||
new_al.dates = migrate_dates(old_al)
|
||||
new_al.save(intmboxes, extmboxes, using=options.to)
|
||||
|
||||
def migrate_mailboxes(domain, options):
|
||||
print "\tMigrating mailboxes"
|
||||
old_mboxes = pf_models.Mailbox.objects.using(options._from).filter(domain=domain.name)
|
||||
for old_mb in old_mboxes:
|
||||
new_mb = md_models.Mailbox()
|
||||
new_mb.address = old_mb.local_part
|
||||
new_mb.domain = domain
|
||||
new_mb.dates = migrate_dates(old_mb)
|
||||
if old_mb.quota:
|
||||
new_mb.quota = old_mb.quota / 1024000
|
||||
|
||||
new_mb.path = "%s/" % old_mb.local_part
|
||||
if options.rename_dirs:
|
||||
oldpath = os.path.join(options.mboxes_path, domain.name, old_mb.maildir)
|
||||
newpath = os.path.join(options.mboxes_path, domain.name, new_mb.path)
|
||||
code, output = exec_cmd("mv %s %s" % (oldpath, newpath))
|
||||
if code:
|
||||
print "Error: cannot rename mailbox directory\n%s" % output
|
||||
sys.exit(1)
|
||||
|
||||
new_mb.save(name=old_mb.name, password=old_mb.password, using=options.to)
|
||||
|
||||
|
||||
def migrate_domain(old_dom, options):
|
||||
print "Migrating domain %s" % old_dom.domain
|
||||
newdom = md_models.Domain()
|
||||
newdom.name = old_dom.domain
|
||||
newdom.enabled = old_dom.active
|
||||
newdom.quota = old_dom.maxquota
|
||||
newdom.dates = migrate_dates(old_dom)
|
||||
newdom.save(using=options.to)
|
||||
migrate_mailboxes(newdom, options)
|
||||
migrate_mailbox_aliases(newdom, options)
|
||||
migrate_domain_aliases(newdom, options)
|
||||
|
||||
def migrate_admins(options):
|
||||
print "Migrating administrators"
|
||||
|
||||
dagroup = Group.objects.using(options.to).get(name="DomainAdmins")
|
||||
for old_admin in pf_models.Admin.objects.using(options._from).all():
|
||||
local_part, domname = split_mailbox(old_admin.username)
|
||||
try:
|
||||
query = Q(username=old_admin.username) & \
|
||||
(Q(domain="ALL") | Q(domain=domname))
|
||||
creds = pf_models.DomainAdmins.objects.using(options._from).get(query)
|
||||
except pf_models.DomainAdmins.DoesNotExist:
|
||||
print "Warning: skipping useless admin %s" % (old_admin.username)
|
||||
continue
|
||||
try:
|
||||
user = md_models.User.objects.using(options.to).get(username=old_admin.username)
|
||||
except md_models.User.DoesNotExist:
|
||||
try:
|
||||
domain = md_models.Domain.objects.using(options.to).get(name=domname)
|
||||
except md_models.Domain.DoesNotExist:
|
||||
print "Warning: skipping domain admin %s, domain not found" \
|
||||
% old_admin.username
|
||||
continue
|
||||
user = md_models.User()
|
||||
user.username = old_admin.username
|
||||
user.email = old_admin.username
|
||||
user.password = old_admin.password
|
||||
user.is_active = old_admin.active
|
||||
user.save(using=options.to)
|
||||
|
||||
user.date_joined = old_admin.modified
|
||||
if creds.domain == "ALL":
|
||||
user.is_superuser = True
|
||||
else:
|
||||
user.groups.add(dagroup)
|
||||
user.save(using=options.to)
|
||||
|
||||
def do_migration(options):
|
||||
pf_domains = pf_models.Domain.objects.using(options._from).all()
|
||||
for olddom in pf_domains:
|
||||
if olddom.domain == "ALL":
|
||||
continue
|
||||
migrate_domain(olddom, options)
|
||||
migrate_admins(options)
|
||||
|
||||
if __name__ == "__main__":
|
||||
from optparse import OptionParser
|
||||
|
||||
parser = OptionParser()
|
||||
parser.add_option("-f", "--from", dest="_from", default="pfxadmin",
|
||||
help="Name of postfixadmin db connection declared in settings.py")
|
||||
parser.add_option("-t", "--to", default="default",
|
||||
help="Name of the Modoboa db connection declared in settings.py")
|
||||
parser.add_option("-r", "--rename-dirs", action="store_true",
|
||||
help="Rename mailbox directories (default is no)")
|
||||
parser.add_option("-p", "--mboxes-path", default=None,
|
||||
help="Path where directories are stored on the filesystem (used only if --rename-dirs)")
|
||||
|
||||
options, args = parser.parse_args()
|
||||
|
||||
if options.rename_dirs and options.mboxes_path is None:
|
||||
print "Error: you must provide the --mboxes-path option"
|
||||
sys.exit(1)
|
||||
|
||||
try:
|
||||
do_migration(options)
|
||||
except ConnectionDoesNotExist, e:
|
||||
print e
|
||||
Reference in New Issue
Block a user