Cosmetics

This commit is contained in:
Marc 2014-09-30 10:20:11 +00:00
parent 8a0a73a640
commit 4a7ac71a38
20 changed files with 115 additions and 91 deletions

View file

@ -5,14 +5,8 @@ from django.utils.translation import ugettext_lazy as _
from orchestra.core.validators import validate_password
from orchestra.forms.widgets import ReadOnlyWidget
from .models import Account
class AccountCreationForm(auth.forms.UserCreationForm):
# class Meta:
# model = Account
# fields = ("username",)
def __init__(self, *args, **kwargs):
super(AccountCreationForm, self).__init__(*args, **kwargs)
self.fields['password1'].validators.append(validate_password)
@ -21,8 +15,9 @@ class AccountCreationForm(auth.forms.UserCreationForm):
# Since model.clean() will check this, this is redundant,
# but it sets a nicer error message than the ORM and avoids conflicts with contrib.auth
username = self.cleaned_data["username"]
if hasattr(Account, 'systemusers'):
systemuser_model = Account.systemusers.related.model
account_model = self._meta.model
if hasattr(account_model, 'systemusers'):
systemuser_model = account_model.systemusers.related.model
if systemuser_model.objects.filter(username=username).exists():
raise forms.ValidationError(self.error_messages['duplicate_username'])
return username

View file

@ -55,8 +55,8 @@ class Bill(models.Model):
type = models.CharField(_("type"), max_length=16, choices=TYPES)
created_on = models.DateField(_("created on"), auto_now_add=True)
closed_on = models.DateField(_("closed on"), blank=True, null=True)
is_open = models.BooleanField(_("is open"), default=True)
is_sent = models.BooleanField(_("is sent"), default=False)
is_open = models.BooleanField(_("open"), default=True)
is_sent = models.BooleanField(_("sent"), default=False)
due_on = models.DateField(_("due on"), null=True, blank=True)
updated_on = models.DateField(_("updated on"), auto_now=True)
total = models.DecimalField(max_digits=12, decimal_places=2, default=0)

View file

@ -40,7 +40,7 @@ class Role(models.Model):
related_name='roles')
user = models.ForeignKey('databases.DatabaseUser', verbose_name=_("user"),
related_name='roles')
is_owner = models.BooleanField(_("is owener"), default=False)
is_owner = models.BooleanField(_("owner"), default=False)
class Meta:
unique_together = ('database', 'user')

View file

@ -20,7 +20,7 @@ class Mailbox(models.Model):
validators=[validators.validate_sieve],
help_text=_("Arbitrary email filtering in sieve language. "
"This overrides any automatic junk email filtering"))
is_active = models.BooleanField(_("is active"), default=True)
is_active = models.BooleanField(_("active"), default=True)
# addresses = models.ManyToManyField('mails.Address',
# verbose_name=_("addresses"),
# related_name='mailboxes', blank=True)

View file

