Remove domains.

remotes/origin/enhancement/email-actions
Andrey Antukh 2014-05-26 12:59:44 +02:00
parent 67a7723ab7
commit 2b087678b9
39 changed files with 273 additions and 1412 deletions

1
.gitignore vendored
View File

@ -9,3 +9,4 @@ media
*.mo *.mo
.venv .venv
.coverage .coverage
.\#*

View File

@ -11,8 +11,6 @@ python manage.py syncdb --all --noinput --traceback
python manage.py migrate --fake python manage.py migrate --fake
# echo "-> Load initial Site" # echo "-> Load initial Site"
# python manage.py loaddata initial_site --traceback # python manage.py loaddata initial_site --traceback
echo "-> Load initial domain"
python manage.py loaddata initial_domains --traceback
echo "-> Load initial user" echo "-> Load initial user"
python manage.py loaddata initial_user --traceback python manage.py loaddata initial_user --traceback
echo "-> Load initial project_templates" echo "-> Load initial project_templates"

View File

@ -12,10 +12,10 @@ pytz>=2013.9
six>=1.4.1 six>=1.4.1
djmail>=0.4 djmail>=0.4
django-pgjson==0.1.2 django-pgjson==0.1.2
django-jinja>=0.23 django-jinja>=1.0.1
jinja2==2.7.1 jinja2==2.7.1
pygments>=1.6 pygments>=1.6
django-sites==0.4 django-sites==0.6
Markdown==2.4 Markdown==2.4
fn==0.2.13 fn==0.2.13
diff-match-patch==20110725.1 diff-match-patch==20110725.1

View File

