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 @@
+
+
+
+
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 %}
-
-a
-