Merge pull request #42 from taigaio/sites-remove
Sites remove & Test refactorremotes/origin/enhancement/email-actions
commit
a1c1491a84
|
@ -9,3 +9,4 @@ media
|
||||||
*.mo
|
*.mo
|
||||||
.venv
|
.venv
|
||||||
.coverage
|
.coverage
|
||||||
|
.\#*
|
|
@ -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"
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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"]
|
|
|
@ -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)
|
|
|
@ -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()
|
|
|
@ -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()
|
|
|
@ -1,12 +0,0 @@
|
||||||
[
|
|
||||||
{
|
|
||||||
"model": "domains.domain",
|
|
||||||
"fields": {
|
|
||||||
"public_register": false,
|
|
||||||
"domain": "localhost",
|
|
||||||
"scheme": null,
|
|
||||||
"name": "localhost"
|
|
||||||
},
|
|
||||||
"pk": 1
|
|
||||||
}
|
|
||||||
]
|
|
|
@ -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
|
|
|
@ -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']
|
|
|
@ -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']
|
|
|
@ -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
|
|
|
@ -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']
|
|
|
@ -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']
|
|
|
@ -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']
|
|
|
@ -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()
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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)
|
|
|
@ -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)
|
||||||
|
|
|
@ -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]
|
||||||
|
|
||||||
|
|
|
@ -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))
|
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -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))
|
||||||
|
|
|
@ -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']
|
|
@ -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()
|
||||||
|
|
|
@ -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
|
|
||||||
|
|
|
@ -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"))
|
||||||
|
|
|
@ -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")
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -1,50 +1,42 @@
|
||||||
from unittest import mock
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
|
|
||||||
from .. import factories
|
from .. import factories
|
||||||
|
|
||||||
pytestmark = pytest.mark.django_db
|
pytestmark = pytest.mark.django_db
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
def setup_module(module):
|
def register_form():
|
||||||
module.patcher = mock.patch("taiga.domains.base.get_default_domain",
|
return {"username": "username",
|
||||||
mock.Mock(return_value=factories.DomainFactory()))
|
"password": "password",
|
||||||
module.patcher.start()
|
"first_name": "fname",
|
||||||
|
"last_name": "lname",
|
||||||
def teardown_module(module):
|
"email": "user@email.com",
|
||||||
module.patcher.stop()
|
"type": "public"}
|
||||||
|
|
||||||
|
|
||||||
class TestPublicRegistration:
|
def test_respond_201_if_domain_allows_public_registration(client, register_form):
|
||||||
@classmethod
|
response = client.post(reverse("auth-register"), register_form)
|
||||||
def setup_class(cls):
|
assert response.status_code == 201
|
||||||
cls.form = {"username": "username", "password": "password", "first_name": "fname",
|
|
||||||
"last_name": "lname", "email": "user@email.com", "type": "public"}
|
|
||||||
|
|
||||||
def test_respond_201_if_domain_allows_public_registration(self, client):
|
|
||||||
domain = factories.DomainFactory(public_register=True)
|
|
||||||
response = client.post(reverse("auth-register"), self.form, HTTP_X_HOST=domain.domain)
|
|
||||||
assert response.status_code == 201
|
|
||||||
|
|
||||||
def test_respond_400_if_domain_does_not_allow_public_registration(self, client):
|
|
||||||
domain = factories.DomainFactory(public_register=False)
|
|
||||||
response = client.post(reverse("auth-register"), self.form, HTTP_X_HOST=domain.domain)
|
|
||||||
assert response.status_code == 400
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.xfail
|
def test_respond_400_if_domain_does_not_allow_public_registration(client, register_form):
|
||||||
class TestPrivateRegistration:
|
response = client.post(reverse("auth-register"), register_form)
|
||||||
@classmethod
|
assert response.status_code == 400
|
||||||
def setup_class(cls):
|
|
||||||
cls.form = {"username": "username", "password": "password", "first_name": "fname",
|
|
||||||
"last_name": "lname", "email": "user@email.com", "type": "private",
|
|
||||||
"existing": "1"}
|
|
||||||
|
|
||||||
def test_respond_201_if_domain_allows_public_registration(self, client):
|
|
||||||
domain = factories.DomainFactory(public_register=True)
|
def test_respond_201_if_domain_allows_public_registration(client, register_form):
|
||||||
membership = factories.MembershipFactory()
|
user = factories.UserFactory()
|
||||||
headers = {"HTTP_X_HOST": domain.domain}
|
membership = factories.MembershipFactory(user=user)
|
||||||
response = client.post(reverse("auth-register"), self.form, **headers)
|
|
||||||
assert response.status_code == 201
|
register_form.update({
|
||||||
|
"type": "private",
|
||||||
|
"existing": "1",
|
||||||
|
"token": membership.token,
|
||||||
|
"username": user.username,
|
||||||
|
"email": user.email,
|
||||||
|
"password": user.username,
|
||||||
|
})
|
||||||
|
|
||||||
|
response = client.post(reverse("auth-register"), register_form)
|
||||||
|
assert response.status_code == 201
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -8,211 +8,167 @@ from .. import factories
|
||||||
pytestmark = pytest.mark.django_db
|
pytestmark = pytest.mark.django_db
|
||||||
|
|
||||||
|
|
||||||
class TestListStorageEntries(object):
|
def test_list_userstories(client):
|
||||||
def _load_initial_data(self):
|
user1 = factories.UserFactory()
|
||||||
self.user1 = factories.UserFactory()
|
user2 = factories.UserFactory()
|
||||||
self.user2 = factories.UserFactory()
|
storage11 = factories.StorageEntryFactory(owner=user1)
|
||||||
self.storage11 = factories.StorageEntryFactory(owner=self.user1)
|
storage12 = factories.StorageEntryFactory(owner=user1)
|
||||||
self.storage12 = factories.StorageEntryFactory(owner=self.user1)
|
storage13 = factories.StorageEntryFactory(owner=user1)
|
||||||
self.storage13 = factories.StorageEntryFactory(owner=self.user1)
|
storage21 = factories.StorageEntryFactory(owner=user2)
|
||||||
self.storage21 = factories.StorageEntryFactory(owner=self.user2)
|
|
||||||
|
|
||||||
def test_list_by_anonymous_user(self, client):
|
# List by anonumous user
|
||||||
self._load_initial_data()
|
response = client.get(reverse("user-storage-list"))
|
||||||
response = client.get(reverse("user-storage-list"))
|
assert response.status_code == 401
|
||||||
assert response.status_code == 401
|
|
||||||
|
|
||||||
def test_list_only_user1_entriees(self, client):
|
# List own entries
|
||||||
self._load_initial_data()
|
client.login(username=user1.username, password=user1.username)
|
||||||
response = client.login(username=self.user1.username, password=self.user1.username)
|
response = client.get(reverse("user-storage-list"))
|
||||||
response = client.get(reverse("user-storage-list"))
|
assert response.status_code == 200
|
||||||
assert response.status_code == 200
|
assert len(response.data) == 3
|
||||||
entries = response.data
|
|
||||||
assert len(entries) == 3
|
|
||||||
response = client.logout()
|
|
||||||
|
|
||||||
def test_list_only_user2_entriees(self, client):
|
client.login(username=user2.username, password=user2.username)
|
||||||
self._load_initial_data()
|
response = client.get(reverse("user-storage-list"))
|
||||||
response = client.login(username=self.user2.username, password=self.user2.username)
|
assert response.status_code == 200
|
||||||
response = client.get(reverse("user-storage-list"))
|
assert len(response.data) == 1
|
||||||
assert response.status_code == 200
|
|
||||||
entries = response.data
|
|
||||||
assert len(entries) == 1
|
|
||||||
response = client.logout()
|
|
||||||
|
|
||||||
def test_list_only_user1_entriees_filter_by_keys(self, client):
|
# Filter results by key
|
||||||
self._load_initial_data()
|
client.login(username=user1.username, password=user1.username)
|
||||||
response = client.login(username=self.user1.username, password=self.user1.username)
|
keys = ",".join([storage11.key, storage13.key])
|
||||||
keys = "{},{}".format(self.storage11.key, self.storage13.key)
|
url = "{}?keys={}".format(reverse("user-storage-list"), keys)
|
||||||
response = client.get("{}?keys={}".format(reverse("user-storage-list"), keys))
|
|
||||||
assert response.status_code == 200
|
response = client.get(url)
|
||||||
entries = response.data
|
assert response.status_code == 200
|
||||||
assert len(entries) == 2
|
assert len(response.data) == 2
|
||||||
response = client.logout()
|
|
||||||
|
client.logout()
|
||||||
|
|
||||||
|
|
||||||
class TestViewStorageEntries(object):
|
def test_view_storage_entries(client):
|
||||||
def _load_initial_data(self):
|
user1 = factories.UserFactory()
|
||||||
self.user1 = factories.UserFactory()
|
user2 = factories.UserFactory()
|
||||||
self.user2 = factories.UserFactory()
|
storage11 = factories.StorageEntryFactory(owner=user1)
|
||||||
self.storage11 = factories.StorageEntryFactory(owner=self.user1)
|
|
||||||
|
|
||||||
def test_view_an_entry_by_anonymous_user(self, client):
|
# Get by anonymous user
|
||||||
self._load_initial_data()
|
response = client.get(reverse("user-storage-detail", args=[storage11.key]))
|
||||||
response = client.get(reverse("user-storage-detail", args=[self.storage11.key]))
|
assert response.status_code == 401
|
||||||
assert response.status_code == 401
|
|
||||||
|
|
||||||
def test_view_an_entry(self, client):
|
# Get single entry
|
||||||
self._load_initial_data()
|
client.login(username=user1.username, password=user1.username)
|
||||||
response = client.login(username=self.user1.username, password=self.user1.username)
|
response = client.get(reverse("user-storage-detail", args=[storage11.key]))
|
||||||
response = client.get(reverse("user-storage-detail", args=[self.storage11.key]))
|
assert response.status_code == 200
|
||||||
assert response.status_code == 200
|
assert response.data["key"] == storage11.key
|
||||||
entry = response.data
|
assert response.data["value"] == storage11.value
|
||||||
assert entry["key"] == self.storage11.key
|
|
||||||
assert entry["value"] == self.storage11.value
|
|
||||||
response = client.logout()
|
|
||||||
|
|
||||||
def test_view_an_entry_by_incorrect_user(self, client):
|
# Get not existent key
|
||||||
self._load_initial_data()
|
client.login(username=user2.username, password=user2.username)
|
||||||
response = client.login(username=self.user2.username, password=self.user2.username)
|
response = client.get(reverse("user-storage-detail", args=[storage11.key]))
|
||||||
response = client.get(reverse("user-storage-detail", args=[self.storage11.key]))
|
assert response.status_code == 404
|
||||||
assert response.status_code == 404
|
|
||||||
response = client.logout()
|
|
||||||
|
|
||||||
def test_view_non_existent_entry(self, client):
|
response = client.get(reverse("user-storage-detail", args=["foobar"]))
|
||||||
self._load_initial_data()
|
assert response.status_code == 404
|
||||||
response = client.login(username=self.user1.username, password=self.user1.username)
|
|
||||||
response = client.get(reverse("user-storage-detail", args=["foo"]))
|
client.logout()
|
||||||
assert response.status_code == 404
|
|
||||||
response = client.logout()
|
|
||||||
|
|
||||||
|
|
||||||
class TestCreateStorageEntries(object):
|
def test_create_entries(client):
|
||||||
@classmethod
|
user1 = factories.UserFactory()
|
||||||
def setup_class(cls):
|
user2 = factories.UserFactory()
|
||||||
cls.form = {"key": "foo",
|
storage11 = factories.StorageEntryFactory(owner=user1)
|
||||||
"value": "bar"}
|
|
||||||
cls.form_without_key = {"value": "bar"}
|
|
||||||
cls.form_without_value = {"key": "foo"}
|
|
||||||
|
|
||||||
def _load_initial_data(self):
|
form = {"key": "foo",
|
||||||
self.user1 = factories.UserFactory()
|
"value": "bar"}
|
||||||
self.user2 = factories.UserFactory()
|
form_without_key = {"value": "bar"}
|
||||||
self.storage11 = factories.StorageEntryFactory(owner=self.user1)
|
form_without_value = {"key": "foo"}
|
||||||
|
error_form = {"key": storage11.key,
|
||||||
|
"value": "bar"}
|
||||||
|
|
||||||
def test_create_entry_by_anonymous_user_with_error(self, client):
|
# Create entry by anonymous user
|
||||||
self._load_initial_data()
|
response = client.post(reverse("user-storage-list"), form)
|
||||||
response = client.post(reverse("user-storage-list"), self.form)
|
assert response.status_code == 401
|
||||||
assert response.status_code == 401
|
|
||||||
|
|
||||||
def test_create_entry_successfully(self, client):
|
# Create by logged user
|
||||||
self._load_initial_data()
|
client.login(username=user1.username, password=user1.username)
|
||||||
response = client.login(username=self.user1.username, password=self.user1.username)
|
response = client.post(reverse("user-storage-list"), form)
|
||||||
response = client.post(reverse("user-storage-list"), self.form)
|
assert response.status_code == 201
|
||||||
assert response.status_code == 201
|
response = client.get(reverse("user-storage-detail", args=[form["key"]]))
|
||||||
response = client.get(reverse("user-storage-detail", args=[self.form["key"]]))
|
assert response.status_code == 200
|
||||||
assert response.status_code == 200
|
|
||||||
response = client.logout()
|
|
||||||
|
|
||||||
def test_create_entry_with_incorret_form_error(self, client):
|
# Wrong data
|
||||||
self._load_initial_data()
|
client.login(username=user1.username, password=user1.username)
|
||||||
response = client.login(username=self.user1.username, password=self.user1.username)
|
response = client.post(reverse("user-storage-list"), form_without_key)
|
||||||
response = client.post(reverse("user-storage-list"), self.form_without_key)
|
assert response.status_code == 400
|
||||||
assert response.status_code == 400
|
response = client.post(reverse("user-storage-list"), form_without_value)
|
||||||
response = client.post(reverse("user-storage-list"), self.form_without_value)
|
assert response.status_code == 400
|
||||||
assert response.status_code == 400
|
response = client.post(reverse("user-storage-list"), error_form)
|
||||||
response = client.logout()
|
assert response.status_code == 400
|
||||||
|
|
||||||
def test_create_entry_with_integrity_error(self, client):
|
client.logout()
|
||||||
self._load_initial_data()
|
|
||||||
response = client.login(username=self.user1.username, password=self.user1.username)
|
|
||||||
error_form = {"key": self.storage11.key,
|
|
||||||
"value": "bar"}
|
|
||||||
response = client.post(reverse("user-storage-list"), error_form)
|
|
||||||
assert response.status_code == 400
|
|
||||||
response = client.logout()
|
|
||||||
|
|
||||||
|
|
||||||
class TestUpdateStorageEntries(object):
|
def test_update_entries(client):
|
||||||
@classmethod
|
user1 = factories.UserFactory()
|
||||||
def setup_class(cls):
|
user2 = factories.UserFactory()
|
||||||
cls.form = {"value": "bar"}
|
storage11 = factories.StorageEntryFactory(owner=user1)
|
||||||
|
|
||||||
def _load_initial_data(self):
|
# Update by anonymous user
|
||||||
self.user1 = factories.UserFactory()
|
form = {"value": "bar", "key": storage11.key}
|
||||||
self.user2 = factories.UserFactory()
|
response = client.put(reverse("user-storage-detail", args=[storage11.key]),
|
||||||
self.storage11 = factories.StorageEntryFactory(owner=self.user1)
|
json.dumps(form),
|
||||||
|
content_type='application/json')
|
||||||
|
assert response.status_code == 401
|
||||||
|
|
||||||
def test_update_entry_by_anonymous_user(self, client):
|
# Update by logged user
|
||||||
self._load_initial_data()
|
client.login(username=user1.username, password=user1.username)
|
||||||
self.form["key"] = self.storage11.key
|
form = {"value": "bar", "key": storage11.key}
|
||||||
response = client.put(reverse("user-storage-detail", args=[self.storage11.key]),
|
|
||||||
json.dumps(self.form),
|
|
||||||
content_type='application/json')
|
|
||||||
assert response.status_code == 401
|
|
||||||
|
|
||||||
def test_update_entry(self, client):
|
response = client.put(reverse("user-storage-detail", args=[storage11.key]),
|
||||||
self._load_initial_data()
|
json.dumps(form),
|
||||||
response = client.login(username=self.user1.username, password=self.user1.username)
|
content_type='application/json')
|
||||||
self.form["key"] = self.storage11.key
|
assert response.status_code == 200
|
||||||
response = client.put(reverse("user-storage-detail", args=[self.storage11.key]),
|
response = client.get(reverse("user-storage-detail", args=[storage11.key]))
|
||||||
json.dumps(self.form),
|
assert response.status_code == 200
|
||||||
content_type='application/json')
|
assert response.data["value"] == form["value"]
|
||||||
assert response.status_code == 200
|
|
||||||
response = client.get(reverse("user-storage-detail", args=[self.storage11.key]))
|
|
||||||
assert response.status_code == 200
|
|
||||||
entry = response.data
|
|
||||||
assert entry["value"] == self.form["value"]
|
|
||||||
response = client.logout()
|
|
||||||
|
|
||||||
def test_update_non_existent_entry(self, client):
|
# Update not existing entry
|
||||||
self._load_initial_data()
|
form = {"value": "bar", "key": "foo"}
|
||||||
response = client.login(username=self.user1.username, password=self.user1.username)
|
response = client.get(reverse("user-storage-detail", args=[form["key"]]))
|
||||||
self.form["key"] = "foo"
|
assert response.status_code == 404
|
||||||
response = client.get(reverse("user-storage-detail", args=[self.form["key"]]))
|
response = client.put(reverse("user-storage-detail", args=[form["key"]]),
|
||||||
assert response.status_code == 404
|
json.dumps(form),
|
||||||
response = client.put(reverse("user-storage-detail", args=[self.form["key"]]),
|
content_type='application/json')
|
||||||
json.dumps(self.form),
|
assert response.status_code == 201
|
||||||
content_type='application/json')
|
response = client.get(reverse("user-storage-detail", args=[form["key"]]))
|
||||||
assert response.status_code == 201
|
assert response.status_code == 200
|
||||||
response = client.get(reverse("user-storage-detail", args=[self.form["key"]]))
|
assert response.data["value"] == form["value"]
|
||||||
assert response.status_code == 200
|
|
||||||
entry = response.data
|
client.logout()
|
||||||
assert entry["value"] == self.form["value"]
|
|
||||||
response = client.logout()
|
|
||||||
|
|
||||||
|
|
||||||
class TestDeleteStorageEntries(object):
|
|
||||||
def _load_initial_data(self):
|
|
||||||
self.user1 = factories.UserFactory()
|
|
||||||
self.user2 = factories.UserFactory()
|
|
||||||
self.storage11 = factories.StorageEntryFactory(owner=self.user1)
|
|
||||||
|
|
||||||
def test_delete_entry_by_anonymous_user(self, client):
|
def test_delete_storage_entry(client):
|
||||||
self._load_initial_data()
|
user1 = factories.UserFactory()
|
||||||
response = client.delete(reverse("user-storage-detail", args=[self.storage11.key]))
|
user2 = factories.UserFactory()
|
||||||
assert response.status_code == 401
|
storage11 = factories.StorageEntryFactory(owner=user1)
|
||||||
|
|
||||||
def test_delete_entry(self, client):
|
# Delete by anonumous user
|
||||||
self._load_initial_data()
|
response = client.delete(reverse("user-storage-detail", args=[storage11.key]))
|
||||||
response = client.login(username=self.user1.username, password=self.user1.username)
|
assert response.status_code == 401
|
||||||
key = self.storage11.key
|
|
||||||
response = client.delete(reverse("user-storage-detail", args=[key]))
|
|
||||||
assert response.status_code == 204
|
|
||||||
response = client.get(reverse("user-storage-detail", args=[key]))
|
|
||||||
assert response.status_code == 404
|
|
||||||
response = client.logout()
|
|
||||||
|
|
||||||
def test_delete_entry_by_incorrect_user(self, client):
|
# Delete by logged user
|
||||||
self._load_initial_data()
|
client.login(username=user1.username, password=user1.username)
|
||||||
response = client.login(username=self.user2.username, password=self.user2.username)
|
response = client.delete(reverse("user-storage-detail", args=[storage11.key]))
|
||||||
response = client.delete(reverse("user-storage-detail", args=[self.storage11.key]))
|
assert response.status_code == 204
|
||||||
assert response.status_code == 404
|
|
||||||
response = client.logout()
|
response = client.get(reverse("user-storage-detail", args=[storage11.key]))
|
||||||
|
assert response.status_code == 404
|
||||||
|
|
||||||
|
# Delete not existent entry
|
||||||
|
response = client.delete(reverse("user-storage-detail", args=["foo"]))
|
||||||
|
assert response.status_code == 404
|
||||||
|
|
||||||
|
client.login(username=user2.username, password=user2.username)
|
||||||
|
response = client.delete(reverse("user-storage-detail", args=[storage11.key]))
|
||||||
|
assert response.status_code == 404
|
||||||
|
|
||||||
|
client.logout()
|
||||||
|
|
||||||
def test_delete_non_existent_entry(self, client):
|
|
||||||
self._load_initial_data()
|
|
||||||
response = client.login(username=self.user1.username, password=self.user1.username)
|
|
||||||
response = client.delete(reverse("user-storage-detail", args=["foo"]))
|
|
||||||
assert response.status_code == 404
|
|
||||||
response = client.logout()
|
|
||||||
|
|
Loading…
Reference in New Issue