@ -73,11 +73,11 @@ LOGIN_URL="/auth/login/"
USE_TZ = True USE_TZ = True
SITES = { SITES = {
1: {"domain": "localhost:8000", "scheme": "http"}, "api": {"domain": "localhost:8000", "scheme": "http", "name": "api"},
"front": {"domain": "localhost:9001", "scheme": "http", "name": "front"},
} }
DOMAIN_ID = 1 SITE_ID = "api"
SITE_ID = 1
# Session configuration (only used for admin) # Session configuration (only used for admin)
SESSION_ENGINE="django.contrib.sessions.backends.db" SESSION_ENGINE="django.contrib.sessions.backends.db"
@ -133,7 +133,6 @@ TEMPLATE_LOADERS = [
MIDDLEWARE_CLASSES = [ MIDDLEWARE_CLASSES = [
"taiga.base.middleware.cors.CoorsMiddleware", "taiga.base.middleware.cors.CoorsMiddleware",
"taiga.domains.middleware.DomainsMiddleware",
"taiga.events.middleware.SessionIDMiddleware", "taiga.events.middleware.SessionIDMiddleware",
# Common middlewares # Common middlewares
@ -174,7 +173,6 @@ INSTALLED_APPS = [
"taiga.base", "taiga.base",
"taiga.base.searches", "taiga.base.searches",
"taiga.events", "taiga.events",
"taiga.domains",
"taiga.front", "taiga.front",
"taiga.users", "taiga.users",
"taiga.userstorage", "taiga.userstorage",
@ -294,6 +292,7 @@ REST_FRAMEWORK = {
} }
DEFAULT_PROJECT_TEMPLATE = "scrum" DEFAULT_PROJECT_TEMPLATE = "scrum"
PUBLIC_REGISTER_ENABLED = False
# NOTE: DON'T INSERT MORE SETTINGS AFTER THIS LINE # NOTE: DON'T INSERT MORE SETTINGS AFTER THIS LINE

View File

@ -18,6 +18,7 @@ from functools import partial
from enum import Enum from enum import Enum
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.conf import settings
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.permissions import AllowAny from rest_framework.permissions import AllowAny
@ -28,7 +29,6 @@ from rest_framework import serializers
from taiga.base.decorators import list_route from taiga.base.decorators import list_route
from taiga.base import exceptions as exc from taiga.base import exceptions as exc
from taiga.users.services import get_and_validate_user from taiga.users.services import get_and_validate_user
from taiga.domains.services import is_public_register_enabled_for_domain
from .serializers import PublicRegisterSerializer from .serializers import PublicRegisterSerializer
from .serializers import PrivateRegisterForExistingUserSerializer from .serializers import PrivateRegisterForExistingUserSerializer
@ -96,16 +96,16 @@ class AuthViewSet(viewsets.ViewSet):
permission_classes = (AllowAny,) permission_classes = (AllowAny,)
def _public_register(self, request): def _public_register(self, request):
if not is_public_register_enabled_for_domain(request.domain): if not settings.PUBLIC_REGISTER_ENABLED:
raise exc.BadRequest(_("Public register is disabled for this domain.")) raise exc.BadRequest(_("Public register is disabled."))
try: try:
data = parse_public_register_data(request.DATA) data = parse_public_register_data(request.DATA)
user = public_register(request.domain, **data) user = public_register(**data)
except exc.IntegrityError as e: except exc.IntegrityError as e:
raise exc.BadRequest(e.detail) raise exc.BadRequest(e.detail)
data = make_auth_response_data(request.domain, user) data = make_auth_response_data(user)
return Response(data, status=status.HTTP_201_CREATED) return Response(data, status=status.HTTP_201_CREATED)
def _private_register(self, request): def _private_register(self, request):
@ -113,12 +113,12 @@ class AuthViewSet(viewsets.ViewSet):
if register_type is RegisterTypeEnum.existing_user: if register_type is RegisterTypeEnum.existing_user:
data = parse_private_register_for_existing_user_data(request.DATA) data = parse_private_register_for_existing_user_data(request.DATA)
user = private_register_for_existing_user(request.domain, **data) user = private_register_for_existing_user(**data)
else: else:
data = parse_private_register_for_new_user_data(request.DATA) data = parse_private_register_for_new_user_data(request.DATA)
user = private_register_for_new_user(request.domain, **data) user = private_register_for_new_user(**data)
data = make_auth_response_data(request.domain, user) data = make_auth_response_data(user)
return Response(data, status=status.HTTP_201_CREATED) return Response(data, status=status.HTTP_201_CREATED)
@list_route(methods=["POST"], permission_classes=[AllowAny]) @list_route(methods=["POST"], permission_classes=[AllowAny])
@ -136,5 +136,5 @@ class AuthViewSet(viewsets.ViewSet):
password = request.DATA.get('password', None) password = request.DATA.get('password', None)
user = get_and_validate_user(username=username, password=password) user = get_and_validate_user(username=username, password=password)
data = make_auth_response_data(request.domain, user) data = make_auth_response_data(user)
return Response(data, status=status.HTTP_200_OK) return Response(data, status=status.HTTP_200_OK)

View File

@ -34,8 +34,6 @@ from djmail.template_mail import MagicMailBuilder
from taiga.base import exceptions as exc from taiga.base import exceptions as exc
from taiga.users.serializers import UserSerializer from taiga.users.serializers import UserSerializer
from taiga.users.services import get_and_validate_user from taiga.users.services import get_and_validate_user
from taiga.domains.services import (create_domain_member,
is_user_exists_on_domain)
from .backends import get_token_for_user from .backends import get_token_for_user
@ -92,8 +90,7 @@ def get_membership_by_token(token:str):
@tx.atomic @tx.atomic
def public_register(domain, *, username:str, password:str, def public_register(username:str, password:str, email:str, first_name:str, last_name:str):
email:str, first_name:str, last_name:str):
""" """
Given a parsed parameters, try register a new user Given a parsed parameters, try register a new user
knowing that it follows a public register flow. knowing that it follows a public register flow.
@ -115,15 +112,12 @@ def public_register(domain, *, username:str, password:str,
user.set_password(password) user.set_password(password)
user.save() user.save()
if not is_user_exists_on_domain(domain, user):
create_domain_member(domain, user)
# send_public_register_email(user) # send_public_register_email(user)
return user return user
@tx.atomic @tx.atomic
def private_register_for_existing_user(domain, *, token:str, username:str, password:str): def private_register_for_existing_user(token:str, username:str, password:str):
""" """
Register works not only for register users, also serves for accept Register works not only for register users, also serves for accept
inviatations for projects as existing user. inviatations for projects as existing user.
@ -135,9 +129,6 @@ def private_register_for_existing_user(domain, *, token:str, username:str, passw
user = get_and_validate_user(username=username, password=password) user = get_and_validate_user(username=username, password=password)
membership = get_membership_by_token(token) membership = get_membership_by_token(token)
if not is_user_exists_on_domain(domain, user):
create_domain_member(domain, user)
membership.user = user membership.user = user
membership.save(update_fields=["user"]) membership.save(update_fields=["user"])
@ -146,7 +137,7 @@ def private_register_for_existing_user(domain, *, token:str, username:str, passw
@tx.atomic @tx.atomic
def private_register_for_new_user(domain, *, token:str, username:str, email:str, def private_register_for_new_user(token:str, username:str, email:str,
first_name:str, last_name:str, password:str): first_name:str, last_name:str, password:str):
""" """
Given a inviation token, try register new user matching Given a inviation token, try register new user matching
@ -169,9 +160,6 @@ def private_register_for_new_user(domain, *, token:str, username:str, email:str,
except IntegrityError: except IntegrityError:
raise exc.IntegrityError(_("Error on creating new user.")) raise exc.IntegrityError(_("Error on creating new user."))
if not is_user_exists_on_domain(domain, user):
create_domain_member(domain, user)
membership = get_membership_by_token(token) membership = get_membership_by_token(token)
membership.user = user membership.user = user
membership.save(update_fields=["user"]) membership.save(update_fields=["user"])
@ -179,7 +167,7 @@ def private_register_for_new_user(domain, *, token:str, username:str, email:str,
return user return user
def make_auth_response_data(domain, user) -> dict: def make_auth_response_data(user) -> dict:
""" """
Given a domain and user, creates data structure Given a domain and user, creates data structure
using python dict containing a representation using python dict containing a representation
@ -187,9 +175,5 @@ def make_auth_response_data(domain, user) -> dict:
""" """
serializer = UserSerializer(user) serializer = UserSerializer(user)
data = dict(serializer.data) data = dict(serializer.data)
data['is_site_owner'] = domain.user_is_owner(user)
data['is_site_staff'] = domain.user_is_staff(user)
data["auth_token"] = get_token_for_user(user) data["auth_token"] = get_token_for_user(user)
return data return data

View File

@ -18,8 +18,6 @@ from django.forms import widgets
from rest_framework import serializers from rest_framework import serializers
from taiga.domains.base import get_active_domain
from taiga.domains.models import Domain
from .neighbors import get_neighbors from .neighbors import get_neighbors
@ -47,20 +45,6 @@ class JsonField(serializers.WritableField):
return data return data
class AutoDomainField(serializers.WritableField):
"""
Automatically set domain field serializer.
"""
def to_native(self, obj):
if obj:
return obj.id
return obj
def from_native(self, data):
domain = get_active_domain()
return domain
class NeighborsSerializerMixin: class NeighborsSerializerMixin:
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):

View File

@ -1,27 +0,0 @@
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from .base import get_default_domain
from .base import get_domain_for_domain_name
from .base import activate
from .base import deactivate
from .base import get_active_domain
from .base import DomainNotFound
__all__ = ["get_default_domain",
"get_domain_for_domain_name",
"activate",
"deactivate",
"get_active_domain",
"DomainNotFound"]

View File

@ -1,27 +0,0 @@
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from django.contrib import admin
from .models import Domain, DomainMember
class DomainMemberInline(admin.TabularInline):
model = DomainMember
class DomainAdmin(admin.ModelAdmin):
list_display = ('domain', 'name')
search_fields = ('domain', 'name')
inlines = [ DomainMemberInline, ]
admin.site.register(Domain, DomainAdmin)

View File

@ -1,64 +0,0 @@
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated
from taiga.base.api import GenericViewSet
from taiga.base.api import ListModelMixin
from taiga.base.api import UpdateModelMixin
from taiga.base import exceptions as exc
from .base import get_active_domain
from .serializers import DomainSerializer
from .serializers import DomainMemberSerializer
from .permissions import DomainPermission
from .permissions import DomainMembersPermission
from .models import Domain
from .models import DomainMember
class DomainViewSet(UpdateModelMixin, GenericViewSet):
permission_classes = (DomainPermission,)
serializer_class = DomainSerializer
queryset = Domain.objects.all()
def list(self, request, **kwargs):
domain_data = DomainSerializer(request.domain).data
if request.domain.user_is_normal_user(request.user):
domain_data['projects'] = None
elif request.user.is_anonymous():
domain_data['projects'] = None
return Response(domain_data)
def update(self, request, *args, **kwargs):
raise exc.NotSupported()
def partial_update(self, request, *args, **kwargs):
raise exc.NotSupported()
def create(self, request, **kwargs):
self.kwargs['pk'] = request.domain.pk
return super().update(request, pk=request.domain.pk, **kwargs)
class DomainMembersViewSet(ListModelMixin, UpdateModelMixin, GenericViewSet):
permission_classes = (IsAuthenticated, DomainMembersPermission,)
serializer_class = DomainMemberSerializer
queryset = DomainMember.objects.all()
def get_queryset(self):
domain = get_active_domain()
qs = super().get_queryset()
return qs.filter(domain=domain).distinct()

View File

@ -1,87 +0,0 @@
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
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,12 +0,0 @@
[
{
"model": "domains.domain",
"fields": {
"public_register": false,
"domain": "localhost",
"scheme": null,
"name": "localhost"
},
"pk": 1
}
]

View File

@ -1,54 +0,0 @@
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import json
from django import http
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):
"""
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):
domain = request.META.get("HTTP_X_HOST", None)
if domain is not None:
try:
domain = get_domain_for_domain_name(domain, follow_alias=True)
except DomainNotFound as e:
detail = format_exception(e)
return http.HttpResponseBadRequest(json.dumps(detail))
else:
domain = get_default_domain()
request.domain = domain
activate_domain(domain)
def process_response(self, request, response):
deactivate_domain()
if hasattr(request, "domain"):
response["X-Site-Host"] = request.domain.domain
return response

View File

@ -1,112 +0,0 @@
# -*- 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 model 'Domain'
db.create_table('domains_domain', (
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('domain', self.gf('django.db.models.fields.CharField')(max_length=255, unique=True)),
('name', self.gf('django.db.models.fields.CharField')(max_length=255)),
('scheme', self.gf('django.db.models.fields.CharField')(max_length=60, null=True, default=None)),
('public_register', self.gf('django.db.models.fields.BooleanField')(default=False)),
))
db.send_create_signal('domains', ['Domain'])
# Adding model 'DomainMember'
db.create_table('domains_domainmember', (
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('site', self.gf('django.db.models.fields.related.ForeignKey')(related_name='+', to=orm['domains.Domain'])),
('user', self.gf('django.db.models.fields.related.ForeignKey')(related_name='+', null=True, to=orm['users.User'])),
('email', self.gf('django.db.models.fields.EmailField')(max_length=255)),
('is_owner', self.gf('django.db.models.fields.BooleanField')(default=False)),
('is_staff', self.gf('django.db.models.fields.BooleanField')(default=False)),
))
db.send_create_signal('domains', ['DomainMember'])
# Adding unique constraint on 'DomainMember', fields ['site', 'user']
db.create_unique('domains_domainmember', ['site_id', 'user_id'])
def backwards(self, orm):
# Removing unique constraint on 'DomainMember', fields ['site', 'user']
db.delete_unique('domains_domainmember', ['site_id', 'user_id'])
# Deleting model 'Domain'
db.delete_table('domains_domain')
# Deleting model 'DomainMember'
db.delete_table('domains_domainmember')
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '80', 'unique': 'True'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'blank': 'True', 'symmetrical': 'False'})
},
'auth.permission': {
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'object_name': 'Permission', 'unique_together': "(('content_type', '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': {'ordering': "('name',)", 'db_table': "'django_content_type'", 'object_name': 'ContentType', 'unique_together': "(('app_label', 'model'),)"},
'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': {'ordering': "('domain',)", 'object_name': 'Domain'},
'domain': ('django.db.models.fields.CharField', [], {'max_length': '255', 'unique': 'True'}),
'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', [], {'max_length': '60', 'null': 'True', 'default': 'None'})
},
'domains.domainmember': {
'Meta': {'ordering': "['email']", 'object_name': 'DomainMember', 'unique_together': "(('site', 'user'),)"},
'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'}),
'site': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': "orm['domains.Domain']"}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'null': 'True', 'to': "orm['users.User']"})
},
'users.user': {
'Meta': {'ordering': "['username']", 'object_name': 'User'},
'color': ('django.db.models.fields.CharField', [], {'max_length': '9', 'blank': 'True', 'default': "'#669933'"}),
'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', [], {'max_length': '20', 'blank': 'True', 'default': "''"}),
'default_timezone': ('django.db.models.fields.CharField', [], {'max_length': '20', 'blank': 'True', 'default': "''"}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'related_name': "'user_set'", 'blank': 'True', 'symmetrical': 'False'}),
'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', [], {'max_length': '30', 'blank': 'True'}),
'notify_changes_by_me': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'notify_level': ('django.db.models.fields.CharField', [], {'max_length': '32', 'default': "'all_owned_projects'"}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'photo': ('django.db.models.fields.files.FileField', [], {'max_length': '500', 'blank': 'True', 'null': 'True'}),
'token': ('django.db.models.fields.CharField', [], {'max_length': '200', 'blank': 'True', 'null': 'True', 'default': 'None'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'related_name': "'user_set'", 'blank': 'True', 'symmetrical': 'False'}),
'username': ('django.db.models.fields.CharField', [], {'max_length': '30', 'unique': 'True'})
}
}
complete_apps = ['domains']

View File

@ -1,98 +0,0 @@
# -*- 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 'DomainMember.domain'
db.add_column('domains_domainmember', 'domain',
self.gf('django.db.models.fields.related.ForeignKey')(to=orm['domains.Domain'], null=True, related_name='+'),
keep_default=False)
# Changing field 'DomainMember.site'
db.alter_column('domains_domainmember', 'site_id', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['domains.Domain'], null=True))
def backwards(self, orm):
# Deleting field 'DomainMember.domain'
db.delete_column('domains_domainmember', 'domain_id')
# User chose to not deal with backwards NULL issues for 'DomainMember.site'
raise RuntimeError("Cannot reverse this migration. 'DomainMember.site' and its values cannot be restored.")
# The following code is provided here to aid in writing a correct migration
# Changing field 'DomainMember.site'
db.alter_column('domains_domainmember', 'site_id', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['domains.Domain']))
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']", 'blank': 'True', 'symmetrical': 'False'})
},
'auth.permission': {
'Meta': {'unique_together': "(('content_type', 'codename'),)", 'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'object_name': 'Permission'},
'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': {'db_table': "'django_content_type'", 'unique_together': "(('app_label', 'model'),)", 'ordering': "('name',)", 'object_name': 'ContentType'},
'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': {'ordering': "('domain',)", 'object_name': 'Domain'},
'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', [], {'null': 'True', 'default': 'None', 'max_length': '60'})
},
'domains.domainmember': {
'Meta': {'unique_together': "(('site', 'user'),)", 'ordering': "['email']", 'object_name': 'DomainMember'},
'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['domains.Domain']", 'null': 'True', 'related_name': "'+'"}),
'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'}),
'site': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['domains.Domain']", 'null': 'True', 'related_name': "'+'"}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['users.User']", 'null': 'True', 'related_name': "'+'"})
},
'users.user': {
'Meta': {'ordering': "['username']", 'object_name': 'User'},
'color': ('django.db.models.fields.CharField', [], {'blank': 'True', 'default': "'#669933'", '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']", 'related_name': "'user_set'", 'blank': 'True', 'symmetrical': 'False'}),
'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', [], {'null': 'True', 'blank': 'True', 'max_length': '500'}),
'token': ('django.db.models.fields.CharField', [], {'null': 'True', 'default': 'None', 'max_length': '200', 'blank': 'True'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'related_name': "'user_set'", 'blank': 'True', 'symmetrical': 'False'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
}
}
complete_apps = ['domains']

View File

@ -1,89 +0,0 @@
# -*- coding: utf-8 -*-
from south.utils import datetime_utils as datetime
from south.db import db
from south.v2 import DataMigration
from django.db import models
class Migration(DataMigration):
def forwards(self, orm):
"Write your forwards methods here."
# Note: Don't use "from appname.models import ModelName".
# Use orm.ModelName to refer to models in this application,
# and orm['appname.ModelName'] for models in other applications.
for dm in orm["domains.DomainMember"].objects.all():
dm.domain = dm.site
dm.save()
def backwards(self, orm):
"Write your backwards methods here."
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', [], {'symmetrical': 'False', 'blank': 'True', 'to': "orm['auth.Permission']"})
},
'auth.permission': {
'Meta': {'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission', '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': {'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'", 'ordering': "('name',)"},
'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',)"},
'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', 'max_length': '60', 'null': 'True'})
},
'domains.domainmember': {
'Meta': {'unique_together': "(('site', 'user'),)", 'object_name': 'DomainMember', 'ordering': "['email']"},
'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['domains.Domain']", 'null': 'True', 'related_name': "'+'"}),
'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'}),
'site': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['domains.Domain']", 'null': 'True', 'related_name': "'+'"}),
'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', [], {'default': "'#669933'", 'blank': 'True', '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', [], {'default': "''", 'blank': 'True', 'max_length': '20'}),
'default_timezone': ('django.db.models.fields.CharField', [], {'default': "''", 'blank': 'True', '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', [], {'symmetrical': 'False', 'blank': 'True', 'to': "orm['auth.Group']", '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', [], {'max_length': '500', 'blank': 'True', 'null': 'True'}),
'token': ('django.db.models.fields.CharField', [], {'default': 'None', 'blank': 'True', 'max_length': '200', 'null': 'True'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'blank': 'True', 'to': "orm['auth.Permission']", 'related_name': "'user_set'"}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
}
}
complete_apps = ['domains']
symmetrical = True

View File

@ -1,99 +0,0 @@
# -*- 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):
# Removing unique constraint on 'DomainMember', fields ['site', 'user']
db.delete_unique('domains_domainmember', ['site_id', 'user_id'])
# Deleting field 'DomainMember.site'
db.delete_column('domains_domainmember', 'site_id')
# Adding unique constraint on 'DomainMember', fields ['domain', 'user']
db.create_unique('domains_domainmember', ['domain_id', 'user_id'])
def backwards(self, orm):
# Removing unique constraint on 'DomainMember', fields ['domain', 'user']
db.delete_unique('domains_domainmember', ['domain_id', 'user_id'])
# Adding field 'DomainMember.site'
db.add_column('domains_domainmember', 'site',
self.gf('django.db.models.fields.related.ForeignKey')(to=orm['domains.Domain'], related_name='+', null=True),
keep_default=False)
# Adding unique constraint on 'DomainMember', fields ['site', 'user']
db.create_unique('domains_domainmember', ['site_id', 'user_id'])
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '80', 'unique': 'True'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'blank': 'True', 'symmetrical': 'False'})
},
'auth.permission': {
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'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': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'db_table': "'django_content_type'", 'object_name': 'ContentType'},
'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': {'ordering': "('domain',)", 'object_name': 'Domain'},
'domain': ('django.db.models.fields.CharField', [], {'max_length': '255', 'unique': 'True'}),
'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', [], {'max_length': '60', 'default': 'None', 'null': 'True'})
},
'domains.domainmember': {
'Meta': {'ordering': "['email']", 'unique_together': "(('domain', 'user'),)", 'object_name': 'DomainMember'},
'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['domains.Domain']", 'related_name': "'+'", 'null': 'True'}),
'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']", 'related_name': "'+'", 'null': 'True'})
},
'users.user': {
'Meta': {'ordering': "['username']", 'object_name': 'User'},
'color': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '9', 'default': "'#669933'"}),
'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', 'max_length': '20', 'default': "''"}),
'default_timezone': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '20', 'default': "''"}),
'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', [], {'max_length': '32', 'default': "'all_owned_projects'"}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'photo': ('django.db.models.fields.files.FileField', [], {'blank': 'True', 'max_length': '500', 'null': 'True'}),
'token': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '200', 'default': 'None', 'null': 'True'}),
'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', [], {'max_length': '30', 'unique': 'True'})
}
}
complete_apps = ['domains']

View File

@ -1,88 +0,0 @@
# -*- 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.default_language'
db.add_column('domains_domain', 'default_language',
self.gf('django.db.models.fields.CharField')(default='', max_length=20, blank=True),
keep_default=False)
def backwards(self, orm):
# Deleting field 'Domain.default_language'
db.delete_column('domains_domain', 'default_language')
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '80', 'unique': 'True'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['auth.Permission']", 'blank': 'True'})
},
'auth.permission': {
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'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': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', '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': {'ordering': "('domain',)", 'object_name': 'Domain'},
'default_language': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20', 'blank': 'True'}),
'domain': ('django.db.models.fields.CharField', [], {'max_length': '255', 'unique': 'True'}),
'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', 'max_length': '60', 'null': 'True'})
},
'domains.domainmember': {
'Meta': {'ordering': "['email']", 'unique_together': "(('domain', 'user'),)", 'object_name': 'DomainMember'},
'domain': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': "orm['domains.Domain']", 'null': 'True'}),
'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', [], {'related_name': "'+'", 'to': "orm['users.User']", 'null': 'True'})
},
'users.user': {
'Meta': {'ordering': "['username']", 'object_name': 'User'},
'color': ('django.db.models.fields.CharField', [], {'default': "'#669933'", 'max_length': '9', 'blank': 'True'}),
'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', [], {'default': "''", 'max_length': '20', 'blank': 'True'}),
'default_timezone': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'user_set'", 'to': "orm['auth.Group']", 'blank': 'True'}),
'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', [], {'max_length': '30', 'blank': 'True'}),
'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', [], {'null': 'True', 'max_length': '500', 'blank': 'True'}),
'token': ('django.db.models.fields.CharField', [], {'default': 'None', 'null': 'True', 'max_length': '200', 'blank': 'True'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'user_set'", 'to': "orm['auth.Permission']", 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'max_length': '30', 'unique': 'True'})
}
}
complete_apps = ['domains']

View File

@ -1,89 +0,0 @@
# -*- 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

@ -1,98 +0,0 @@
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import string
from django.db import models
from django.db.models.signals import pre_save, pre_delete
from django.dispatch import receiver
from django.utils.translation import ugettext_lazy as _
from django.core.exceptions import ValidationError
from .base import clear_domain_cache
def _simple_domain_name_validator(value):
"""
Validates that the given value contains no whitespaces to prevent common
typos.
"""
if not value:
return
checks = ((s in value) for s in string.whitespace)
if any(checks):
raise ValidationError(
_("The domain name cannot contain any spaces or tabs."),
code='invalid',
)
class Domain(models.Model):
domain = models.CharField(_('domain name'), max_length=255, unique=True,
validators=[_simple_domain_name_validator])
name = models.CharField(_('display name'), max_length=255)
scheme = models.CharField(_('scheme'), max_length=60, null=True, default=None)
# Site Metadata
public_register = models.BooleanField(default=False)
default_language = models.CharField(max_length=20, null=False, blank=True, default="",
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:
verbose_name = _('domain')
verbose_name_plural = _('domain')
ordering = ('domain',)
def __str__(self):
return self.domain
def user_is_owner(self, user):
return self.members.filter(user_id=user.id, is_owner=True).exists()
def user_is_staff(self, user):
return self.members.filter(user_id=user.id, is_staff=True).exists()
def user_is_normal_user(self, user):
return self.members.filter(user_id=user.id, is_owner=False, is_staff=False).exists()
class DomainMember(models.Model):
domain = models.ForeignKey("Domain", related_name="members", null=True)
user = models.ForeignKey("users.User", related_name="+", null=True)
email = models.EmailField(max_length=255)
is_owner = models.BooleanField(default=False)
is_staff = models.BooleanField(default=False)
class Meta:
ordering = ["email"]
verbose_name = "Domain Member"
verbose_name_plural = "Domain Members"
unique_together = ("domain", "user")
def __str__(self):
return "DomainMember: {0}:{1}".format(self.domain, self.user)
pre_save.connect(clear_domain_cache, sender=Domain)
pre_delete.connect(clear_domain_cache, sender=Domain)
@receiver(pre_delete, sender=DomainMember, dispatch_uid="domain_member_pre_delete")
def domain_member_pre_delete(sender, instance, *args, **kwargs):
for domain_project in instance.domain.projects.all():
domain_project.memberships.filter(user=instance.user).delete()

View File

@ -1,43 +0,0 @@
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from rest_framework import permissions
from .models import DomainMember
from .base import get_active_domain
class DomainPermission(permissions.BasePermission):
safe_methods = ['HEAD', 'OPTIONS', 'GET']
def has_object_permission(self, request, view, obj):
if request.method in self.safe_methods:
return True
domain = get_active_domain()
return domain.user_is_owner(request.user)
class DomainMembersPermission(permissions.BasePermission):
safe_methods = ['HEAD', 'OPTIONS']
def has_permission(self, request, view):
if request.method in self.safe_methods:
return True
domain = get_active_domain()
if request.method in ["POST", "PUT", "PATCH", "GET"]:
return domain.user_is_owner(request.user)
return False

View File

@ -1,42 +0,0 @@
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from django.conf import settings
from rest_framework import serializers
from taiga.users.serializers import UserSerializer
from .models import Domain, DomainMember
class DomainSerializer(serializers.ModelSerializer):
projects = serializers.SerializerMethodField('get_projects')
default_project_template = serializers.SerializerMethodField('get_default_project_template')
class Meta:
model = Domain
fields = ('public_register', 'default_language', "projects", "default_project_template")
def get_projects(self, obj):
return map(lambda x: {"id": x.id, "name": x.name, "slug": x.slug, "owner": x.owner.id}, obj.projects.all().order_by('name'))
def get_default_project_template(self, obj):
return settings.DEFAULT_PROJECT_TEMPLATE
class DomainMemberSerializer(serializers.ModelSerializer):
user = UserSerializer()
class Meta:
model = DomainMember

View File

@ -1,65 +0,0 @@
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
# Copyright (C) 2014 Jesús Espino <jespinog@gmail.com>
# Copyright (C) 2014 David Barragán <bameda@dbarragan.com>
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
This module contains a domain logic for domains application.
"""
from django.db.models.loading import get_model
from django.db import transaction as tx
from django.db import IntegrityError
from taiga.base import exceptions as exc
def is_user_exists_on_domain(domain, user) -> bool:
"""
Checks if user is alredy exists on domain.
"""
return domain.members.filter(user=user).exists()
def is_public_register_enabled_for_domain(domain) -> bool:
"""
Checks if a specified domain have public register
activated.
The implementation is very simple but it encapsulates
request attribute access into more semantic function
call.
"""
return domain.public_register
@tx.atomic
def create_domain_member(domain, user):
"""
Given a domain and user, add user as member to
specified domain.
:returns: DomainMember
"""
domain_member_model = get_model("domains", "DomainMember")
try:
domain_member = domain_member_model(domain=domain, user=user,
email=user.email, is_owner=False,
is_staff=False)
domain_member.save()
except IntegrityError:
raise exc.IntegrityError("User is already member in a site")
return domain_member

View File

@ -1,118 +0,0 @@
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from django import test
from django.test.utils import override_settings
from django.core.exceptions import ImproperlyConfigured
from django.db.models import get_model
from django.http import HttpResponse
from . import base
from .models import Domain
from .middleware import DomainsMiddleware
class DomainCoreTests(test.TestCase):
fixtures = ["initial_domains.json"]
def setUp(self):
base.clear_domain_cache()
@override_settings(DOMAIN_ID=1)
def test_get_default_domain(self):
default_domain = base.get_default_domain()
self.assertEqual(default_domain.domain, "localhost")
@override_settings(DOMAIN_ID=2)
def test_get_wrong_default_domain(self):
with self.assertRaises(ImproperlyConfigured):
default_domain = base.get_default_domain()
def test_get_domain_by_name(self):
domain = base.get_domain_for_domain_name("localhost")
self.assertEqual(domain.id, 1)
self.assertEqual(domain.domain, "localhost")
def test_get_domain_by_name_aliased(self):
main_domain = base.get_default_domain()
aliased_domain = Domain.objects.create(domain="beta.localhost", scheme="http",
alias_of=main_domain)
resolved_domain = base.get_domain_for_domain_name("beta.localhost", follow_alias=False)
self.assertEqual(resolved_domain.domain, "beta.localhost")
resolved_domain = base.get_domain_for_domain_name("beta.localhost", follow_alias=True)
self.assertEqual(resolved_domain.domain, "localhost")
def test_lru_cache_for_get_default_domain(self):
with self.assertNumQueries(1):
base.get_default_domain()
base.get_default_domain()
def test_lru_cache_for_get_domain_for_domain_name(self):
with self.assertNumQueries(2):
base.get_domain_for_domain_name("localhost", follow_alias=True)
base.get_domain_for_domain_name("localhost", follow_alias=True)
base.get_domain_for_domain_name("localhost", follow_alias=False)
base.get_domain_for_domain_name("localhost", follow_alias=False)
def test_activate_deactivate_domain(self):
main_domain = base.get_default_domain()
aliased_domain = Domain.objects.create(domain="beta.localhost", scheme="http",
alias_of=main_domain)
self.assertEqual(base.get_active_domain(), main_domain)
base.activate(aliased_domain)
self.assertEqual(base.get_active_domain(), aliased_domain)
base.deactivate()
self.assertEqual(base.get_active_domain(), main_domain)
from django.test.client import RequestFactory
class DomainMiddlewareTests(test.TestCase):
fixtures = ["initial_domains.json"]
def setUp(self):
self.main_domain = base.get_default_domain()
self.aliased_domain = Domain.objects.create(domain="beta.localhost", scheme="http",
alias_of=self.main_domain)
self.factory = RequestFactory()
def test_process_request(self):
request = self.factory.get("/", HTTP_X_HOST="beta.localhost")
middleware = DomainsMiddleware()
ret = middleware.process_request(request)
self.assertEqual(request.domain, self.main_domain)
self.assertEqual(ret, None)
def test_process_request_with_wrong_domain(self):
request = self.factory.get("/", HTTP_X_HOST="beta2.localhost")
middleware = DomainsMiddleware()
ret = middleware.process_request(request)
self.assertFalse(hasattr(request, "domain"))
self.assertNotEqual(ret, None)
self.assertIsInstance(ret, HttpResponse)
def test_process_request_without_host_header(self):
request = self.factory.get("/")
middleware = DomainsMiddleware()
ret = middleware.process_request(request)
self.assertEqual(request.domain, self.main_domain)
self.assertEqual(ret, None)

View File

@ -13,11 +13,12 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from django.conf import settings from django.conf import settings
from django_jinja import library from django_jinja import library
from django_sites import get_by_id as get_site_by_id
from taiga import domains
URLS = { urls = {
"home": "/", "home": "/",
"backlog": "/#/project/{0}/backlog/", "backlog": "/#/project/{0}/backlog/",
"taskboard": "/#/project/{0}/taskboard/{1}", "taskboard": "/#/project/{0}/taskboard/{1}",
@ -31,14 +32,11 @@ URLS = {
} }
lib = library.Library() @library.global_function(name="resolve_front_url")
@lib.global_function(name="resolve_front_url")
def resolve(type, *args): def resolve(type, *args):
domain = domains.get_active_domain() site = get_site_by_id("front")
url_tmpl = "{scheme}//{domain}{url}" url_tmpl = "{scheme}//{domain}{url}"
scheme = domain.scheme and "{0}:".format(domain.scheme) or "" scheme = site.scheme and "{0}:".format(site.scheme) or ""
url = URLS[type].format(*args) url = urlsp[type].format(*args)
return url_tmpl.format(scheme=scheme, domain=domain.domain, url=url) return url_tmpl.format(scheme=scheme, domain=site.domain, url=url)

View File

@ -34,7 +34,7 @@ class MembershipInline(admin.TabularInline):
class ProjectAdmin(admin.ModelAdmin): class ProjectAdmin(admin.ModelAdmin):
list_display = ["name", "owner", "created_date", "total_milestones", list_display = ["name", "owner", "created_date", "total_milestones",
"total_story_points", "domain"] "total_story_points"]
list_display_links = list_display list_display_links = list_display
inlines = [RoleInline, MembershipInline, MilestoneInline] inlines = [RoleInline, MembershipInline, MilestoneInline]

View File

@ -28,7 +28,6 @@ from rest_framework import status
from djmail.template_mail import MagicMailBuilder from djmail.template_mail import MagicMailBuilder
from taiga.domains import get_active_domain
from taiga.base import filters from taiga.base import filters
from taiga.base import exceptions as exc from taiga.base import exceptions as exc
from taiga.base.decorators import list_route, detail_route from taiga.base.decorators import list_route, detail_route
@ -50,8 +49,7 @@ class ProjectAdminViewSet(ModelCrudViewSet):
permission_classes = (IsAuthenticated, permissions.ProjectAdminPermission) permission_classes = (IsAuthenticated, permissions.ProjectAdminPermission)
def get_queryset(self): def get_queryset(self):
domain = get_active_domain() return models.Project.objects.all()
return domain.projects.all()
def pre_save(self, obj): def pre_save(self, obj):
if not obj.id: if not obj.id:
@ -61,13 +59,6 @@ class ProjectAdminViewSet(ModelCrudViewSet):
if not obj.id: if not obj.id:
obj.template = self.request.QUERY_PARAMS.get('template', None) obj.template = self.request.QUERY_PARAMS.get('template', None)
# FIXME
# Assign domain only if it current
# value is None
if not obj.domain:
obj.domain = self.request.domain
super().pre_save(obj) super().pre_save(obj)
@ -100,20 +91,13 @@ class ProjectViewSet(ModelCrudViewSet):
def get_queryset(self): def get_queryset(self):
qs = super().get_queryset() qs = super().get_queryset()
qs = qs.filter(Q(owner=self.request.user) | qs = qs.filter(Q(owner=self.request.user) |
Q(members=self.request.user)).filter(domain=get_active_domain()) Q(members=self.request.user))
return qs.distinct() return qs.distinct()
def pre_save(self, obj): def pre_save(self, obj):
if not obj.id: if not obj.id:
obj.owner = self.request.user obj.owner = self.request.user
# FIXME
# Assign domain only if it current
# value is None
if not obj.domain:
obj.domain = self.request.domain
super().pre_save(obj) super().pre_save(obj)
@ -310,10 +294,8 @@ class ProjectTemplateViewSet(ModelCrudViewSet):
template_slug = slugify_uniquely(template_name, models.ProjectTemplate) template_slug = slugify_uniquely(template_name, models.ProjectTemplate)
domain = get_active_domain()
try: try:
project = models.Project.objects.get(domain=domain, pk=project_id) project = models.Project.objects.get(pk=project_id)
except models.Project.DoesNotExist: except models.Project.DoesNotExist:
raise ParseError("Not valid project_id") raise ParseError("Not valid project_id")
@ -321,12 +303,11 @@ class ProjectTemplateViewSet(ModelCrudViewSet):
name=template_name, name=template_name,
slug=template_slug, slug=template_slug,
description=template_description, description=template_description,
domain=domain,
) )
template.load_data_from_project(project) template.load_data_from_project(project)
template.save() template.save()
return Response(self.serializer_class(template).data, status=201) return Response(self.serializer_class(template).data, status=201)
def get_queryset(self): def get_queryset(self):
domain = get_active_domain() return models.ProjectTemplate.objects.all()
return models.ProjectTemplate.objects.filter(Q(domain=domain) | Q(domain__isnull=True))

View File

@ -23,8 +23,7 @@
"us_statuses": "[{\"order\": 1, \"name\": \"Open\", \"color\": \"#669933\", \"is_closed\": false, \"wip_limit\": null}, {\"order\": 2, \"name\": \"Closed\", \"color\": \"#999999\", \"is_closed\": true, \"wip_limit\": null}]", "us_statuses": "[{\"order\": 1, \"name\": \"Open\", \"color\": \"#669933\", \"is_closed\": false, \"wip_limit\": null}, {\"order\": 2, \"name\": \"Closed\", \"color\": \"#999999\", \"is_closed\": true, \"wip_limit\": null}]",
"videoconferences_salt": null, "videoconferences_salt": null,
"priorities": "[{\"order\": 1, \"name\": \"Low\", \"color\": \"#666666\"}, {\"order\": 3, \"name\": \"Normal\", \"color\": \"#669933\"}, {\"order\": 5, \"name\": \"High\", \"color\": \"#CC0000\"}]", "priorities": "[{\"order\": 1, \"name\": \"Low\", \"color\": \"#666666\"}, {\"order\": 3, \"name\": \"Normal\", \"color\": \"#669933\"}, {\"order\": 5, \"name\": \"High\", \"color\": \"#CC0000\"}]",
"severities": "[{\"order\": 1, \"name\": \"Wishlist\", \"color\": \"#666666\"}, {\"order\": 2, \"name\": \"Minor\", \"color\": \"#669933\"}, {\"order\": 3, \"name\": \"Normal\", \"color\": \"#0000FF\"}, {\"order\": 4, \"name\": \"Important\", \"color\": \"#FFA500\"}, {\"order\": 5, \"name\": \"Critical\", \"color\": \"#CC0000\"}]", "severities": "[{\"order\": 1, \"name\": \"Wishlist\", \"color\": \"#666666\"}, {\"order\": 2, \"name\": \"Minor\", \"color\": \"#669933\"}, {\"order\": 3, \"name\": \"Normal\", \"color\": \"#0000FF\"}, {\"order\": 4, \"name\": \"Important\", \"color\": \"#FFA500\"}, {\"order\": 5, \"name\": \"Critical\", \"color\": \"#CC0000\"}]"
"domain": null
} }
}, },
{ {
@ -51,8 +50,7 @@
"us_statuses": "[{\"order\": 1, \"name\": \"To do\", \"color\": \"#999999\", \"is_closed\": false, \"wip_limit\": null}, {\"order\": 2, \"name\": \"Doing\", \"color\": \"#ff9900\", \"is_closed\": false, \"wip_limit\": null}, {\"order\": 3, \"name\": \"Done\", \"color\": \"#ffcc00\", \"is_closed\": true, \"wip_limit\": null}]", "us_statuses": "[{\"order\": 1, \"name\": \"To do\", \"color\": \"#999999\", \"is_closed\": false, \"wip_limit\": null}, {\"order\": 2, \"name\": \"Doing\", \"color\": \"#ff9900\", \"is_closed\": false, \"wip_limit\": null}, {\"order\": 3, \"name\": \"Done\", \"color\": \"#ffcc00\", \"is_closed\": true, \"wip_limit\": null}]",
"videoconferences_salt": null, "videoconferences_salt": null,
"priorities": "[{\"order\": 1, \"name\": \"Low\", \"color\": \"#666666\"}, {\"order\": 3, \"name\": \"Normal\", \"color\": \"#669933\"}, {\"order\": 5, \"name\": \"High\", \"color\": \"#CC0000\"}]", "priorities": "[{\"order\": 1, \"name\": \"Low\", \"color\": \"#666666\"}, {\"order\": 3, \"name\": \"Normal\", \"color\": \"#669933\"}, {\"order\": 5, \"name\": \"High\", \"color\": \"#CC0000\"}]",
"severities": "[{\"order\": 1, \"name\": \"Wishlist\", \"color\": \"#666666\"}, {\"order\": 2, \"name\": \"Minor\", \"color\": \"#669933\"}, {\"order\": 3, \"name\": \"Normal\", \"color\": \"#0000FF\"}, {\"order\": 4, \"name\": \"Important\", \"color\": \"#FFA500\"}, {\"order\": 5, \"name\": \"Critical\", \"color\": \"#CC0000\"}]", "severities": "[{\"order\": 1, \"name\": \"Wishlist\", \"color\": \"#666666\"}, {\"order\": 2, \"name\": \"Minor\", \"color\": \"#669933\"}, {\"order\": 3, \"name\": \"Normal\", \"color\": \"#0000FF\"}, {\"order\": 4, \"name\": \"Important\", \"color\": \"#FFA500\"}, {\"order\": 5, \"name\": \"Critical\", \"color\": \"#CC0000\"}]"
"domain": null
} }
} }
] ]

