diff --git a/TODO.md b/TODO.md index 952a4cbc..0df6ad02 100644 --- a/TODO.md +++ b/TODO.md @@ -177,3 +177,4 @@ Remember that, as always with QuerySets, any subsequent chained methods which im * ServiceBackend.validate() : used for server paths validation * ServiceBackend.grant_access() : used for granting access * bottom line: allow arbitrary backend methods (underscore method names that are not to be executed?) +* HowTo?? Signals ? what? diff --git a/orchestra/admin/options.py b/orchestra/admin/options.py index 1be4dc71..330c6f62 100644 --- a/orchestra/admin/options.py +++ b/orchestra/admin/options.py @@ -39,8 +39,7 @@ class ChangeListDefaultFilter(object): defaults.append(key) # hack response cl context in order to hook default filter awaearness # into search_form.html template - response = super(ChangeListDefaultFilter, self).changelist_view(request, - extra_context=extra_context) + response = super(ChangeListDefaultFilter, self).changelist_view(request, extra_context) if hasattr(response, 'context_data') and 'cl' in response.context_data: response.context_data['cl'].default_changelist_filters = defaults return response @@ -100,7 +99,7 @@ class ChangeViewActionsMixin(object): kwargs['extra_context'] = {} obj = self.get_object(request, unquote(object_id)) kwargs['extra_context']['object_tools_items'] = [ - action.__dict__ for action in self.get_change_view_actions(obj=obj) + action.__dict__ for action in self.get_change_view_actions(obj) ] return super(ChangeViewActionsMixin, self).change_view(request, object_id, **kwargs) @@ -116,11 +115,11 @@ class ChangeAddFieldsMixin(object): def get_prepopulated_fields(self, request, obj=None): if not obj: - return super(ChangeAddFieldsMixin, self).get_prepopulated_fields(request, obj=obj) + return super(ChangeAddFieldsMixin, self).get_prepopulated_fields(request, obj) return {} def get_readonly_fields(self, request, obj=None): - fields = super(ChangeAddFieldsMixin, self).get_readonly_fields(request, obj=obj) + fields = super(ChangeAddFieldsMixin, self).get_readonly_fields(request, obj) if obj: return fields + self.change_readonly_fields return fields @@ -131,7 +130,7 @@ class ChangeAddFieldsMixin(object): return self.add_fieldsets elif self.add_fields: return [(None, {'fields': self.add_fields})] - return super(ChangeAddFieldsMixin, self).get_fieldsets(request, obj=obj) + return super(ChangeAddFieldsMixin, self).get_fieldsets(request, obj) def get_inline_instances(self, request, obj=None): """ add_inlines and inline.parent_object """ @@ -139,7 +138,7 @@ class ChangeAddFieldsMixin(object): self.inlines = type(self).inlines else: self.inlines = self.add_inlines or self.inlines - inlines = super(ChangeAddFieldsMixin, self).get_inline_instances(request, obj=obj) + inlines = super(ChangeAddFieldsMixin, self).get_inline_instances(request, obj) for inline in inlines: inline.parent_object = obj return inlines @@ -200,7 +199,7 @@ class ChangePasswordAdminMixin(object): related.append(rel) if request.method == 'POST': - form = self.change_password_form(user, request.POST, related=related) + form = self.change_password_form(user, request.POST, related) if form.is_valid(): form.save() change_message = self.construct_change_message(request, form, None) @@ -210,7 +209,7 @@ class ChangePasswordAdminMixin(object): update_session_auth_hash(request, form.user) # This is safe return HttpResponseRedirect('..') else: - form = self.change_password_form(user, related=related) + form = self.change_password_form(user, related) fieldsets = [ (user._meta.verbose_name.capitalize(), { diff --git a/orchestra/api/root.py b/orchestra/api/root.py index 19ada57e..7e48cefe 100644 --- a/orchestra/api/root.py +++ b/orchestra/api/root.py @@ -3,6 +3,7 @@ from rest_framework.response import Response from rest_framework.reverse import reverse from .. import settings +from ..core import services, accounts class APIRoot(views.APIView): @@ -15,6 +16,10 @@ class APIRoot(views.APIView): '<%s>; rel="%s"' % (root_url, 'api-root'), '<%s>; rel="%s"' % (token_url, 'api-get-auth-token'), ] + body = { + 'accountancy': [], + 'services': [], + } if not request.user.is_anonymous(): list_name = '{basename}-list' detail_name = '{basename}-detail' @@ -30,17 +35,34 @@ class APIRoot(views.APIView): kwargs = {} url = reverse(url_name, request=request, format=format, kwargs=kwargs) links.append('<%s>; rel="%s"' % (url, url_name)) + model = viewset.model + group = None + if model in services: + group = 'services' + menu = services[model].menu + elif model in accounts: + group = 'accountancy' + menu = accounts[model].menu + if group and menu: + body[group].append({ + 'url': url, + 'name': basename, + 'verbose_name': model._meta.verbose_name, + 'verbose_name_plural': model._meta.verbose_name_plural, + }) headers = { 'Link': ', '.join(links) } - body = { - name.lower(): getattr(settings, name, None) for name in self.names - } + body.update({ + name.lower(): getattr(settings, name, None) + for name in self.names + }) return Response(body, headers=headers) def metadata(self, request): ret = super(APIRoot, self).metadata(request) ret['settings'] = { - name.lower(): getattr(settings, name, None) for name in self.names + name.lower(): getattr(settings, name, None) + for name in self.names } return ret diff --git a/orchestra/apps/domains/models.py b/orchestra/apps/domains/models.py index 277232bf..71c24043 100644 --- a/orchestra/apps/domains/models.py +++ b/orchestra/apps/domains/models.py @@ -11,11 +11,15 @@ from . import settings, validators, utils class Domain(models.Model): name = models.CharField(_("name"), max_length=256, unique=True, - validators=[validators.validate_domain_name, validators.validate_allowed_domain], - help_text=_("Domain or subdomain name.")) - account = models.ForeignKey('accounts.Account', verbose_name=_("Account"), - related_name='domains', blank=True, help_text=_("Automatically selected for subdomains.")) - top = models.ForeignKey('domains.Domain', null=True, related_name='subdomain_set', editable=False) + help_text=_("Domain or subdomain name."), + validators=[ + validators.validate_domain_name, + validators.validate_allowed_domain + ]) + account = models.ForeignKey('accounts.Account', verbose_name=_("Account"), blank=True, + related_name='domains', help_text=_("Automatically selected for subdomains.")) + top = models.ForeignKey('domains.Domain', null=True, related_name='subdomain_set', + editable=False) serial = models.IntegerField(_("serial"), default=utils.generate_zone_serial, help_text=_("Serial number")) @@ -88,17 +92,24 @@ class Domain(models.Model): # Update serial and insert at 0 value = record.value.split() value[2] = str(self.serial) - records.insert(0, - AttrDict(type=record.SOA, ttl=record.get_ttl(), value=' '.join(value)) - ) + records.insert(0, AttrDict( + type=record.SOA, + ttl=record.get_ttl(), + value=' '.join(value) + )) else: - records.append( - AttrDict(type=record.type, ttl=record.get_ttl(), value=record.value) - ) + records.append(AttrDict( + type=record.type, + ttl=record.get_ttl(), + value=record.value + )) if self.is_top: if Record.NS not in types: for ns in settings.DOMAINS_DEFAULT_NS: - records.append(AttrDict(type=Record.NS, value=ns)) + records.append(AttrDict( + type=Record.NS, + value=ns + )) if Record.SOA not in types: soa = [ "%s." % settings.DOMAINS_DEFAULT_NAME_SERVER, @@ -109,27 +120,42 @@ class Domain(models.Model): settings.DOMAINS_DEFAULT_EXPIRATION, settings.DOMAINS_DEFAULT_MIN_CACHING_TIME ] - records.insert(0, AttrDict(type=Record.SOA, value=' '.join(soa))) + records.insert(0, AttrDict( + type=Record.SOA, + value=' '.join(soa) + )) is_a = not types or Record.A in types or Record.AAAA in types if Record.MX not in types and is_a: for mx in settings.DOMAINS_DEFAULT_MX: - records.append(AttrDict(type=Record.MX, value=mx)) + records.append(AttrDict( + type=Record.MX, + value=mx + )) if (Record.A not in types and Record.AAAA not in types) and is_a: - records.append(AttrDict(type=Record.A, value=settings.DOMAINS_DEFAULT_A)) + records.append(AttrDict( + type=Record.A, + value=settings.DOMAINS_DEFAULT_A + )) result = '' for record in records: name = '{name}.{spaces}'.format( - name=self.name, spaces=' ' * (37-len(self.name)) + name=self.name, + spaces=' ' * (37-len(self.name)) ) ttl = record.get('ttl', settings.DOMAINS_DEFAULT_TTL) ttl = '{spaces}{ttl}'.format( - spaces=' ' * (7-len(ttl)), ttl=ttl + spaces=' ' * (7-len(ttl)), + ttl=ttl ) type = '{type} {spaces}'.format( - type=record.type, spaces=' ' * (7-len(record.type)) + type=record.type, + spaces=' ' * (7-len(record.type)) ) result += '{name} {ttl} IN {type} {value}\n'.format( - name=name, ttl=ttl, type=type, value=record.value + name=name, + ttl=ttl, + type=type, + value=record.value ) return result diff --git a/orchestra/apps/domains/serializers.py b/orchestra/apps/domains/serializers.py index c1fd69f8..2e092647 100644 --- a/orchestra/apps/domains/serializers.py +++ b/orchestra/apps/domains/serializers.py @@ -39,13 +39,7 @@ class DomainSerializer(AccountSerializerMixin, HyperlinkedModelSerializer): """ Checks if everything is consistent """ instance = super(DomainSerializer, self).full_clean(instance) if instance and instance.name: - records = self.init_data['records'] + records = self.init_data.get('records', []) domain = domain_for_validation(instance, records) - try: - validators.validate_zone(domain.render_zone()) - except ValidationError as err: - self._errors = { - 'all': err.message - } - return None + validators.validate_zone(domain.render_zone()) return instance diff --git a/orchestra/apps/miscellaneous/models.py b/orchestra/apps/miscellaneous/models.py index 8309f212..51bfa759 100644 --- a/orchestra/apps/miscellaneous/models.py +++ b/orchestra/apps/miscellaneous/models.py @@ -51,7 +51,7 @@ class Miscellaneous(models.Model): verbose_name_plural = _("miscellaneous") def __unicode__(self): - return self.identifier or str(self.service) + return self.identifier or self.description[:32] or str(self.service) @cached_property def active(self): diff --git a/orchestra/apps/systemusers/admin.py b/orchestra/apps/systemusers/admin.py index ec1a0ff5..a1cf0bc0 100644 --- a/orchestra/apps/systemusers/admin.py +++ b/orchestra/apps/systemusers/admin.py @@ -21,7 +21,9 @@ from .models import SystemUser class SystemUserAdmin(ChangePasswordAdminMixin, SelectAccountAdminMixin, ExtendedModelAdmin): - list_display = ('username', 'account_link', 'shell', 'display_home', 'display_active', 'display_main') + list_display = ( + 'username', 'account_link', 'shell', 'display_home', 'display_active', 'display_main' + ) list_filter = ('is_active', 'shell', IsMainListFilter) fieldsets = ( (None, { diff --git a/orchestra/apps/systemusers/models.py b/orchestra/apps/systemusers/models.py index 9bfaa380..6bb72ffc 100644 --- a/orchestra/apps/systemusers/models.py +++ b/orchestra/apps/systemusers/models.py @@ -24,8 +24,9 @@ class SystemUser(models.Model): """ System users """ username = models.CharField(_("username"), max_length=64, unique=True, help_text=_("Required. 64 characters or fewer. Letters, digits and ./-/_ only."), - validators=[validators.RegexValidator(r'^[\w.-]+$', - _("Enter a valid username."), 'invalid')]) + validators=[ + validators.RegexValidator(r'^[\w.-]+$', _("Enter a valid username.")) + ]) password = models.CharField(_("password"), max_length=128) account = models.ForeignKey('accounts.Account', verbose_name=_("Account"), related_name='systemusers') @@ -82,7 +83,10 @@ class SystemUser(models.Model): return settings.SYSTEMUSERS_HOME % context def get_home(self): - return os.path.join(self.home or self.get_base_home(), self.directory) + return os.path.join( + self.home or self.get_base_home(), + self.directory + ) services.register(SystemUser) diff --git a/orchestra/core/__init__.py b/orchestra/core/__init__.py index 496c0fbc..bdd068f1 100644 --- a/orchestra/core/__init__.py +++ b/orchestra/core/__init__.py @@ -1,3 +1,6 @@ +from ..utils.python import AttrDict + + class Register(object): def __init__(self): self._registry = {} @@ -5,17 +8,22 @@ class Register(object): def __contains__(self, key): return key in self._registry + def __getitem__(self, key): + return self._registry[key] + def register(self, model, **kwargs): if model in self._registry: raise KeyError("%s already registered" % str(model)) plural = kwargs.get('verbose_name_plural', model._meta.verbose_name_plural) - self._registry[model] = { + self._registry[model] = AttrDict(**{ 'verbose_name': kwargs.get('verbose_name', model._meta.verbose_name), 'verbose_name_plural': plural, 'menu': kwargs.get('menu', True) - } + }) - def get(self): + def get(self, *args): + if args: + return self._registry[arg[0]] return self._registry