Allow add domain aliases.

This implies some minor refactor of domains app code.
remotes/origin/enhancement/email-actions
Andrey Antukh 2014-03-22 15:49:05 +01:00
parent a3b8362a9e
commit cbcf9dddc3
5 changed files with 186 additions and 10 deletions

View File

@ -7,8 +7,8 @@ from rest_framework.permissions import AllowAny, IsAuthenticated
from django.http import Http404 from django.http import Http404
from taiga.base.api import ModelCrudViewSet, UpdateModelMixin from taiga.base.api import ModelCrudViewSet, UpdateModelMixin
from taiga.base.domains import get_active_domain
from .base import get_active_domain
from .serializers import DomainSerializer, DomainMemberSerializer from .serializers import DomainSerializer, DomainMemberSerializer
from .permissions import DomainMembersPermission, DomainPermission from .permissions import DomainMembersPermission, DomainPermission
from .models import DomainMember, Domain from .models import DomainMember, Domain

View File

@ -0,0 +1,75 @@
# -*- coding: utf-8 -*-
import logging
import functools
import threading
from django.db.models import get_model
from django.core.exceptions import ImproperlyConfigured
from django.utils.translation import ugettext_lazy as _
from taiga.base import exceptions as exc
log = logging.getLogger("taiga.domains")
_local = threading.local()
class DomainNotFound(exc.BaseException):
pass
@functools.lru_cache(maxsize=1)
def get_default_domain():
from django.conf import settings
try:
sid = settings.DOMAIN_ID
except AttributeError:
raise ImproperlyConfigured("You're using the \"domains framework\" without having "
"set the DOMAIN_ID setting. Create a domain in your database "
"and set the DOMAIN_ID setting to fix this error.")
model_cls = get_model("domains", "Domain")
try:
return model_cls.objects.get(pk=sid)
except model_cls.DoesNotExist:
raise ImproperlyConfigured("default domain not found on database.")
@functools.lru_cache(maxsize=100, typed=True)
def get_domain_for_domain_name(domain:str, follow_alias:bool=True):
log.debug("Trying activate domain for domain name: {}".format(domain))
model_cls = get_model("domains", "Domain")
try:
domain = model_cls.objects.get(domain=domain)
except model_cls.DoesNotExist:
log.warning("Domain does not exist for domain: {}".format(domain))
raise DomainNotFound(_("domain not found"))
# Use `alias_of_id` instead of simple `alias_of` for performace reasons.
if domain.alias_of_id is None or not follow_alias:
return domain
return domain.alias_of
def activate(domain):
log.debug("Activating domain: {}".format(domain))
_local.active_domain = domain
def deactivate():
if hasattr(_local, "active_domain"):
log.debug("Deactivating domain: {}".format(_local.active_domain))
del _local.active_domain
def get_active_domain():
active_domain = getattr(_local, "active_domain", None)
if active_domain is None:
return get_default_domain()
return active_domain
def clear_domain_cache(**kwargs):
get_default_domain.cache_clear()
get_domain_for_domain_name.cache_clear()

View File

@ -1,29 +1,38 @@
# -*- coding: utf-8 -*-
import json import json
from django import http from django import http
from taiga.base import domains
from taiga.base.exceptions import format_exception from taiga.base.exceptions import format_exception
from .base import get_domain_for_domain_name
from .base import activate as activate_domain
from .base import deactivate as deactivate_domain
from .base import get_default_domain
from .base import DomainNotFound
class DomainsMiddleware(object): class DomainsMiddleware(object):
"""
Domain middlewate: process request and try resolve domain
from HTTP_X_HOST header. If no header is specified, one
default is used.
"""
def process_request(self, request): def process_request(self, request):
domain = request.META.get("HTTP_X_HOST", None) domain = request.META.get("HTTP_X_HOST", None)
if domain is not None: if domain is not None:
try: try:
domain = domains.get_domain_for_domain_name(domain) domain = get_domain_for_domain_name(domain, follow_alias=True)
except domains.DomainNotFound as e: except DomainNotFound as e:
detail = format_exception(e) detail = format_exception(e)
return http.HttpResponseBadRequest(json.dumps(detail)) return http.HttpResponseBadRequest(json.dumps(detail))
else: else:
domain = domains.get_default_domain() domain = get_default_domain()
request.domain = domain request.domain = domain
domains.activate(domain) activate_domain(domain)
def process_response(self, request, response): def process_response(self, request, response):
domains.deactivate() deactivate_domain()
if hasattr(request, "domain"): if hasattr(request, "domain"):
response["X-Site-Host"] = request.domain.domain response["X-Site-Host"] = request.domain.domain