View File

@ -281,7 +281,6 @@ class Command(BaseCommand):
name='Project Example {0}'.format(counter), name='Project Example {0}'.format(counter),
description='Project example {0} description'.format(counter), description='Project example {0} description'.format(counter),
owner=random.choice(self.users), owner=random.choice(self.users),
domain_id=1,
public=True, public=True,
total_story_points=self.sd.int(600, 3000), total_story_points=self.sd.int(600, 3000),
total_milestones=self.sd.int(5,10)) total_milestones=self.sd.int(5,10))

View File

@ -0,0 +1,224 @@
# -*- 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):
# Removing unique constraint on 'ProjectTemplate', fields ['slug', 'domain']
db.delete_unique('projects_projecttemplate', ['slug', 'domain_id'])
# Deleting field 'ProjectTemplate.domain'
db.delete_column('projects_projecttemplate', 'domain_id')
# Adding unique constraint on 'ProjectTemplate', fields ['slug']
db.create_unique('projects_projecttemplate', ['slug'])
# Deleting field 'Project.domain'
db.delete_column('projects_project', 'domain_id')
def backwards(self, orm):
# Removing unique constraint on 'ProjectTemplate', fields ['slug']
db.delete_unique('projects_projecttemplate', ['slug'])
# Adding field 'ProjectTemplate.domain'
db.add_column('projects_projecttemplate', 'domain',
self.gf('django.db.models.fields.related.ForeignKey')(default=None, related_name='templates', to=orm['domains.Domain'], blank=True, null=True),
keep_default=False)
# Adding unique constraint on 'ProjectTemplate', fields ['slug', 'domain']
db.create_unique('projects_projecttemplate', ['slug', 'domain_id'])
# Adding field 'Project.domain'
db.add_column('projects_project', 'domain',
self.gf('django.db.models.fields.related.ForeignKey')(default=None, related_name='projects', to=orm['domains.Domain'], blank=True, null=True),
keep_default=False)
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '80', 'unique': 'True'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'blank': 'True', 'symmetrical': 'False'})
},
'auth.permission': {
'Meta': {'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission', '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': {'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', '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'})
},
'projects.issuestatus': {
'Meta': {'unique_together': "(('project', 'name'),)", 'object_name': 'IssueStatus', 'ordering': "['project', 'order', 'name']"},
'color': ('django.db.models.fields.CharField', [], {'default': "'#999999'", 'max_length': '20'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}),
'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']", 'related_name': "'issue_statuses'"})
},
'projects.issuetype': {
'Meta': {'unique_together': "(('project', 'name'),)", 'object_name': 'IssueType', 'ordering': "['project', 'order', 'name']"},
'color': ('django.db.models.fields.CharField', [], {'default': "'#999999'", 'max_length': '20'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}),
'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']", 'related_name': "'issue_types'"})
},
'projects.membership': {
'Meta': {'unique_together': "(('user', 'project'),)", 'object_name': 'Membership', 'ordering': "['project', 'role']"},
'created_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'auto_now_add': 'True', 'blank': 'True'}),
'email': ('django.db.models.fields.EmailField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']", 'related_name': "'memberships'"}),
'role': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['users.Role']", 'related_name': "'memberships'"}),
'token': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '60', 'null': 'True', 'blank': 'True'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'null': 'True', 'to': "orm['users.User']", 'related_name': "'memberships'", 'blank': 'True'})
},
'projects.points': {
'Meta': {'unique_together': "(('project', 'name'),)", 'object_name': 'Points', 'ordering': "['project', 'order', 'name']"},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}),
'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']", 'related_name': "'points'"}),
'value': ('django.db.models.fields.FloatField', [], {'default': 'None', 'null': 'True', 'blank': 'True'})
},
'projects.priority': {
'Meta': {'unique_together': "(('project', 'name'),)", 'object_name': 'Priority', 'ordering': "['project', 'order', 'name']"},
'color': ('django.db.models.fields.CharField', [], {'default': "'#999999'", 'max_length': '20'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}),
'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']", 'related_name': "'priorities'"})
},
'projects.project': {
'Meta': {'object_name': 'Project', 'ordering': "['name']"},
'created_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'creation_template': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'null': 'True', 'to': "orm['projects.ProjectTemplate']", 'related_name': "'projects'", 'blank': 'True'}),
'default_issue_status': ('django.db.models.fields.related.OneToOneField', [], {'unique': 'True', 'related_name': "'+'", 'to': "orm['projects.IssueStatus']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
'default_issue_type': ('django.db.models.fields.related.OneToOneField', [], {'unique': 'True', 'related_name': "'+'", 'to': "orm['projects.IssueType']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
'default_points': ('django.db.models.fields.related.OneToOneField', [], {'unique': 'True', 'related_name': "'+'", 'to': "orm['projects.Points']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
'default_priority': ('django.db.models.fields.related.OneToOneField', [], {'unique': 'True', 'related_name': "'+'", 'to': "orm['projects.Priority']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
'default_severity': ('django.db.models.fields.related.OneToOneField', [], {'unique': 'True', 'related_name': "'+'", 'to': "orm['projects.Severity']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
'default_task_status': ('django.db.models.fields.related.OneToOneField', [], {'unique': 'True', 'related_name': "'+'", 'to': "orm['projects.TaskStatus']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
'default_us_status': ('django.db.models.fields.related.OneToOneField', [], {'unique': 'True', 'related_name': "'+'", 'to': "orm['projects.UserStoryStatus']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_backlog_activated': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_issues_activated': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_kanban_activated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_wiki_activated': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'members': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['users.User']", 'related_name': "'projects'", 'through': "orm['projects.Membership']", 'symmetrical': 'False'}),
'modified_date': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '250', 'unique': 'True'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['users.User']", 'related_name': "'owned_projects'"}),
'public': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'slug': ('django.db.models.fields.SlugField', [], {'max_length': '250', 'unique': 'True', 'blank': 'True'}),
'tags': ('picklefield.fields.PickledObjectField', [], {'blank': 'True'}),
'total_milestones': ('django.db.models.fields.IntegerField', [], {'default': '0', 'null': 'True', 'blank': 'True'}),
'total_story_points': ('django.db.models.fields.FloatField', [], {'default': 'None', 'null': 'True'}),
'videoconferences': ('django.db.models.fields.CharField', [], {'max_length': '250', 'null': 'True', 'blank': 'True'}),
'videoconferences_salt': ('django.db.models.fields.CharField', [], {'max_length': '250', 'null': 'True', 'blank': 'True'})
},
'projects.projecttemplate': {
'Meta': {'object_name': 'ProjectTemplate', 'ordering': "['name']"},
'created_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'default_options': ('django_pgjson.fields.JsonField', [], {}),
'default_owner_role': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
'description': ('django.db.models.fields.TextField', [], {}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_backlog_activated': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_issues_activated': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_kanban_activated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_wiki_activated': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'issue_statuses': ('django_pgjson.fields.JsonField', [], {}),
'issue_types': ('django_pgjson.fields.JsonField', [], {}),
'modified_date': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '250'}),
'points': ('django_pgjson.fields.JsonField', [], {}),
'priorities': ('django_pgjson.fields.JsonField', [], {}),
'roles': ('django_pgjson.fields.JsonField', [], {}),
'severities': ('django_pgjson.fields.JsonField', [], {}),
'slug': ('django.db.models.fields.SlugField', [], {'max_length': '250', 'unique': 'True', 'blank': 'True'}),
'task_statuses': ('django_pgjson.fields.JsonField', [], {}),
'us_statuses': ('django_pgjson.fields.JsonField', [], {}),
'videoconferences': ('django.db.models.fields.CharField', [], {'max_length': '250', 'null': 'True', 'blank': 'True'}),
'videoconferences_salt': ('django.db.models.fields.CharField', [], {'max_length': '250', 'null': 'True', 'blank': 'True'})
},
'projects.severity': {
'Meta': {'unique_together': "(('project', 'name'),)", 'object_name': 'Severity', 'ordering': "['project', 'order', 'name']"},
'color': ('django.db.models.fields.CharField', [], {'default': "'#999999'", 'max_length': '20'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}),
'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']", 'related_name': "'severities'"})
},
'projects.taskstatus': {
'Meta': {'unique_together': "(('project', 'name'),)", 'object_name': 'TaskStatus', 'ordering': "['project', 'order', 'name']"},
'color': ('django.db.models.fields.CharField', [], {'default': "'#999999'", 'max_length': '20'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}),
'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']", 'related_name': "'task_statuses'"})
},
'projects.userstorystatus': {
'Meta': {'unique_together': "(('project', 'name'),)", 'object_name': 'UserStoryStatus', 'ordering': "['project', 'order', 'name']"},
'color': ('django.db.models.fields.CharField', [], {'default': "'#999999'", 'max_length': '20'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}),
'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']", 'related_name': "'us_statuses'"}),
'wip_limit': ('django.db.models.fields.IntegerField', [], {'default': 'None', 'null': 'True', 'blank': 'True'})
},
'users.role': {
'Meta': {'unique_together': "(('slug', 'project'),)", 'object_name': 'Role', 'ordering': "['order', 'slug']"},
'computable': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'related_name': "'roles'", 'symmetrical': 'False'}),
'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']", 'related_name': "'roles'"}),
'slug': ('django.db.models.fields.SlugField', [], {'max_length': '250', 'blank': 'True'})
},
'users.user': {
'Meta': {'object_name': 'User', 'ordering': "['username']"},
'color': ('django.db.models.fields.CharField', [], {'default': "'#9d3a9a'", 'max_length': '9', 'blank': 'True'}),
'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', [], {'default': "''", 'max_length': '20', 'blank': 'True'}),
'default_timezone': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'related_name': "'user_set'", 'blank': 'True', 'symmetrical': 'False'}),
'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', [], {'max_length': '30', 'blank': 'True'}),
'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', [], {'max_length': '500', 'null': 'True', 'blank': 'True'}),
'token': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '200', 'null': 'True', 'blank': 'True'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'related_name': "'user_set'", 'blank': 'True', 'symmetrical': 'False'}),
'username': ('django.db.models.fields.CharField', [], {'max_length': '30', 'unique': 'True'})
}
}
complete_apps = ['projects']

