diff --git a/taiga/base/filters.py b/taiga/base/filters.py
index 5c73ed95..1cc73a60 100644
--- a/taiga/base/filters.py
+++ b/taiga/base/filters.py
@@ -17,15 +17,13 @@ import operator
from functools import reduce
import logging
+from django.apps import apps
from django.db.models import Q
-from django.db.models.sql.where import ExtraWhere, OR, AND
from rest_framework import filters
-from taiga.base import tags
from taiga.base import exceptions as exc
-from taiga.projects.models import Membership
logger = logging.getLogger(__name__)
@@ -101,7 +99,8 @@ class PermissionBasedFilterBackend(FilterBackend):
try:
project_id = int(request.QUERY_PARAMS["project"])
except:
- logger.error("Filtering project diferent value than an integer: {}".format(request.QUERY_PARAMS["project"]))
+ logger.error("Filtering project diferent value than an integer: {}".format(
+ request.QUERY_PARAMS["project"]))
raise exc.BadRequest("'project' must be an integer value.")
qs = queryset
@@ -109,7 +108,8 @@ class PermissionBasedFilterBackend(FilterBackend):
if request.user.is_authenticated() and request.user.is_superuser:
qs = qs
elif request.user.is_authenticated():
- memberships_qs = Membership.objects.filter(user=request.user)
+ membership_model = apps.get_model('projects', 'Membership')
+ memberships_qs = membership_model.objects.filter(user=request.user)
if project_id:
memberships_qs = memberships_qs.filter(project_id=project_id)
memberships_qs = memberships_qs.filter(Q(role__permissions__contains=[self.permission]) |
@@ -187,7 +187,8 @@ class CanViewProjectObjFilterBackend(FilterBackend):
try:
project_id = int(request.QUERY_PARAMS["project"])
except:
- logger.error("Filtering project diferent value than an integer: {}".format(request.QUERY_PARAMS["project"]))
+ logger.error("Filtering project diferent value than an integer: {}".format(
+ request.QUERY_PARAMS["project"]))
raise exc.BadRequest("'project' must be an integer value.")
qs = queryset
@@ -195,7 +196,8 @@ class CanViewProjectObjFilterBackend(FilterBackend):
if request.user.is_authenticated() and request.user.is_superuser:
qs = qs
elif request.user.is_authenticated():
- memberships_qs = Membership.objects.filter(user=request.user)
+ membership_model = apps.get_model("projects", "Membership")
+ memberships_qs = membership_model.objects.filter(user=request.user)
if project_id:
memberships_qs = memberships_qs.filter(project_id=project_id)
memberships_qs = memberships_qs.filter(Q(role__permissions__contains=['view_project']) |
@@ -203,7 +205,8 @@ class CanViewProjectObjFilterBackend(FilterBackend):
projects_list = [membership.project_id for membership in memberships_qs]
- qs = qs.filter(Q(id__in=projects_list) | Q(public_permissions__contains=["view_project"]))
+ qs = qs.filter((Q(id__in=projects_list) |
+ Q(public_permissions__contains=["view_project"])))
else:
qs = qs.filter(public_permissions__contains=["view_project"])
@@ -221,6 +224,25 @@ class IsProjectMemberFilterBackend(FilterBackend):
return super().filter_queryset(request, queryset.distinct(), view)
+
+class MembersFilterBackend(filters.BaseFilterBackend):
+ def filter_queryset(self, request, queryset, view):
+ project_id = request.QUERY_PARAMS.get('project', None)
+ if project_id:
+ project_model = apps.get_model('projects', 'Project')
+ project = get_object_or_404(project_model, pk=project_id)
+ if (request.user.is_authenticated() and
+ project.memberships.filter(user=request.user).exists()):
+ return queryset.filter(memberships__project=project).distinct()
+ else:
+ raise exc.PermissionDenied(_("You don't have permisions to see this project users."))
+
+ if request.user.is_superuser:
+ return queryset
+
+ return []
+
+
class BaseIsProjectAdminFilterBackend(object):
def get_project_ids(self, request, view):
project_id = None
@@ -233,7 +255,8 @@ class BaseIsProjectAdminFilterBackend(object):
if not request.user.is_authenticated():
return []
- memberships_qs = Membership.objects.filter(user=request.user, is_owner=True)
+ membership_model = apps.get_model('projects', 'Membership')
+ memberships_qs = membership_model.objects.filter(user=request.user, is_owner=True)
if project_id:
memberships_qs = memberships_qs.filter(project_id=project_id)
diff --git a/taiga/projects/api.py b/taiga/projects/api.py
index 406438a7..f9f605a5 100644
--- a/taiga/projects/api.py
+++ b/taiga/projects/api.py
@@ -20,7 +20,8 @@ from django.db.models import signals
from django.core.exceptions import ValidationError
from django.utils.translation import ugettext_lazy as _
-from taiga.base import filters, response
+from taiga.base import filters
+from taiga.base import response
from taiga.base import exceptions as exc
from taiga.base.decorators import list_route
from taiga.base.decorators import detail_route
@@ -32,7 +33,6 @@ from taiga.base.utils.slug import slugify_uniquely
from taiga.projects.mixins.ordering import BulkUpdateOrderMixin
from taiga.projects.mixins.on_destroy import MoveOnDestroyMixin
-from taiga.users.models import Role
from taiga.projects.userstories.models import UserStory
from taiga.projects.tasks.models import Task
from taiga.projects.issues.models import Issue
@@ -325,7 +325,7 @@ class ProjectTemplateViewSet(ModelCrudViewSet):
######################################################
-## Members Invitations and Roles
+## Members & Invitations
######################################################
class MembershipViewSet(ModelCrudViewSet):
@@ -403,20 +403,3 @@ class InvitationViewSet(ModelListViewSet):
def list(self, *args, **kwargs):
raise exc.PermissionDenied(_("You don't have permisions to see that."))
-
-
-class RolesViewSet(ModelCrudViewSet):
- model = Role
- serializer_class = serializers.RoleSerializer
- permission_classes = (permissions.RolesPermission, )
- filter_backends = (filters.CanViewProjectFilterBackend,)
- filter_fields = ('project',)
-
- def pre_delete(self, obj):
- move_to = self.request.QUERY_PARAMS.get('moveTo', None)
- if move_to:
- role_dest = get_object_or_404(self.model, project=obj.project, id=move_to)
- qs = models.Membership.objects.filter(project_id=obj.project.pk, role=obj)
- qs.update(role=role_dest)
-
- super().pre_delete(obj)
diff --git a/taiga/projects/permissions.py b/taiga/projects/permissions.py
index 4d0fcdbd..5fa9180b 100644
--- a/taiga/projects/permissions.py
+++ b/taiga/projects/permissions.py
@@ -16,9 +16,13 @@
from django.utils.translation import ugettext_lazy as _
-from taiga.base.api.permissions import (TaigaResourcePermission, HasProjectPerm,
- IsAuthenticated, IsProjectOwner,
- AllowAny, IsSuperUser, PermissionComponent)
+from taiga.base.api.permissions import TaigaResourcePermission
+from taiga.base.api.permissions import HasProjectPerm
+from taiga.base.api.permissions import IsAuthenticated
+from taiga.base.api.permissions import IsProjectOwner
+from taiga.base.api.permissions import AllowAny
+from taiga.base.api.permissions import IsSuperUser
+from taiga.base.api.permissions import PermissionComponent
from taiga.base import exceptions as exc
from taiga.projects.models import Membership
@@ -32,8 +36,8 @@ class CanLeaveProject(PermissionComponent):
try:
if not services.can_user_leave_project(request.user, obj):
- raise exc.PermissionDenied(_("You can't leave the project if there are no more owners"))
-
+ raise exc.PermissionDenied(_("You can't leave the project if there are no "
+ "more owners"))
return True
except Membership.DoesNotExist:
return False
@@ -140,14 +144,6 @@ class IssueTypePermission(TaigaResourcePermission):
bulk_update_order_perms = IsProjectOwner()
-class RolesPermission(TaigaResourcePermission):
- retrieve_perms = HasProjectPerm('view_project')
- create_perms = IsProjectOwner()
- update_perms = IsProjectOwner()
- destroy_perms = IsProjectOwner()
- list_perms = AllowAny()
-
-
# Project Templates
class ProjectTemplatePermission(TaigaResourcePermission):
diff --git a/taiga/projects/serializers.py b/taiga/projects/serializers.py
index 5c89484f..ad0e99a7 100644
--- a/taiga/projects/serializers.py
+++ b/taiga/projects/serializers.py
@@ -14,27 +14,32 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
-from os import path
from django.utils.translation import ugettext_lazy as _
from django.db.models import Q
from rest_framework import serializers
-from taiga.base.serializers import JsonField, PgArrayField, ModelSerializer, TagsColorsField
-from taiga.users.models import Role, User
+from taiga.base.serializers import JsonField
+from taiga.base.serializers import PgArrayField
+from taiga.base.serializers import ModelSerializer
+from taiga.base.serializers import TagsColorsField
from taiga.users.services import get_photo_or_gravatar_url
from taiga.users.serializers import UserSerializer
+from taiga.users.serializers import ProjectRoleSerializer
from taiga.users.validators import RoleExistsValidator
-from taiga.permissions.service import get_user_project_permissions, is_project_owner
+from taiga.permissions.service import get_user_project_permissions
+from taiga.permissions.service import is_project_owner
from . import models
from . import services
from . validators import ProjectExistsValidator
-# User Stories common serializers
+######################################################
+## Custom values for selectors
+######################################################
class PointsSerializer(ModelSerializer):
class Meta:
@@ -69,10 +74,12 @@ class UserStoryStatusSerializer(ModelSerializer):
qs = None
# If the user story status exists:
if self.object and attrs.get("name", None):
- qs = models.UserStoryStatus.objects.filter(project=self.object.project, name=attrs[source])
+ qs = models.UserStoryStatus.objects.filter(project=self.object.project,
+ name=attrs[source])
if not self.object and attrs.get("project", None) and attrs.get("name", None):
- qs = models.UserStoryStatus.objects.filter(project=attrs["project"], name=attrs[source])
+ qs = models.UserStoryStatus.objects.filter(project=attrs["project"],
+ name=attrs[source])
if qs and qs.exists():
raise serializers.ValidationError("Name duplicated for the project")
@@ -80,8 +87,6 @@ class UserStoryStatusSerializer(ModelSerializer):
return attrs
-# Task common serializers
-
class TaskStatusSerializer(ModelSerializer):
class Meta:
model = models.TaskStatus
@@ -103,7 +108,6 @@ class TaskStatusSerializer(ModelSerializer):
return attrs
-# Issues common serializers
class SeveritySerializer(ModelSerializer):
class Meta:
@@ -142,13 +146,16 @@ class IssueTypeSerializer(ModelSerializer):
model = models.IssueType
-# Projects
+######################################################
+## Members
+######################################################
class MembershipSerializer(ModelSerializer):
role_name = serializers.CharField(source='role.name', required=False, read_only=True)
full_name = serializers.CharField(source='user.get_full_name', required=False, read_only=True)
user_email = serializers.EmailField(source='user.email', required=False, read_only=True)
- is_user_active = serializers.BooleanField(source='user.is_active', required=False, read_only=True)
+ is_user_active = serializers.BooleanField(source='user.is_active', required=False,
+ read_only=True)
email = serializers.EmailField(required=True)
color = serializers.CharField(source='user.color', required=False, read_only=True)
photo = serializers.SerializerMethodField("get_photo")
@@ -210,7 +217,8 @@ class MembershipSerializer(ModelSerializer):
if project is None:
project = self.object.project
- if self.object and not services.project_has_valid_owners(project, exclude_user=self.object.user):
+ if (self.object and
+ not services.project_has_valid_owners(project, exclude_user=self.object.user)):
raise serializers.ValidationError(_("At least one of the user must be an active admin"))
return attrs
@@ -229,6 +237,21 @@ class ProjectMembershipSerializer(ModelSerializer):
return get_photo_or_gravatar_url(project.user)
+class MemberBulkSerializer(RoleExistsValidator, serializers.Serializer):
+ email = serializers.EmailField()
+ role_id = serializers.IntegerField()
+
+
+class MembersBulkSerializer(ProjectExistsValidator, serializers.Serializer):
+ project_id = serializers.IntegerField()
+ bulk_memberships = MemberBulkSerializer(many=True)
+ invitation_extra_text = serializers.CharField(required=False, max_length=255)
+
+
+######################################################
+## Projects
+######################################################
+
class ProjectSerializer(ModelSerializer):
tags = PgArrayField(required=False)
anon_permissions = PgArrayField(required=False)
@@ -300,23 +323,20 @@ class ProjectDetailSerializer(ProjectSerializer):
return serializer.data
-class ProjectRoleSerializer(ModelSerializer):
+######################################################
+## Starred
+######################################################
+
+class StarredSerializer(ModelSerializer):
class Meta:
- model = Role
- fields = ('id', 'name', 'slug', 'order', 'computable')
+ model = models.Project
+ fields = ['id', 'name', 'slug']
-class RoleSerializer(ModelSerializer):
- members_count = serializers.SerializerMethodField("get_members_count")
- permissions = PgArrayField(required=False)
-
- class Meta:
- model = Role
- fields = ('id', 'name', 'permissions', 'computable', 'project', 'order', 'members_count')
-
- def get_members_count(self, obj):
- return obj.memberships.count()
+######################################################
+## Project Templates
+######################################################
class ProjectTemplateSerializer(ModelSerializer):
default_options = JsonField(required=False, label=_("Default options"))
@@ -332,20 +352,3 @@ class ProjectTemplateSerializer(ModelSerializer):
class Meta:
model = models.ProjectTemplate
read_only_fields = ("created_date", "modified_date")
-
-
-class StarredSerializer(ModelSerializer):
- class Meta:
- model = models.Project
- fields = ['id', 'name', 'slug']
-
-
-class MemberBulkSerializer(RoleExistsValidator, serializers.Serializer):
- email = serializers.EmailField()
- role_id = serializers.IntegerField()
-
-
-class MembersBulkSerializer(ProjectExistsValidator, serializers.Serializer):
- project_id = serializers.IntegerField()
- bulk_memberships = MemberBulkSerializer(many=True)
- invitation_extra_text = serializers.CharField(required=False, max_length=255)
diff --git a/taiga/routers.py b/taiga/routers.py
index 7ca20aa6..ad39b94c 100644
--- a/taiga/routers.py
+++ b/taiga/routers.py
@@ -18,15 +18,18 @@ from taiga.base import routers
router = routers.DefaultRouter(trailing_slash=False)
-# taiga.users
-from taiga.users.api import UsersViewSet
+
+# Users & Roles
from taiga.auth.api import AuthViewSet
+from taiga.users.api import UsersViewSet
+from taiga.users.api import RolesViewSet
-router.register(r"users", UsersViewSet, base_name="users")
router.register(r"auth", AuthViewSet, base_name="auth")
+router.register(r"users", UsersViewSet, base_name="users")
+router.register(r"roles", RolesViewSet, base_name="roles")
-#taiga.userstorage
+# User Storage
from taiga.userstorage.api import StorageEntriesViewSet
router.register(r"user-storage", StorageEntriesViewSet, base_name="user-storage")
@@ -51,8 +54,7 @@ router.register(r"importer", ProjectImporterViewSet, base_name="importer")
router.register(r"exporter", ProjectExporterViewSet, base_name="exporter")
-# Projects & Types
-from taiga.projects.api import RolesViewSet
+# Projects & Selectors
from taiga.projects.api import ProjectViewSet
from taiga.projects.api import MembershipViewSet
from taiga.projects.api import InvitationViewSet
@@ -65,8 +67,6 @@ from taiga.projects.api import PriorityViewSet
from taiga.projects.api import SeverityViewSet
from taiga.projects.api import ProjectTemplateViewSet
-
-router.register(r"roles", RolesViewSet, base_name="roles")
router.register(r"projects", ProjectViewSet, base_name="projects")
router.register(r"project-templates", ProjectTemplateViewSet, base_name="project-templates")
router.register(r"memberships", MembershipViewSet, base_name="memberships")
@@ -79,22 +79,27 @@ router.register(r"issue-types", IssueTypeViewSet, base_name="issue-types")
router.register(r"priorities", PriorityViewSet, base_name="priorities")
router.register(r"severities",SeverityViewSet , base_name="severities")
+
# Attachments
from taiga.projects.attachments.api import UserStoryAttachmentViewSet
from taiga.projects.attachments.api import IssueAttachmentViewSet
from taiga.projects.attachments.api import TaskAttachmentViewSet
from taiga.projects.attachments.api import WikiAttachmentViewSet
-router.register(r"userstories/attachments", UserStoryAttachmentViewSet, base_name="userstory-attachments")
+router.register(r"userstories/attachments", UserStoryAttachmentViewSet,
+ base_name="userstory-attachments")
router.register(r"tasks/attachments", TaskAttachmentViewSet, base_name="task-attachments")
router.register(r"issues/attachments", IssueAttachmentViewSet, base_name="issue-attachments")
router.register(r"wiki/attachments", WikiAttachmentViewSet, base_name="wiki-attachments")
+
# Webhooks
from taiga.webhooks.api import WebhookViewSet, WebhookLogViewSet
+
router.register(r"webhooks", WebhookViewSet, base_name="webhooks")
router.register(r"webhooklogs", WebhookLogViewSet, base_name="webhooklogs")
+
# History & Components
from taiga.projects.history.api import UserStoryHistory
from taiga.projects.history.api import TaskHistory
@@ -131,22 +136,30 @@ router.register(r"issues/(?P\d+)/voters", VotersViewSet, base_name="is
router.register(r"wiki", WikiViewSet, base_name="wiki")
router.register(r"wiki-links", WikiLinkViewSet, base_name="wiki-links")
+
# Notify policies
from taiga.projects.notifications.api import NotifyPolicyViewSet
router.register(r"notify-policies", NotifyPolicyViewSet, base_name="notifications")
+
# GitHub webhooks
from taiga.hooks.github.api import GitHubViewSet
+
router.register(r"github-hook", GitHubViewSet, base_name="github-hook")
+
# Gitlab webhooks
from taiga.hooks.gitlab.api import GitLabViewSet
+
router.register(r"gitlab-hook", GitLabViewSet, base_name="gitlab-hook")
+
# Bitbucket webhooks
from taiga.hooks.bitbucket.api import BitBucketViewSet
+
router.register(r"bitbucket-hook", BitBucketViewSet, base_name="bitbucket-hook")
+
# feedback
# - see taiga.feedback.routers and taiga.feedback.apps
diff --git a/taiga/users/api.py b/taiga/users/api.py
index b634a962..a446de8d 100644
--- a/taiga/users/api.py
+++ b/taiga/users/api.py
@@ -26,20 +26,21 @@ from django.conf import settings
from easy_thumbnails.source_generators import pil_image
from rest_framework.response import Response
-from rest_framework.filters import BaseFilterBackend
from rest_framework import status
-from djmail.template_mail import MagicMailBuilder, InlineCSSTemplateMail
+from djmail.template_mail import MagicMailBuilder
+from djmail.template_mail import InlineCSSTemplateMail
from taiga.auth.tokens import get_user_for_token
-from taiga.base.decorators import list_route, detail_route
+from taiga.base.decorators import list_route
+from taiga.base.decorators import detail_route
from taiga.base import exceptions as exc
+from taiga.base import filters
from taiga.base.api import ModelCrudViewSet
from taiga.base.api.utils import get_object_or_404
-from taiga.base.utils.slug import slugify_uniquely
+from taiga.base.filters import MembersFilterBackend
from taiga.projects.votes import services as votes_service
from taiga.projects.serializers import StarredSerializer
-from taiga.permissions.service import is_project_owner
from . import models
from . import serializers
@@ -47,22 +48,9 @@ from . import permissions
from .signals import user_cancel_account as user_cancel_account_signal
-class MembersFilterBackend(BaseFilterBackend):
- def filter_queryset(self, request, queryset, view):
- project_id = request.QUERY_PARAMS.get('project', None)
- if project_id:
- Project = apps.get_model('projects', 'Project')
- project = get_object_or_404(Project, pk=project_id)
- if request.user.is_authenticated() and project.memberships.filter(user=request.user).exists():
- return queryset.filter(memberships__project=project).distinct()
- else:
- raise exc.PermissionDenied(_("You don't have permisions to see this project users."))
-
- if request.user.is_superuser:
- return queryset
-
- return []
-
+######################################################
+## User
+######################################################
class UsersViewSet(ModelCrudViewSet):
permission_classes = (permissions.UserPermission,)
@@ -73,7 +61,9 @@ class UsersViewSet(ModelCrudViewSet):
raise exc.NotSupported()
def list(self, request, *args, **kwargs):
- self.object_list = MembersFilterBackend().filter_queryset(request, self.get_queryset(), self)
+ self.object_list = MembersFilterBackend().filter_queryset(request,
+ self.get_queryset(),
+ self)
page = self.paginate_queryset(self.object_list)
if page is not None:
@@ -249,12 +239,14 @@ class UsersViewSet(ModelCrudViewSet):
"""
serializer = serializers.ChangeEmailSerializer(data=request.DATA, many=False)
if not serializer.is_valid():
- raise exc.WrongArguments(_("Invalid, are you sure the token is correct and you didn't use it before?"))
+ raise exc.WrongArguments(_("Invalid, are you sure the token is correct and you "
+ "didn't use it before?"))
try:
user = models.User.objects.get(email_token=serializer.data["email_token"])
except models.User.DoesNotExist:
- raise exc.WrongArguments(_("Invalid, are you sure the token is correct and you didn't use it before?"))
+ raise exc.WrongArguments(_("Invalid, are you sure the token is correct and you "
+ "didn't use it before?"))
self.check_permissions(request, "change_email", user)
user.email = user.new_email
@@ -304,3 +296,25 @@ class UsersViewSet(ModelCrudViewSet):
user_cancel_account_signal.send(sender=user.__class__, user=user, request_data=request_data)
user.cancel()
return Response(status=status.HTTP_204_NO_CONTENT)
+
+
+######################################################
+## Role
+######################################################
+
+class RolesViewSet(ModelCrudViewSet):
+ model = models.Role
+ serializer_class = serializers.RoleSerializer
+ permission_classes = (permissions.RolesPermission, )
+ filter_backends = (filters.CanViewProjectFilterBackend,)
+ filter_fields = ('project',)
+
+ def pre_delete(self, obj):
+ move_to = self.request.QUERY_PARAMS.get('moveTo', None)
+ if move_to:
+ membership_model = apps.get_model("projects", "Membership")
+ role_dest = get_object_or_404(self.model, project=obj.project, id=move_to)
+ qs = membership_model.objects.filter(project_id=obj.project.pk, role=obj)
+ qs.update(role=role_dest)
+
+ super().pre_delete(obj)
diff --git a/taiga/users/models.py b/taiga/users/models.py
index 83226725..e2ffe491 100644
--- a/taiga/users/models.py
+++ b/taiga/users/models.py
@@ -174,6 +174,7 @@ class User(AbstractBaseUser, PermissionsMixin):
self.save()
self.auth_data.all().delete()
+
class Role(models.Model):
name = models.CharField(max_length=200, null=False, blank=False,
verbose_name=_("name"))
diff --git a/taiga/users/permissions.py b/taiga/users/permissions.py
index 5b7a8f27..cbabe22c 100644
--- a/taiga/users/permissions.py
+++ b/taiga/users/permissions.py
@@ -14,9 +14,13 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
-from taiga.base.api.permissions import (TaigaResourcePermission, IsSuperUser,
- AllowAny, PermissionComponent,
- IsAuthenticated)
+from taiga.base.api.permissions import TaigaResourcePermission
+from taiga.base.api.permissions import IsSuperUser
+from taiga.base.api.permissions import AllowAny
+from taiga.base.api.permissions import IsAuthenticated
+from taiga.base.api.permissions import HasProjectPerm
+from taiga.base.api.permissions import IsProjectOwner
+from taiga.base.api.permissions import PermissionComponent
class IsTheSameUser(PermissionComponent):
@@ -39,3 +43,11 @@ class UserPermission(TaigaResourcePermission):
remove_avatar_perms = IsAuthenticated()
starred_perms = AllowAny()
change_email_perms = IsTheSameUser()
+
+
+class RolesPermission(TaigaResourcePermission):
+ retrieve_perms = HasProjectPerm('view_project')
+ create_perms = IsProjectOwner()
+ update_perms = IsProjectOwner()
+ destroy_perms = IsProjectOwner()
+ list_perms = AllowAny()
diff --git a/taiga/users/serializers.py b/taiga/users/serializers.py
index 5fb28310..acd42741 100644
--- a/taiga/users/serializers.py
+++ b/taiga/users/serializers.py
@@ -16,16 +16,24 @@
from django.core import validators
from django.core.exceptions import ValidationError
+from django.utils.translation import ugettext_lazy as _
from rest_framework import serializers
+from taiga.base.serializers import Serializer
+from taiga.base.serializers import ModelSerializer
+from taiga.base.serializers import PgArrayField
-from .models import User
+from .models import User, Role
from .services import get_photo_or_gravatar_url, get_big_photo_or_gravatar_url
import re
-class UserSerializer(serializers.ModelSerializer):
+######################################################
+## User
+######################################################
+
+class UserSerializer(ModelSerializer):
full_name_display = serializers.SerializerMethodField("get_full_name_display")
photo = serializers.SerializerMethodField("get_photo")
big_photo = serializers.SerializerMethodField("get_big_photo")
@@ -39,16 +47,19 @@ class UserSerializer(serializers.ModelSerializer):
def validate_username(self, attrs, source):
value = attrs[source]
- validator = validators.RegexValidator(re.compile('^[\w.-]+$'), "invalid username", "invalid")
+ validator = validators.RegexValidator(re.compile('^[\w.-]+$'), _("invalid username"),
+ _("invalid"))
try:
validator(value)
except ValidationError:
- raise serializers.ValidationError("Required. 255 characters or fewer. Letters, numbers "
- "and /./-/_ characters'")
+ raise serializers.ValidationError(_("Required. 255 characters or fewer. Letters, "
+ "numbers and /./-/_ characters'"))
- if self.object and self.object.username != value and User.objects.filter(username=value).exists():
- raise serializers.ValidationError("Invalid username. Try with a different one.")
+ if (self.object and
+ self.object.username != value and
+ User.objects.filter(username=value).exists()):
+ raise serializers.ValidationError(_("Invalid username. Try with a different one."))
return attrs
@@ -62,14 +73,36 @@ class UserSerializer(serializers.ModelSerializer):
return get_big_photo_or_gravatar_url(user)
-class RecoverySerializer(serializers.Serializer):
+class RecoverySerializer(Serializer):
token = serializers.CharField(max_length=200)
password = serializers.CharField(min_length=6)
-class ChangeEmailSerializer(serializers.Serializer):
+class ChangeEmailSerializer(Serializer):
email_token = serializers.CharField(max_length=200)
-class CancelAccountSerializer(serializers.Serializer):
+class CancelAccountSerializer(Serializer):
cancel_token = serializers.CharField(max_length=200)
+
+
+######################################################
+## Role
+######################################################
+
+class RoleSerializer(ModelSerializer):
+ members_count = serializers.SerializerMethodField("get_members_count")
+ permissions = PgArrayField(required=False)
+
+ class Meta:
+ model = Role
+ fields = ('id', 'name', 'permissions', 'computable', 'project', 'order', 'members_count')
+
+ def get_members_count(self, obj):
+ return obj.memberships.count()
+
+
+class ProjectRoleSerializer(ModelSerializer):
+ class Meta:
+ model = Role
+ fields = ('id', 'name', 'slug', 'order', 'computable')
diff --git a/tests/integration/resources_permissions/test_projects_choices_resources.py b/tests/integration/resources_permissions/test_projects_choices_resources.py
index ed0a6a77..0c31df3d 100644
--- a/tests/integration/resources_permissions/test_projects_choices_resources.py
+++ b/tests/integration/resources_permissions/test_projects_choices_resources.py
@@ -2,6 +2,7 @@ from django.core.urlresolvers import reverse
from taiga.base.utils import json
from taiga.projects import serializers
+from taiga.users.serializers import RoleSerializer
from taiga.permissions.permissions import MEMBERS_PERMISSIONS
from tests import factories as f
@@ -140,19 +141,19 @@ def test_roles_update(client, data):
data.project_owner
]
- role_data = serializers.RoleSerializer(data.public_project.roles.all()[0]).data
+ role_data = RoleSerializer(data.public_project.roles.all()[0]).data
role_data["name"] = "test"
role_data = json.dumps(role_data)
results = helper_test_http_method(client, 'put', public_url, role_data, users)
assert results == [401, 403, 403, 403, 200]
- role_data = serializers.RoleSerializer(data.private_project1.roles.all()[0]).data
+ role_data = RoleSerializer(data.private_project1.roles.all()[0]).data
role_data["name"] = "test"
role_data = json.dumps(role_data)
results = helper_test_http_method(client, 'put', private1_url, role_data, users)
assert results == [401, 403, 403, 403, 200]
- role_data = serializers.RoleSerializer(data.private_project2.roles.all()[0]).data
+ role_data = RoleSerializer(data.private_project2.roles.all()[0]).data
role_data["name"] = "test"
role_data = json.dumps(role_data)
results = helper_test_http_method(client, 'put', private2_url, role_data, users)