View File

@ -0,0 +1,89 @@
# -*- 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):
# Adding field 'Domain.alias_of'
db.add_column('domains_domain', 'alias_of',
self.gf('django.db.models.fields.related.ForeignKey')(to=orm['domains.Domain'], default=None, blank=True, null=True, related_name='+'),
keep_default=False)
def backwards(self, orm):
# Deleting field 'Domain.alias_of'
db.delete_column('domains_domain', 'alias_of_id')
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
'Meta': {'object_name': 'Permission', 'unique_together': "(('content_type', 'codename'),)", 'ordering': "('content_type__app_label', 'content_type__model', 'codename')"},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'contenttypes.contenttype': {
'Meta': {'object_name': 'ContentType', 'unique_together': "(('app_label', 'model'),)", 'ordering': "('name',)", 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'domains.domain': {
'Meta': {'object_name': 'Domain', 'ordering': "('domain',)"},
'alias_of': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['domains.Domain']", 'default': 'None', 'blank': 'True', 'null': 'True', 'related_name': "'+'"}),
'default_language': ('django.db.models.fields.CharField', [], {'blank': 'True', 'default': "''", 'max_length': '20'}),
'domain': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'public_register': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'scheme': ('django.db.models.fields.CharField', [], {'default': 'None', 'null': 'True', 'max_length': '60'})
},
'domains.domainmember': {
'Meta': {'object_name': 'DomainMember', 'unique_together': "(('domain', 'user'),)", 'ordering': "['email']"},
'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['domains.Domain']", 'null': 'True', 'related_name': "'members'"}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '255'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_owner': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['users.User']", 'null': 'True', 'related_name': "'+'"})
},
'users.user': {
'Meta': {'object_name': 'User', 'ordering': "['username']"},
'color': ('django.db.models.fields.CharField', [], {'blank': 'True', 'default': "'#28261c'", 'max_length': '9'}),
'colorize_tags': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'default_language': ('django.db.models.fields.CharField', [], {'blank': 'True', 'default': "''", 'max_length': '20'}),
'default_timezone': ('django.db.models.fields.CharField', [], {'blank': 'True', 'default': "''", 'max_length': '20'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'email': ('django.db.models.fields.EmailField', [], {'blank': 'True', 'max_length': '75'}),
'first_name': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '30'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'blank': 'True', 'symmetrical': 'False', 'related_name': "'user_set'"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '30'}),
'notify_changes_by_me': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'notify_level': ('django.db.models.fields.CharField', [], {'default': "'all_owned_projects'", 'max_length': '32'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'photo': ('django.db.models.fields.files.FileField', [], {'blank': 'True', 'null': 'True', 'max_length': '500'}),
'token': ('django.db.models.fields.CharField', [], {'blank': 'True', 'default': 'None', 'null': 'True', 'max_length': '200'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'blank': 'True', 'symmetrical': 'False', 'related_name': "'user_set'"}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
}
}
complete_apps = ['domains']

View File

@ -8,7 +8,7 @@ from django.dispatch import receiver
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from . import clear_domain_cache from .base import clear_domain_cache
def _simple_domain_name_validator(value): def _simple_domain_name_validator(value):
@ -38,6 +38,9 @@ class Domain(models.Model):
default_language = models.CharField(max_length=20, null=False, blank=True, default="", default_language = models.CharField(max_length=20, null=False, blank=True, default="",
verbose_name=_("default language")) verbose_name=_("default language"))
alias_of = models.ForeignKey("self", null=True, default=None, blank=True,
verbose_name=_("Mark as alias of"), related_name="+")
class Meta: class Meta:
verbose_name = _('domain') verbose_name = _('domain')
verbose_name_plural = _('domain') verbose_name_plural = _('domain')