View File

@ -31,8 +31,6 @@ from picklefield.fields import PickledObjectField
from django_pgjson.fields import JsonField from django_pgjson.fields import JsonField
from taiga.users.models import Role from taiga.users.models import Role
from taiga.domains.models import DomainMember
from taiga.domains import get_active_domain
from taiga.projects.userstories.models import UserStory from taiga.projects.userstories.models import UserStory
from taiga.base.utils.slug import slugify_uniquely from taiga.base.utils.slug import slugify_uniquely
from taiga.base.utils.dicts import dict_sum from taiga.base.utils.dicts import dict_sum
@ -154,9 +152,6 @@ class Project(ProjectDefaults, models.Model):
blank=True, default=None, blank=True, default=None,
verbose_name=_("creation template")) verbose_name=_("creation template"))
domain = models.ForeignKey("domains.Domain", related_name="projects", null=True, blank=True,
default=None, verbose_name=_("domain"))
class Meta: class Meta:
verbose_name = "project" verbose_name = "project"
verbose_name_plural = "projects" verbose_name_plural = "projects"
@ -441,15 +436,13 @@ class ProjectTemplate(models.Model):
name = models.CharField(max_length=250, null=False, blank=False, name = models.CharField(max_length=250, null=False, blank=False,
verbose_name=_("name")) verbose_name=_("name"))
slug = models.SlugField(max_length=250, null=False, blank=True, slug = models.SlugField(max_length=250, null=False, blank=True,
verbose_name=_("slug")) verbose_name=_("slug"), unique=True)
description = models.TextField(null=False, blank=False, description = models.TextField(null=False, blank=False,
verbose_name=_("description")) verbose_name=_("description"))
created_date = models.DateTimeField(auto_now_add=True, null=False, blank=False, created_date = models.DateTimeField(auto_now_add=True, null=False, blank=False,
verbose_name=_("created date")) verbose_name=_("created date"))
modified_date = models.DateTimeField(auto_now=True, null=False, blank=False, modified_date = models.DateTimeField(auto_now=True, null=False, blank=False,
verbose_name=_("modified date")) verbose_name=_("modified date"))
domain = models.ForeignKey("domains.Domain", related_name="templates", null=True, blank=True,
default=None, verbose_name=_("domain"))
default_owner_role = models.CharField(max_length=50, null=False, default_owner_role = models.CharField(max_length=50, null=False,
blank=False, blank=False,
verbose_name=_("default owner's role")) verbose_name=_("default owner's role"))
@ -481,7 +474,6 @@ class ProjectTemplate(models.Model):
class Meta: class Meta:
verbose_name = "project template" verbose_name = "project template"
verbose_name_plural = "project templates" verbose_name_plural = "project templates"
unique_together = ["slug", "domain"]
ordering = ["name"] ordering = ["name"]
def __str__(self): def __str__(self):
@ -704,21 +696,6 @@ class ProjectTemplate(models.Model):
return project return project
# On membership object is created/changed, update
# role-points relation.
@receiver(signals.post_save, sender=Membership, dispatch_uid='membership_post_save')
def membership_post_save(sender, instance, created, **kwargs):
instance.project.update_role_points()
exists_user_on_domain = instance.project.domain.members.filter(user=instance.user).exists()
if instance.user and instance.project.domain and not exists_user_on_domain:
DomainMember.objects.create(domain=instance.project.domain,
user=instance.user,
email=instance.email,
is_owner=False,
is_staff=False)
# On membership object is deleted, update role-points relation. # On membership object is deleted, update role-points relation.
@receiver(signals.pre_delete, sender=Membership, dispatch_uid='membership_pre_delete') @receiver(signals.pre_delete, sender=Membership, dispatch_uid='membership_pre_delete')
def membership_post_delete(sender, instance, using, **kwargs): def membership_post_delete(sender, instance, using, **kwargs):
@ -748,9 +725,7 @@ def project_post_save(sender, instance, created, **kwargs):
return return
template_slug = getattr(instance, "template", settings.DEFAULT_PROJECT_TEMPLATE) template_slug = getattr(instance, "template", settings.DEFAULT_PROJECT_TEMPLATE)
template = ProjectTemplate.objects.filter(slug=template_slug).get( template = ProjectTemplate.objects.get(slug=template_slug)
models.Q(domain__isnull=True) | models.Q(domain=get_active_domain())
)
template.apply_to_project(instance) template.apply_to_project(instance)
instance.save() instance.save()

