diff --git a/TODO.md b/TODO.md index a12ff9fa..78a04b9f 100644 --- a/TODO.md +++ b/TODO.md @@ -127,3 +127,19 @@ Remember that, as always with QuerySets, any subsequent chained methods which im * prevent deletion of main user by the user itself + + +* AccountAdminMixin auto adds 'account__name' on searchfields and handle account_link on fieldsets + +* account defiition: + * identify a customer or a person + * has one main system user for running website + * pangea staff are different accounts + * An account identify a person + * Maybe merge users into accounts? again. Account contains main_users, users contains FTP shit + * Separate panel from server passwords? + * Store passwords on panel? + + + +* What fields we really need on contacts? name email phone and what more? diff --git a/docs/services.svg b/docs/services.svg new file mode 100644 index 00000000..e06e6422 --- /dev/null +++ b/docs/services.svg @@ -0,0 +1,482 @@ + + + + + + + + + + image/svg+xml + + + + + + + Orders + Metric + Periodicbilling + One-timeservice + Pricingperiod + No pricingperiod + Periodicbilling + One-timeservice + Pricingperiod + No pricingperiod + Pricingperiod + No pricingperiod + Pricingperiod + No pricingperiod + Mail accountsConcurrent (changes)Compensate on prepay + DomainsRegister or renew eventsCompensate on prepay + PlansAlways one order + CMS installationRegister or renew events + Traffic consumptionMetric period lookupPrepay and != billing_period NotImplemented + Mailbox sizeConcurrent (changes) + JobsLast known metric + NotImplement + + + + + + + + + + + + + + diff --git a/orchestra/apps/accounts/admin.py b/orchestra/apps/accounts/admin.py index 189ed31e..ea779c04 100644 --- a/orchestra/apps/accounts/admin.py +++ b/orchestra/apps/accounts/admin.py @@ -9,8 +9,7 @@ from django.utils.six.moves.urllib.parse import parse_qsl from django.utils.translation import ugettext_lazy as _ from orchestra.admin import ExtendedModelAdmin -from orchestra.admin.utils import (wrap_admin_view, admin_link, set_url_query, - change_url) +from orchestra.admin.utils import wrap_admin_view, admin_link, set_url_query, change_url from orchestra.core import services, accounts from .filters import HasMainUserListFilter @@ -19,7 +18,7 @@ from .models import Account class AccountAdmin(auth.UserAdmin, ExtendedModelAdmin): - list_display = ('name', 'type', 'is_active') + list_display = ('username', 'type', 'is_active') list_filter = ( 'type', 'is_active', HasMainUserListFilter ) @@ -27,16 +26,28 @@ class AccountAdmin(auth.UserAdmin, ExtendedModelAdmin): (_("User"), { 'fields': ('username', 'password1', 'password2',), }), - (_("Account info"), { - 'fields': (('type', 'language'), 'comments'), + (_("Personal info"), { + 'fields': ('first_name', 'last_name', 'email', ('type', 'language'), 'comments'), + }), + (_("Permissions"), { + 'fields': ('is_superuser', 'is_active') + }), + (_("Important dates"), { + 'fields': ('last_login', 'date_joined') }), ) fieldsets = ( (_("User"), { - 'fields': ('username', 'password',), + 'fields': ('username', 'password',) }), - (_("Account info"), { - 'fields': (('type', 'language'), 'comments'), + (_("Personal info"), { + 'fields': ('first_name', 'last_name', 'email', ('type', 'language'), 'comments'), + }), + (_("Permissions"), { + 'fields': ('is_superuser', 'is_active') + }), + (_("Important dates"), { + 'fields': ('last_login', 'date_joined') }), ) search_fields = ('username',) @@ -45,10 +56,6 @@ class AccountAdmin(auth.UserAdmin, ExtendedModelAdmin): filter_horizontal = () change_form_template = 'admin/accounts/account/change_form.html' - def name(self, account): - return account.name - name.admin_order_field = 'username' - def formfield_for_dbfield(self, db_field, **kwargs): """ Make value input widget bigger """ if db_field.name == 'comments': @@ -95,7 +102,7 @@ class AccountListAdmin(AccountAdmin): # TODO get query string from request.META['QUERY_STRING'] to preserve filters context = { 'url': '../?account=' + str(instance.pk), - 'name': instance.name + 'name': instance.username } return '%(name)s' % context select_account.short_description = _("account") @@ -143,9 +150,6 @@ class AccountAdminMixin(object): def formfield_for_dbfield(self, db_field, **kwargs): """ Filter by account """ -# if db_field.name == 'account': -# qs = kwargs.get('queryset', db_field.rel.to.objects) -# kwargs['queryset'] = qs.select_related('user') formfield = super(AccountAdminMixin, self).formfield_for_dbfield(db_field, **kwargs) if db_field.name in self.filter_by_account_fields: if hasattr(self, 'account'): @@ -197,7 +201,7 @@ class AccountAdminMixin(object): context.update({ 'all_selected': False, 'title': _("Select %s to change for %s") % ( - opts.verbose_name, account.name), + opts.verbose_name, account.username), }) else: request_copy = request.GET.copy() @@ -247,7 +251,7 @@ class SelectAccountAdminMixin(AccountAdminMixin): self.account = Account.objects.get(**kwargs) opts = self.model._meta context = { - 'title': _("Add %s for %s") % (opts.verbose_name, self.account.name), + 'title': _("Add %s for %s") % (opts.verbose_name, self.account.username), 'from_account': bool(from_account_id), 'account': self.account, 'account_opts': Account._meta, diff --git a/orchestra/apps/accounts/forms.py b/orchestra/apps/accounts/forms.py index e59a574f..ccd8eb16 100644 --- a/orchestra/apps/accounts/forms.py +++ b/orchestra/apps/accounts/forms.py @@ -5,30 +5,27 @@ from django.utils.translation import ugettext_lazy as _ from orchestra.core.validators import validate_password from orchestra.forms.widgets import ReadOnlyWidget -User = auth.get_user_model() +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) def clean_username(self): - # Since User.username is unique, this check is redundant, - # but it sets a nicer error message than the ORM. See #13147. + # 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"] - try: - User._default_manager.get(username=username) - except User.DoesNotExist: - return username - raise forms.ValidationError(self.error_messages['duplicate_username']) - -# def save(self, commit=True): -# account = super(auth.forms.UserCreationForm, self).save(commit=False) -# account.set_password(self.cleaned_data['password1']) -# if commit: -# account.save() -# return account + if hasattr(Account, 'systemusers'): + systemuser_model = Account.systemusers.related.model + if systemuser_model.objects.filter(username=username).exists(): + raise forms.ValidationError(self.error_messages['duplicate_username']) + return username class AccountChangeForm(forms.ModelForm): diff --git a/orchestra/apps/accounts/management/commands/createinitialaccount.py b/orchestra/apps/accounts/management/commands/createinitialaccount.py index 2e1cfa37..63eb9dfa 100644 --- a/orchestra/apps/accounts/management/commands/createinitialaccount.py +++ b/orchestra/apps/accounts/management/commands/createinitialaccount.py @@ -27,4 +27,6 @@ class Command(BaseCommand): email = options.get('email') username = options.get('username') password = options.get('password') - Account.objects.create_user(username, email=email, password=password) + account = Account.objects.create(name=username) + account.main_user = account.users.create_superuser(username, email, password, account=account, is_main=True) + account.save() diff --git a/orchestra/apps/accounts/models.py b/orchestra/apps/accounts/models.py index c8b4b3d1..12e08a13 100644 --- a/orchestra/apps/accounts/models.py +++ b/orchestra/apps/accounts/models.py @@ -16,16 +16,18 @@ class Account(auth.AbstractBaseUser): help_text=_("Required. 30 characters or fewer. Letters, digits and ./-/_ only."), validators=[validators.RegexValidator(r'^[\w.-]+$', _("Enter a valid username."), 'invalid')]) + 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'), help_text=_("Used for password recovery")) type = models.CharField(_("type"), choices=settings.ACCOUNTS_TYPES, max_length=32, default=settings.ACCOUNTS_DEFAULT_TYPE) language = models.CharField(_("language"), max_length=2, choices=settings.ACCOUNTS_LANGUAGES, default=settings.ACCOUNTS_DEFAULT_LANGUAGE) - registered_on = models.DateField(_("registered"), auto_now_add=True) comments = models.TextField(_("comments"), max_length=256, blank=True) - 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) + is_superuser = models.BooleanField(_("superuser status"), default=False, + help_text=_("Designates that this user has all permissions without " + "explicitly assigning them.")) is_active = models.BooleanField(_("active"), default=True, help_text=_("Designates whether this account should be treated as active. " "Unselect this instead of deleting accounts.")) @@ -43,10 +45,6 @@ class Account(auth.AbstractBaseUser): def name(self): return self.username - @property - def is_superuser(self): - return self.pk == settings.ACCOUNTS_MAIN_PK - @property def is_staff(self): return self.is_superuser @@ -55,11 +53,17 @@ class Account(auth.AbstractBaseUser): def get_main(cls): return cls.objects.get(pk=settings.ACCOUNTS_MAIN_PK) + def clean(self): + """ unique usernames between accounts and system users """ + if not self.pk and hasattr(self, 'systemusers'): + if self.systemusers.model.objects.filter(username=self.username).exists(): + raise validators.ValidationError(_("A user with this name already exists")) + def save(self, *args, **kwargs): created = not self.pk super(Account, self).save(*args, **kwargs) - if created: - self.users.create(username=self.username, password=self.password) + if created and hasattr(self, 'groups'): + self.groups.create(name=self.username, account=self) def send_email(self, template, context, contacts=[], attachments=[], html=None): contacts = self.contacts.filter(email_usages=contacts) diff --git a/orchestra/apps/accounts/templates/admin/accounts/account/change_form.html b/orchestra/apps/accounts/templates/admin/accounts/account/change_form.html index 513972c7..2b3f0525 100644 --- a/orchestra/apps/accounts/templates/admin/accounts/account/change_form.html +++ b/orchestra/apps/accounts/templates/admin/accounts/account/change_form.html @@ -21,28 +21,20 @@ {% block object-tools-items %} {% if services %} +
  • {% endif %} {% if accounts %} -