@ -6,11 +6,11 @@ from orchestra.core import services
class MiscService(models.Model):
name = models.CharField(_("name"), max_length=256)
description = models.TextField(blank=True)
has_amount = models.BooleanField(default=False,
description = models.TextField(_("description"), blank=True)
has_amount = models.BooleanField(_("has amount"), default=False,
help_text=_("Designates whether this service has <tt>amount</tt> "
"property or not."))
is_active = models.BooleanField(default=True,
is_active = models.BooleanField(_("active"), default=True,
help_text=_("Whether new instances of this service can be created "
"or not. Unselect this instead of deleting services."))
@ -25,7 +25,7 @@ class Miscellaneous(models.Model):
related_name='miscellaneous')
description = models.TextField(_("description"), blank=True)
amount = models.PositiveIntegerField(_("amount"), default=1)
is_active = models.BooleanField(default=True,
is_active = models.BooleanField(_("active"), default=True,
help_text=_("Designates whether this service should be treated as "
"active. Unselect this instead of deleting services."))

View file

@ -136,7 +136,7 @@ class Route(models.Model):
# async = models.BooleanField(default=False)
# method = models.CharField(_("method"), max_lenght=32, choices=method_choices,
# default=MethodBackend.get_default())
is_active = models.BooleanField(_("is active"), default=True)
is_active = models.BooleanField(_("active"), default=True)
class Meta:
unique_together = ('backend', 'host')

View file

@ -22,7 +22,7 @@ class PaymentSource(models.Model):
method = models.CharField(_("method"), max_length=32,
choices=PaymentMethod.get_plugin_choices())
data = JSONField(_("data"))
is_active = models.BooleanField(_("is active"), default=True)
is_active = models.BooleanField(_("active"), default=True)
objects = PaymentSourcesQueryset.as_manager()

View file

@ -65,7 +65,7 @@ class Resource(models.Model):
monitors = fields.MultiSelectField(_("monitors"), max_length=256, blank=True,
choices=ServiceMonitor.get_plugin_choices(),
help_text=_("Monitor backends used for monitoring this resource."))
is_active = models.BooleanField(_("is active"), default=True)
is_active = models.BooleanField(_("active"), default=True)
objects = ResourceQuerySet.as_manager()

View file

@ -18,9 +18,9 @@ from .handlers import ServiceHandler
class Plan(models.Model):
name = models.CharField(_("plan"), max_length=128)
is_default = models.BooleanField(_("is default"), default=False)
is_combinable = models.BooleanField(_("is combinable"), default=True)
allow_multiple = models.BooleanField(_("allow multipls"), default=False)
is_default = models.BooleanField(_("default"), default=False)
is_combinable = models.BooleanField(_("combinable"), default=True)
allow_multiple = models.BooleanField(_("allow multiple"), default=False)
def __unicode__(self):
return self.name
@ -107,7 +107,7 @@ class Service(models.Model):
"enables customized behaviour far beyond what options "
"here allow to."),
choices=ServiceHandler.get_plugin_choices())
is_active = models.BooleanField(_("is active"), default=True)
is_active = models.BooleanField(_("active"), default=True)
# Billing
billing_period = models.CharField(_("billing period"), max_length=16,
help_text=_("Renewal period for recurring invoicing"),
@ -133,7 +133,7 @@ class Service(models.Model):
# (ONE_MONTH, _("One month")),
# ),
# default=ONE_MONTH, blank=True)
is_fee = models.BooleanField(_("is fee"), default=False,
is_fee = models.BooleanField(_("fee"), default=False,
help_text=_("Designates whether this service should be billed as "
" membership fee or not"))
# Pricing

View file

@ -17,7 +17,7 @@ class SystemUserAdmin(AccountAdminMixin, ExtendedModelAdmin):
list_filter = ('is_active',)
fieldsets = (
(None, {
'fields': ('username', 'password', 'is_active', 'account_link')
'fields': ('username', 'password', 'account_link', 'is_active')
}),
(_("System"), {
'fields': ('home', 'shell', 'groups'),

View file

@ -5,14 +5,9 @@ from django.utils.translation import ugettext, ugettext_lazy as _
from orchestra.apps.accounts.models import Account
from orchestra.core.validators import validate_password
from .models import SystemUser
# TODO orchestra.UserCretionForm
class UserCreationForm(auth.forms.UserCreationForm):
# class Meta:
# model = SystemUser
# fields = ('username',)
def __init__(self, *args, **kwargs):
super(UserCreationForm, self).__init__(*args, **kwargs)
self.fields['password1'].validators.append(validate_password)
@ -21,12 +16,13 @@ class UserCreationForm(auth.forms.UserCreationForm):
# Since model.clean() will check this, this is redundant,
# but it sets a nicer error message than the ORM and avoids conflicts with contrib.auth
username = self.cleaned_data["username"]
account_model = SystemUser.account.field.rel.to
account_model = self._meta.model.account.field.rel.to
if account_model.objects.filter(username=username).exists():
raise forms.ValidationError(self.error_messages['duplicate_username'])
return username
# TODO orchestra.UserCretionForm
class UserChangeForm(forms.ModelForm):
password = auth.forms.ReadOnlyPasswordHashField(label=_("Password"),
help_text=_("Raw passwords are not stored, so there is no way to see "

View file

@ -26,7 +26,7 @@ class SystemUser(models.Model):
account = models.ForeignKey('accounts.Account', verbose_name=_("Account"),
related_name='systemusers')
home = models.CharField(_("home"), max_length=256, blank=True,
help_text=_("Home directory relative to account's ~primary_user"))
help_text=_("Home directory relative to account's ~main_user"))
shell = models.CharField(_("shell"), max_length=32,
choices=settings.USERS_SHELLS, default=settings.USERS_DEFAULT_SHELL)
groups = models.ManyToManyField('systemusers.Group', blank=True,

View file

@ -8,9 +8,10 @@ USERS_SYSTEMUSER_HOME = getattr(settings, 'USERES_SYSTEMUSER_HOME', '/home/%(use
USERS_FTP_LOG_PATH = getattr(settings, 'USERS_FTP_LOG_PATH', '/var/log/vsftpd.log')
USERS_SHELLS = getattr(settings, 'USERS_SHELLS', (
('/bin/false', _("FTP/sFTP only")),
('/bin/rsync', _("rsync shell")),
('/bin/bash', "Bash"),
('/bin/false', _("No shell, FTP only")),
('/bin/rsync', _("No shell, SFTP/RSYNC only")),
('/bin/bash', "/bin/bash"),
('/bin/sh', "/bin/sh"),
))
USERS_DEFAULT_SHELL = getattr(settings, 'USERS_DEFAULT_SHELL', '/bin/false')

View file

@ -11,23 +11,21 @@ from orchestra.apps.accounts.admin import AccountAdminMixin
from .forms import UserCreationForm, UserChangeForm
from .models import User
from .roles.filters import role_list_filter_factory
class UserAdmin(AccountAdminMixin, auth.UserAdmin, ExtendedModelAdmin):
list_display = ('username', 'account_link', 'is_main', 'is_superuser', 'is_active')
list_filter = ('is_main', 'is_superuser', 'is_active')
list_display = ('username', 'display_is_main')
list_filter = ('is_staff', 'is_superuser', 'is_active')
fieldsets = (
(None, {
'fields': ('username', 'password', 'account_link')
}),
(_("System"), {
'fields': ('home', 'shell', 'groups'),
'fields': ('account', 'username', 'password')
}),
(_("Personal info"), {
'fields': ('first_name', 'last_name', 'email')
}),
(_("Permissions"), {
'fields': ('is_main', 'is_active', 'is_superuser')
'fields': ('is_active', 'is_staff', 'is_superuser', 'display_is_main')
}),
(_("Important dates"), {
'fields': ('last_login', 'date_joined')
@ -39,25 +37,74 @@ class UserAdmin(AccountAdminMixin, auth.UserAdmin, ExtendedModelAdmin):
'fields': ('username', 'password1', 'password2', 'account'),
}),
)
search_fields = ['username']
readonly_fields = ('is_main', 'account_link',)
search_fields = ['username', 'account__user__username']
readonly_fields = ('display_is_main', 'account_link')
change_readonly_fields = ('username',)
filter_horizontal = ()
filter_by_account_fields = ('groups',)
add_form = UserCreationForm
form = UserChangeForm
roles = []
ordering = ('-id',)
change_form_template = 'admin/users/user/change_form.html'
def get_form(self, request, obj=None, **kwargs):
""" exclude self reference on groups """
form = super(AccountAdminMixin, self).get_form(request, obj=obj, **kwargs)
if obj:
# Has to be done here and not in the form because of strange phenomenon
# derived from monkeypatching formfield.widget.render on AccountAdminMinxin,
# don't ask.
formfield = form.base_fields['groups']
formfield.queryset = formfield.queryset.exclude(id=obj.id)
return form
def display_is_main(self, instance):
return instance.is_main
display_is_main.short_description = _("is main")
display_is_main.boolean = True
def get_urls(self):
""" Returns the additional urls for the change view links """
urls = super(UserAdmin, self).get_urls()
opts = self.model._meta
new_urls = patterns("")
for role in self.roles:
new_urls += patterns("",
url('^(\d+)/%s/$' % role.url_name,
wrap_admin_view(self, role().change_view),
name='%s_%s_%s_change' % (opts.app_label, opts.model_name, role.name)),
url('^(\d+)/%s/delete/$' % role.url_name,
wrap_admin_view(self, role().delete_view),
name='%s_%s_%s_delete' % (opts.app_label, opts.model_name, role.name))
)
return new_urls + urls
def get_fieldsets(self, request, obj=None):
fieldsets = super(UserAdmin, self).get_fieldsets(request, obj=obj)
if obj and obj.account:
fieldsets[0][1]['fields'] = ('account_link',) + fieldsets[0][1]['fields'][1:]
return fieldsets
def get_list_display(self, request):
roles = []
for role in self.roles:
def has_role(user, role_class=role):
role = role_class(user=user)
if role.exists:
return '<img src="/static/admin/img/icon-yes.gif" alt="True">'
url = reverse('admin:users_user_%s_change' % role.name, args=(user.pk,))
false = '<img src="/static/admin/img/icon-no.gif" alt="False">'
return '<a href="%s">%s</a>' % (url, false)
has_role.short_description = _("Has %s") % role.name
has_role.admin_order_field = role.name
has_role.allow_tags = True
roles.append(has_role)
return list(self.list_display) + roles + ['account_link']
def get_list_filter(self, request):
roles = [ role_list_filter_factory(role) for role in self.roles ]
return list(self.list_filter) + roles
def change_view(self, request, object_id, **kwargs):
user = self.get_object(User, unquote(object_id))
extra_context = kwargs.get('extra_context', {})
extra_context['roles'] = [ role(user=user) for role in self.roles ]
kwargs['extra_context'] = extra_context
return super(UserAdmin, self).change_view(request, object_id, **kwargs)
def get_queryset(self, request):
""" Select related for performance """
related = ['account__user'] + [ role.name for role in self.roles ]
return super(UserAdmin, self).get_queryset(request).select_related(*related)
admin.site.register(User, UserAdmin)

View file

@ -10,6 +10,11 @@ from .serializers import UserSerializer
class UserViewSet(AccountApiMixin, SetPasswordApiMixin, viewsets.ModelViewSet):
model = get_user_model()
serializer_class = UserSerializer
def get_queryset(self):
""" select related roles """
qs = super(UserViewSet, self).get_queryset()
return qs.select_related(*self.inserted)
router.register(r'users', UserViewSet)

View file

@ -47,4 +47,3 @@ class UserChangeForm(forms.ModelForm):
# This is done here, rather than on the field, because the
# field does not have access to the initial value
return self.initial["password"]

View file

@ -7,8 +7,6 @@ from django.utils.translation import ugettext_lazy as _
from orchestra.core import services
from . import settings
class User(auth.AbstractBaseUser):
username = models.CharField(_("username"), max_length=64, unique=True,
@ -16,22 +14,19 @@ class User(auth.AbstractBaseUser):
"./-/_ only."),
validators=[validators.RegexValidator(r'^[\w.-]+$',
_("Enter a valid username."), 'invalid')])
account = models.ForeignKey('accounts.Account', verbose_name=_("Account"), related_name='users')
account = models.ForeignKey('accounts.Account', verbose_name=_("Account"),
related_name='users')
first_name = models.CharField(_("first name"), max_length=30, blank=True)
last_name = models.CharField(_("last name"), max_length=30, blank=True)
email = models.EmailField(_("email address"), blank=True)
email = models.EmailField(_('email address'), blank=True)
is_superuser = models.BooleanField(_("superuser status"), default=False,
help_text=_("Designates that this user has all permissions without "
"explicitly assigning them."))
is_main = models.BooleanField(_("is main"), default=False)
# system_password = models.CharField(_("system password"), max_length=128)
home = models.CharField(_("home"), max_length=256, blank=True,
help_text=_("Home directory relative to account's ~primary_user"))
shell = models.CharField(_("shell"), max_length=32,
choices=settings.USERS_SHELLS, default=settings.USERS_DEFAULT_SHELL)
groups = models.ManyToManyField('self', blank=True,
help_text=_("A new group will be created for the user. "
"Which additional groups would you like them to be a member of?"))
help_text=_("Designates that this user has all permissions without "
"explicitly assigning them."))
is_staff = models.BooleanField(_("staff status"), default=False,
help_text=_("Designates whether the user can log into this admin "
"site."))
is_admin = models.BooleanField(_("admin status"), default=False,
help_text=_("Designates whether the user can administrate its account."))
is_active = models.BooleanField(_("active"), default=True,
help_text=_("Designates whether this user should be treated as "
"active. Unselect this instead of deleting accounts."))
@ -40,11 +35,11 @@ class User(auth.AbstractBaseUser):
objects = auth.UserManager()
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = []
REQUIRED_FIELDS = ['email']
@property
def is_staff(self):
return self.is_superuser or self.is_main
def is_main(self):
return self.account.user == self
def get_full_name(self):
full_name = '%s %s' % (self.first_name, self.last_name)
@ -92,9 +87,6 @@ class User(auth.AbstractBaseUser):
if self.is_active and self.is_superuser:
return True
return auth._user_has_module_perms(self, app_label)
#
# def set_system_password(self, raw_password):
# self.system_password = make_password(raw_password)
services.register(User)
services.register(User, menu=False)

View file

@ -33,4 +33,3 @@ class UserSerializer(AccountSerializerMixin, serializers.HyperlinkedModelSeriali
if not obj.pk:
obj.set_password(obj.password)
super(UserSerializer, self).save_object(obj, **kwargs)

View file

@ -1,16 +1,6 @@
from django.conf import settings
from django.utils.translation import ugettext, ugettext_lazy as _
USERS_SYSTEMUSER_HOME = getattr(settings, 'USERES_SYSTEMUSER_HOME', '/home/%(username)s')
USERS_FTP_LOG_PATH = getattr(settings, 'USERS_FTP_LOG_PATH', '/var/log/vsftpd.log')
USERS_SHELLS = getattr(settings, 'USERS_SHELLS', (
('/bin/false', _("FTP/sFTP only")),
('/bin/rsync', _("rsync shell")),
('/bin/bash', "Bash"),
))
USERS_DEFAULT_SHELL = getattr(settings, 'USERS_DEFAULT_SHELL', '/bin/false')

View file

@ -28,7 +28,7 @@ class Website(models.Model):
domains = models.ManyToManyField(settings.WEBSITES_DOMAIN_MODEL,
related_name='websites', verbose_name=_("domains"))
contents = models.ManyToManyField('webapps.WebApp', through='websites.Content')
is_active = models.BooleanField(_("is active"), default=True)
is_active = models.BooleanField(_("active"), default=True)
def __unicode__(self):
return self.name