View File

@ -15,7 +15,6 @@
from taiga.base.permissions import BasePermission from taiga.base.permissions import BasePermission
from taiga.domains import get_active_domain
class ProjectPermission(BasePermission): class ProjectPermission(BasePermission):
@ -27,28 +26,16 @@ class ProjectPermission(BasePermission):
safe_methods = ["HEAD", "OPTIONS"] safe_methods = ["HEAD", "OPTIONS"]
path_to_project = [] path_to_project = []
class ProjectAdminPermission(BasePermission): class ProjectAdminPermission(BasePermission):
def has_permission(self, request, view): def has_permission(self, request, view):
if request.method in self.safe_methods: if request.method in self.safe_methods:
return True return True
domain = get_active_domain()
if request.method in ["POST", "PUT", "GET", "PATCH"]:
return domain.user_is_staff(request.user) or domain.user_is_owner(request.user)
elif request.method == "DELETE":
return domain.user_is_owner(request.user)
return super().has_permission(request, view) return super().has_permission(request, view)
def has_object_permission(self, request, view, obj): def has_object_permission(self, request, view, obj):
if request.method in self.safe_methods: if request.method in self.safe_methods:
return True return True
domain = get_active_domain()
if request.method in ["POST", "PUT", "GET", "PATCH"]:
return domain.user_is_staff(request.user) or domain.user_is_owner(request.user)
elif request.method == "DELETE":
return domain.user_is_owner(request.user) or (
domain.user_is_staff(request.user) and obj.user == request.user)
return super().has_object_permission(request, view, obj) return super().has_object_permission(request, view, obj)
@ -151,16 +138,5 @@ class RolesPermission(BasePermission):
# Project Templates # Project Templates
class ProjectTemplatePermission(BasePermission): class ProjectTemplatePermission(BasePermission):
def has_permission(self, request, view): # TODO: should be improved in permissions refactor
domain = get_active_domain() pass
return domain.user_is_owner(request.user)
def has_object_permission(self, request, view, obj):
current_domain = get_active_domain()
if obj.domain:
return obj.domain == current_domain and current_domain.user_is_owner(request.user)
else:
if request.method == "GET":
return current_domain.user_is_owner(request.user)
else:
False

