diff --git a/gnuviechadmin/managemails/admin.py b/gnuviechadmin/managemails/admin.py
index 8c38f3f..6aef221 100644
--- a/gnuviechadmin/managemails/admin.py
+++ b/gnuviechadmin/managemails/admin.py
@@ -1,3 +1,140 @@
+from django.utils.html import format_html
from django.contrib import admin
+from django import forms
+from django.forms.util import flatatt
+from django.utils.translation import ugettext as _
-# Register your models here.
+from .models import (
+ MailDomain,
+ Mailbox,
+ MailAddress
+)
+
+PASSWORD_MISMATCH_ERROR = _("Passwords don't match")
+
+
+class ReadOnlyPasswordHashWidget(forms.Widget):
+ def render(self, name, value, attrs):
+ final_attrs = self.build_attrs(attrs)
+ summary = format_html("{0}: {1} ",
+ _('Hash'), value)
+ return format_html("
{1}
", flatatt(final_attrs), summary)
+
+
+class ReadOnlyPasswordHashField(forms.Field):
+ widget = ReadOnlyPasswordHashWidget
+
+ def __init__(self, *args, **kwargs):
+ kwargs.setdefault("required", False)
+ super(ReadOnlyPasswordHashField, self).__init__(*args, **kwargs)
+
+ def bound_data(self, data, initial):
+ return initial
+
+ def _has_changed(self, initial, data):
+ return False
+
+
+class MailboxCreationForm(forms.ModelForm):
+ """
+ A form for creating mailboxes.
+
+ """
+ password1 = forms.CharField(label=_('Password'),
+ widget=forms.PasswordInput)
+ password2 = forms.CharField(label=_('Password (again)'),
+ widget=forms.PasswordInput)
+
+ class Meta:
+ model = Mailbox
+ fields = ('username', 'domain')
+
+ def clean_password2(self):
+ """
+ Check that the two password entries match.
+
+ """
+ password1 = self.cleaned_data.get('password1')
+ password2 = self.cleaned_data.get('password2')
+ if password1 and password2 and password1 != password2:
+ raise forms.ValidationError(PASSWORD_MISMATCH_ERROR)
+ return password2
+
+ def save(self, commit=True):
+ """
+ Save the provided password in hashed format.
+
+ """
+ mailbox = super(MailboxCreationForm, self).save(commit=False)
+ mailbox.set_password(self.cleaned_data['password1'])
+ mailbox.uid = 0
+ mailbox.gid = 0
+ if commit:
+ mailbox.save()
+ return mailbox
+
+
+class MailboxChangeForm(forms.ModelForm):
+ """
+ A form for updating mailboxes.
+
+ """
+ password = ReadOnlyPasswordHashField()
+
+ class Meta:
+ model = Mailbox
+ fields = ('username', 'domain', 'password', 'home', 'uid', 'gid',
+ 'active')
+
+ def clean_password(self):
+ return self.initial['password']
+
+
+class MailboxAdmin(admin.ModelAdmin):
+ """
+ Custom admin page for mailboxes.
+
+ """
+ form = MailboxChangeForm
+ add_form = MailboxCreationForm
+
+ list_display = ('username', 'domain', 'active')
+ list_filter = ('active',)
+ fieldsets = (
+ (None, {
+ 'fields': ('username', 'domain', 'password', 'active')}),
+ (_('System'), {
+ 'fields': ('home', 'uid', 'gid')}),
+ )
+ add_fieldsets = (
+ (None, {
+ 'classes': ('wide',),
+ 'fields': ('username', 'domain', 'password1', 'password2')}),
+ )
+ search_fields = ('username', 'domain')
+ ordering = ('username', 'domain')
+ filter_horizontal = ()
+
+ def get_fieldsets(self, request, obj=None):
+ if not obj:
+ return self.add_fieldsets
+ return super(MailboxAdmin, self).get_fieldsets(request, obj)
+
+ def get_form(self, request, obj=None, **kwargs):
+ """
+ Use special form during mailbox creation.
+
+ """
+ defaults = {}
+ if obj is None:
+ defaults.update({
+ 'form': self.add_form,
+ 'fields': admin.util.flatten_fieldsets(self.add_fieldsets),
+ })
+ defaults.update(kwargs)
+ return super(MailboxAdmin, self).get_form(request, obj, **defaults)
+
+
+admin.site.register(MailDomain)
+admin.site.register(Mailbox, MailboxAdmin)
+admin.site.register(MailAddress)
diff --git a/gnuviechadmin/managemails/migrations/0001_initial.py b/gnuviechadmin/managemails/migrations/0001_initial.py
index 50429b6..f170f2c 100644
--- a/gnuviechadmin/managemails/migrations/0001_initial.py
+++ b/gnuviechadmin/managemails/migrations/0001_initial.py
@@ -8,11 +8,18 @@ from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
+ # Adding model 'MailDomain'
+ db.create_table(u'managemails_maildomain', (
+ (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('domain', self.gf('django.db.models.fields.CharField')(unique=True, max_length=128)),
+ ))
+ db.send_create_signal(u'managemails', ['MailDomain'])
+
# Adding model 'Mailbox'
db.create_table(u'managemails_mailbox', (
(u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
- ('username', self.gf('django.db.models.fields.CharField')(max_length=128)),
- ('domain', self.gf('django.db.models.fields.CharField')(max_length=128)),
+ ('username', self.gf('django.db.models.fields.CharField')(unique=True, max_length=128)),
+ ('domain', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['managemails.MailDomain'])),
('password', self.gf('django.db.models.fields.CharField')(max_length=64)),
('home', self.gf('django.db.models.fields.CharField')(max_length=255)),
('uid', self.gf('django.db.models.fields.PositiveSmallIntegerField')()),
@@ -21,23 +28,102 @@ class Migration(SchemaMigration):
))
db.send_create_signal(u'managemails', ['Mailbox'])
+ # Adding model 'MailAddress'
+ db.create_table(u'managemails_mailaddress', (
+ (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('localpart', self.gf('django.db.models.fields.CharField')(max_length=128)),
+ ('domain', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['managemails.MailDomain'])),
+ ('active', self.gf('django.db.models.fields.BooleanField')(default=True)),
+ ))
+ db.send_create_signal(u'managemails', ['MailAddress'])
+
+ # Adding unique constraint on 'MailAddress', fields ['localpart', 'domain']
+ db.create_unique(u'managemails_mailaddress', ['localpart', 'domain_id'])
+
+ # Adding model 'MailAddressMailbox'
+ db.create_table(u'managemails_mailaddressmailbox', (
+ (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('mailaddress', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['managemails.MailAddress'])),
+ ('mailbox', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['managemails.Mailbox'])),
+ ))
+ db.send_create_signal(u'managemails', ['MailAddressMailbox'])
+
+ # Adding unique constraint on 'MailAddressMailbox', fields ['mailaddress', 'mailbox']
+ db.create_unique(u'managemails_mailaddressmailbox', ['mailaddress_id', 'mailbox_id'])
+
+ # Adding model 'MailAddressForward'
+ db.create_table(u'managemails_mailaddressforward', (
+ (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('mailaddress', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['managemails.MailAddress'])),
+ ('target', self.gf('django.db.models.fields.EmailField')(max_length=254)),
+ ))
+ db.send_create_signal(u'managemails', ['MailAddressForward'])
+
+ # Adding unique constraint on 'MailAddressForward', fields ['mailaddress', 'target']
+ db.create_unique(u'managemails_mailaddressforward', ['mailaddress_id', 'target'])
+
def backwards(self, orm):
+ # Removing unique constraint on 'MailAddressForward', fields ['mailaddress', 'target']
+ db.delete_unique(u'managemails_mailaddressforward', ['mailaddress_id', 'target'])
+
+ # Removing unique constraint on 'MailAddressMailbox', fields ['mailaddress', 'mailbox']
+ db.delete_unique(u'managemails_mailaddressmailbox', ['mailaddress_id', 'mailbox_id'])
+
+ # Removing unique constraint on 'MailAddress', fields ['localpart', 'domain']
+ db.delete_unique(u'managemails_mailaddress', ['localpart', 'domain_id'])
+
+ # Deleting model 'MailDomain'
+ db.delete_table(u'managemails_maildomain')
+
# Deleting model 'Mailbox'
db.delete_table(u'managemails_mailbox')
+ # Deleting model 'MailAddress'
+ db.delete_table(u'managemails_mailaddress')
+
+ # Deleting model 'MailAddressMailbox'
+ db.delete_table(u'managemails_mailaddressmailbox')
+
+ # Deleting model 'MailAddressForward'
+ db.delete_table(u'managemails_mailaddressforward')
+
models = {
+ u'managemails.mailaddress': {
+ 'Meta': {'unique_together': "(('localpart', 'domain'),)", 'object_name': 'MailAddress'},
+ 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['managemails.MailDomain']"}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'localpart': ('django.db.models.fields.CharField', [], {'max_length': '128'})
+ },
+ u'managemails.mailaddressforward': {
+ 'Meta': {'unique_together': "(('mailaddress', 'target'),)", 'object_name': 'MailAddressForward'},
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'mailaddress': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['managemails.MailAddress']"}),
+ 'target': ('django.db.models.fields.EmailField', [], {'max_length': '254'})
+ },
+ u'managemails.mailaddressmailbox': {
+ 'Meta': {'unique_together': "(('mailaddress', 'mailbox'),)", 'object_name': 'MailAddressMailbox'},
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'mailaddress': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['managemails.MailAddress']"}),
+ 'mailbox': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['managemails.Mailbox']"})
+ },
u'managemails.mailbox': {
'Meta': {'object_name': 'Mailbox'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
- 'domain': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['managemails.MailDomain']"}),
'gid': ('django.db.models.fields.PositiveSmallIntegerField', [], {}),
'home': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
'uid': ('django.db.models.fields.PositiveSmallIntegerField', [], {}),
- 'username': ('django.db.models.fields.CharField', [], {'max_length': '128'})
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'})
+ },
+ u'managemails.maildomain': {
+ 'Meta': {'object_name': 'MailDomain'},
+ 'domain': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
}
}
diff --git a/gnuviechadmin/managemails/migrations/0002_auto__chg_field_mailbox_password.py b/gnuviechadmin/managemails/migrations/0002_auto__chg_field_mailbox_password.py
new file mode 100644
index 0000000..deaf3cd
--- /dev/null
+++ b/gnuviechadmin/managemails/migrations/0002_auto__chg_field_mailbox_password.py
@@ -0,0 +1,58 @@
+# -*- coding: utf-8 -*-
+from south.utils import datetime_utils as datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+
+ # Changing field 'Mailbox.password'
+ db.alter_column(u'managemails_mailbox', 'password', self.gf('django.db.models.fields.CharField')(max_length=255))
+
+ def backwards(self, orm):
+
+ # Changing field 'Mailbox.password'
+ db.alter_column(u'managemails_mailbox', 'password', self.gf('django.db.models.fields.CharField')(max_length=64))
+
+ models = {
+ u'managemails.mailaddress': {
+ 'Meta': {'unique_together': "(('localpart', 'domain'),)", 'object_name': 'MailAddress'},
+ 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['managemails.MailDomain']"}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'localpart': ('django.db.models.fields.CharField', [], {'max_length': '128'})
+ },
+ u'managemails.mailaddressforward': {
+ 'Meta': {'unique_together': "(('mailaddress', 'target'),)", 'object_name': 'MailAddressForward'},
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'mailaddress': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['managemails.MailAddress']"}),
+ 'target': ('django.db.models.fields.EmailField', [], {'max_length': '254'})
+ },
+ u'managemails.mailaddressmailbox': {
+ 'Meta': {'unique_together': "(('mailaddress', 'mailbox'),)", 'object_name': 'MailAddressMailbox'},
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'mailaddress': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['managemails.MailAddress']"}),
+ 'mailbox': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['managemails.Mailbox']"})
+ },
+ u'managemails.mailbox': {
+ 'Meta': {'object_name': 'Mailbox'},
+ 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['managemails.MailDomain']"}),
+ 'gid': ('django.db.models.fields.PositiveSmallIntegerField', [], {}),
+ 'home': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'uid': ('django.db.models.fields.PositiveSmallIntegerField', [], {}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'})
+ },
+ u'managemails.maildomain': {
+ 'Meta': {'object_name': 'MailDomain'},
+ 'domain': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
+ }
+ }
+
+ complete_apps = ['managemails']
\ No newline at end of file
diff --git a/gnuviechadmin/managemails/models.py b/gnuviechadmin/managemails/models.py
index 6e5e9bb..6cf7a81 100644
--- a/gnuviechadmin/managemails/models.py
+++ b/gnuviechadmin/managemails/models.py
@@ -1,11 +1,64 @@
from django.db import models
+from django.utils.encoding import python_2_unicode_compatible
+from django.utils.translation import ugettext as _
+from passlib.hash import sha512_crypt
+
+
+@python_2_unicode_compatible
+class MailDomain(models.Model):
+ domain = models.CharField(max_length=128, unique=True)
+
+ class Meta:
+ verbose_name = _('Mail domain')
+ verbose_name_plural = _('Mail domains')
+
+ def __str__(self):
+ return self.domain
class Mailbox(models.Model):
- username = models.CharField(max_length=128)
- domain = models.CharField(max_length=128)
- password = models.CharField(max_length=64)
+ username = models.CharField(max_length=128, unique=True)
+ domain = models.ForeignKey(MailDomain)
+ password = models.CharField(max_length=255)
home = models.CharField(max_length=255)
uid = models.PositiveSmallIntegerField()
gid = models.PositiveSmallIntegerField()
active = models.BooleanField(default=True)
+
+ class Meta:
+ verbose_name = _('Mailbox')
+ verbose_name_plural = _('Mailboxes')
+
+ def set_password(self, password):
+ self.password = sha512_crypt.encrypt(password)
+
+
+@python_2_unicode_compatible
+class MailAddress(models.Model):
+ localpart = models.CharField(max_length=128)
+ domain = models.ForeignKey(MailDomain)
+ active = models.BooleanField(default=True)
+
+ class Meta:
+ unique_together = ('localpart', 'domain')
+ verbose_name = _('Mail address')
+ verbose_name_plural = _('Mail addresses')
+
+ def __str__(self):
+ return "{0}@{1}".format(self.localpart, self.domain)
+
+
+class MailAddressMailbox(models.Model):
+ mailaddress = models.ForeignKey(MailAddress)
+ mailbox = models.ForeignKey(Mailbox)
+
+ class Meta:
+ unique_together = ('mailaddress', 'mailbox')
+
+
+class MailAddressForward(models.Model):
+ mailaddress = models.ForeignKey(MailAddress)
+ target = models.EmailField(max_length=254)
+
+ class Meta:
+ unique_together = ('mailaddress', 'target')
diff --git a/requirements/base.txt b/requirements/base.txt
index 51d537e..2cedcab 100644
--- a/requirements/base.txt
+++ b/requirements/base.txt
@@ -5,3 +5,4 @@ django-model-utils==2.0.3
logutils==0.3.3
South==0.8.4
psycopg2==2.5.3
+passlib==1.6.2