View File

@ -18,7 +18,7 @@ from os import path
from rest_framework import serializers from rest_framework import serializers
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from taiga.base.serializers import PickleField, JsonField, AutoDomainField from taiga.base.serializers import PickleField, JsonField
from taiga.users.models import Role from taiga.users.models import Role
from . import models from . import models
@ -87,7 +87,7 @@ class ProjectSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = models.Project model = models.Project
read_only_fields = ("created_date", "modified_date", "owner", "domain") read_only_fields = ("created_date", "modified_date", "owner")
exclude = ("last_us_ref", "last_task_ref", "last_issue_ref") exclude = ("last_us_ref", "last_task_ref", "last_issue_ref")
@ -138,8 +138,6 @@ class RoleSerializer(serializers.ModelSerializer):
class ProjectTemplateSerializer(serializers.ModelSerializer): class ProjectTemplateSerializer(serializers.ModelSerializer):
domain = AutoDomainField(required=False, label=_("Domain"))
default_options = JsonField(required=False, label=_("Default options")) default_options = JsonField(required=False, label=_("Default options"))
us_statuses = JsonField(required=False, label=_("User story's statuses")) us_statuses = JsonField(required=False, label=_("User story's statuses"))
points = JsonField(required=False, label=_("Points")) points = JsonField(required=False, label=_("Points"))

View File

@ -42,13 +42,7 @@ router.register(r"resolver", ResolverViewSet, base_name="resolver")
router.register(r"search", SearchViewSet, base_name="search") router.register(r"search", SearchViewSet, base_name="search")
# Domains
from taiga.domains.api import DomainViewSet
from taiga.domains.api import DomainMembersViewSet
from taiga.projects.api import ProjectAdminViewSet from taiga.projects.api import ProjectAdminViewSet
router.register(r"sites", DomainViewSet, base_name="sites")
router.register(r"site-members", DomainMembersViewSet, base_name="site-members")
router.register(r"site-projects", ProjectAdminViewSet, base_name="site-projects") router.register(r"site-projects", ProjectAdminViewSet, base_name="site-projects")

View File

@ -63,14 +63,12 @@ class PermissionsViewSet(ModelListViewSet):
"add_permission", "change_permission", "delete_permission", "add_permission", "change_permission", "delete_permission",
"add_contenttype", "change_contenttype", "delete_contenttype", "add_contenttype", "change_contenttype", "delete_contenttype",
"add_message", "change_message", "delete_message", "add_message", "change_message", "delete_message",
"add_domain", "change_domain", "delete_domain",
"add_session", "change_session", "delete_session", "add_session", "change_session", "delete_session",
"add_migrationhistory", "change_migrationhistory", "delete_migrationhistory", "add_migrationhistory", "change_migrationhistory", "delete_migrationhistory",
"add_version", "change_version", "delete_version", "add_version", "change_version", "delete_version",
"add_revision", "change_revision", "delete_revision", "add_revision", "change_revision", "delete_revision",
"add_user", "delete_user", "add_user", "delete_user",
"add_project", "add_project",
"add_domainmember", "change_domainmember", "delete_domainmember",
] ]
def get_queryset(self): def get_queryset(self):

View File

@ -23,26 +23,5 @@
"email": "niwi@niwi.be", "email": "niwi@niwi.be",
"date_joined": "2013-04-01T13:48:21.711Z" "date_joined": "2013-04-01T13:48:21.711Z"
} }
},
{
"model": "domains.domain",
"pk": 1,
"fields": {
"domain": "localhost",
"name": "localhost",
"scheme": "http",
"public_register": false
}
},
{
"model": "domains.domainmember",
"pk": 1,
"fields": {
"is_staff": true,
"is_owner": true,
"user": 1,
"domain": 1,
"email": "niwi@niwi.be"
}
} }
] ]

View File

@ -3,7 +3,6 @@ import uuid
import factory import factory
from django.conf import settings from django.conf import settings
import taiga.domains.models
import taiga.projects.models import taiga.projects.models
import taiga.projects.userstories.models import taiga.projects.userstories.models
import taiga.projects.issues.models import taiga.projects.issues.models
@ -12,18 +11,9 @@ import taiga.users.models
import taiga.userstorage.models import taiga.userstorage.models
class DomainFactory(factory.DjangoModelFactory):
FACTORY_FOR = taiga.domains.models.Domain
FACTORY_DJANGO_GET_OR_CREATE = ("domain",)
name = "Default domain"
domain = "default"
scheme = None
public_register = False
class ProjectTemplateFactory(factory.DjangoModelFactory): class ProjectTemplateFactory(factory.DjangoModelFactory):
FACTORY_FOR = taiga.projects.models.ProjectTemplate FACTORY_FOR = taiga.projects.models.ProjectTemplate
FACTORY_DJANGO_GET_OR_CREATE = ("slug", )
name = "Template name" name = "Template name"
slug = settings.DEFAULT_PROJECT_TEMPLATE slug = settings.DEFAULT_PROJECT_TEMPLATE
@ -45,7 +35,6 @@ class ProjectFactory(factory.DjangoModelFactory):
slug = factory.Sequence(lambda n: "project-{}-slug".format(n)) slug = factory.Sequence(lambda n: "project-{}-slug".format(n))
description = "Project description" description = "Project description"
owner = factory.SubFactory("tests.factories.UserFactory") owner = factory.SubFactory("tests.factories.UserFactory")
domain = factory.SubFactory("tests.factories.DomainFactory")
creation_template = factory.SubFactory("tests.factories.ProjectTemplateFactory") creation_template = factory.SubFactory("tests.factories.ProjectTemplateFactory")
@ -67,7 +56,7 @@ class UserFactory(factory.DjangoModelFactory):
class MembershipFactory(factory.DjangoModelFactory): class MembershipFactory(factory.DjangoModelFactory):
FACTORY_FOR = taiga.projects.models.Membership FACTORY_FOR = taiga.projects.models.Membership
token = factory.LazyAttribute(lambda obj: uuid.uuid1()) token = factory.LazyAttribute(lambda obj: str(uuid.uuid1()))
project = factory.SubFactory("tests.factories.ProjectFactory") project = factory.SubFactory("tests.factories.ProjectFactory")
role = factory.SubFactory("tests.factories.RoleFactory") role = factory.SubFactory("tests.factories.RoleFactory")
user = factory.SubFactory("tests.factories.UserFactory") user = factory.SubFactory("tests.factories.UserFactory")

View File

@ -11,11 +11,13 @@ def seq():
from taiga.projects.references import sequences as seq from taiga.projects.references import sequences as seq
return seq return seq
@pytest.fixture @pytest.fixture
def refmodels(): def refmodels():
from taiga.projects.references import models from taiga.projects.references import models
return models return models
@pytest.mark.django_db @pytest.mark.django_db
def test_sequences(seq): def test_sequences(seq):
seqname = "foo" seqname = "foo"
@ -47,14 +49,10 @@ def test_sequences(seq):
@pytest.mark.django_db @pytest.mark.django_db
def test_unique_reference_per_project(seq, refmodels): def test_unique_reference_per_project(seq, refmodels):
# management.call_command("loaddata", "initial_project_templates")
domain = factories.DomainFactory(public_register=True)
settings.DOMAIN_ID = domain.id
project = factories.ProjectFactory.create() project = factories.ProjectFactory.create()
seqname = refmodels.make_sequence_name(project) seqname = refmodels.make_sequence_name(project)
assert seqname == "references_project1" assert seqname == "references_project{0}".format(project.id)
assert seq.exists(seqname) assert seq.exists(seqname)
assert refmodels.make_unique_reference_id(project, create=True) == 1 assert refmodels.make_unique_reference_id(project, create=True) == 1