Site admin interface

remotes/origin/enhancement/email-actions
Jesús Espino 2013-12-11 19:59:18 +01:00
parent fcaa4855e8
commit 15d92664f9
10 changed files with 148 additions and 16 deletions

View File

@ -29,13 +29,17 @@ class AuthViewSet(viewsets.ViewSet):
def _create_response(self, user): def _create_response(self, user):
serializer = UserSerializer(user) serializer = UserSerializer(user)
response_data = serializer.data response_data = serializer.data
domain = get_active_domain()
response_data['is_site_owner'] = domain.user_is_owner(user)
response_data['is_site_staff'] = domain.user_is_staff(user)
response_data["auth_token"] = auth.get_token_for_user(user) response_data["auth_token"] = auth.get_token_for_user(user)
return response_data return response_data
def _create_domain_member(self, user): def _create_domain_member(self, user):
domain = get_active_domain() domain = get_active_domain()
if DomainMember.objects.filter(domain=domain, user=user).count() == 0: if domain.members.filter(user=user).count() == 0:
domain_member = DomainMember(domain=domain, user=user, email=user.email, domain_member = DomainMember(domain=domain, user=user, email=user.email,
is_owner=False, is_staff=False) is_owner=False, is_staff=False)
domain_member.save() domain_member.save()

View File

@ -1,10 +1,13 @@
from django.contrib import admin from django.contrib import admin
from .models import Domain from .models import Domain, DomainMember
class DomainMemberInline(admin.TabularInline):
model = DomainMember
class DomainAdmin(admin.ModelAdmin): class DomainAdmin(admin.ModelAdmin):
list_display = ('domain', 'name') list_display = ('domain', 'name')
search_fields = ('domain', 'name') search_fields = ('domain', 'name')
inlines = [ DomainMemberInline, ]
admin.site.register(Domain, DomainAdmin) admin.site.register(Domain, DomainAdmin)

View File

@ -2,13 +2,38 @@
from rest_framework import viewsets from rest_framework import viewsets
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.permissions import AllowAny from rest_framework.permissions import AllowAny, IsAuthenticated
from .serializers import DomainSerializer from django.http import Http404
from greenmine.base.api import ModelCrudViewSet, UpdateModelMixin
from .serializers import DomainSerializer, DomainMemberSerializer
from .permissions import DomainMembersPermission, DomainPermission
from .models import DomainMember, Domain
class DomainViewSet(viewsets.ViewSet): class DomainViewSet(UpdateModelMixin, viewsets.GenericViewSet):
permission_classes = (AllowAny,) permission_classes = (IsAuthenticated, DomainPermission,)
serializer_class = DomainSerializer
queryset = Domain.objects.all()
def list(self, request, **kwargs): def list(self, request, **kwargs):
return Response(DomainSerializer(request.domain).data) 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, **kwargs):
raise Http404
def create(self, request, **kwargs):
self.kwargs['pk'] = request.domain.pk
return super().update(request, pk=request.domain.pk, **kwargs)
class DomainMembersViewSet(ModelCrudViewSet):
permission_classes = (IsAuthenticated, DomainMembersPermission,)
serializer_class = DomainMemberSerializer
queryset = DomainMember.objects.all()

View File

@ -45,9 +45,18 @@ class Domain(models.Model):
def __str__(self): def __str__(self):
return self.domain return self.domain
def user_is_owner(self, user):
return self.members.filter(id=user.id, is_owner=True).count() > 0
def user_is_staff(self, user):
return self.members.filter(id=user.id, is_staff=True).count() > 0
def user_is_normal_user(self, user):
return self.members.filter(id=user.id, is_owner=False, is_staff=False).count() > 0
class DomainMember(models.Model): class DomainMember(models.Model):
domain = models.ForeignKey("Domain", related_name="+", null=True) domain = models.ForeignKey("Domain", related_name="members", null=True)
user = models.ForeignKey("users.User", related_name="+", null=True) user = models.ForeignKey("users.User", related_name="+", null=True)
email = models.EmailField(max_length=255) email = models.EmailField(max_length=255)

View File

@ -0,0 +1,29 @@
from rest_framework import permissions
from greenmine.base.domains.models import DomainMember
from greenmine.base.domains 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)
else:
return False

View File

@ -1,10 +1,22 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from rest_framework import serializers from rest_framework import serializers
from .models import Domain from .models import Domain, DomainMember
from greenmine.base.users.serializers import UserSerializer
class DomainSerializer(serializers.ModelSerializer): class DomainSerializer(serializers.ModelSerializer):
projects = serializers.SerializerMethodField('get_projects')
class Meta: class Meta:
model = Domain model = Domain
fields = ('public_register', 'default_language') fields = ('public_register', 'default_language', "projects")
def get_projects(self, obj):
return map(lambda x: {"id": x.id, "name": x.name }, obj.projects.all().order_by('name'))
class DomainMemberSerializer(serializers.ModelSerializer):
user = UserSerializer()
class Meta:
model = DomainMember

View File

@ -16,6 +16,7 @@ from djmail.template_mail import MagicMailBuilder
from greenmine.base import filters from greenmine.base import filters
from greenmine.base import exceptions as exc from greenmine.base import exceptions as exc
from greenmine.base.api import ModelCrudViewSet, ModelListViewSet, RetrieveModelMixin from greenmine.base.api import ModelCrudViewSet, ModelListViewSet, RetrieveModelMixin
from greenmine.base.domains import get_active_domain
from greenmine.base.notifications.api import NotificationSenderMixin from greenmine.base.notifications.api import NotificationSenderMixin
from greenmine.projects.aggregates.tags import get_all_tags from greenmine.projects.aggregates.tags import get_all_tags
@ -27,6 +28,29 @@ from .aggregates import stats
from .aggregates import filters as filters_aggr from .aggregates import filters as filters_aggr
class ProjectAdminViewSet(ModelCrudViewSet):
model = models.Project
serializer_class = serializers.ProjectDetailSerializer
list_serializer_class = serializers.ProjectSerializer
permission_classes = (IsAuthenticated, permissions.ProjectAdminPermission)
def get_queryset(self):
domain = get_active_domain()
return domain.projects.all()
def pre_save(self, obj):
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)
class ProjectViewSet(ModelCrudViewSet): class ProjectViewSet(ModelCrudViewSet):
model = models.Project model = models.Project
serializer_class = serializers.ProjectDetailSerializer serializer_class = serializers.ProjectDetailSerializer

View File

@ -1,17 +1,41 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from greenmine.base.permissions import BasePermission from greenmine.base.permissions import BasePermission
from greenmine.base.domains import get_active_domain
class ProjectPermission(BasePermission): class ProjectPermission(BasePermission):
get_permission = "view_project" get_permission = "view_project"
post_permission = "add_project" post_permission = None
put_permission = "change_project" put_permission = "change_project"
patch_permission = "change_project" patch_permission = "change_project"
delete_permission = "delete_project" delete_permission = None
safe_methods = ["HEAD", "OPTIONS"] safe_methods = ["HEAD", "OPTIONS"]
path_to_project = [] path_to_project = []
class ProjectAdminPermission(BasePermission):
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", "GET", "PATCH"]:
return domain.user_is_staff(request.user)
elif request.method == "DELETE":
return domain.user_is_owner(request.user)
return super().has_permission(request, view)
def has_object_permission(self, request, view, obj):
if request.method in self.safe_methods:
return True
domain = get_active_domain()
if request.method in ["POST", "PUT", "GET", "PATCH"]:
return domain.user_is_staff(request.user)
elif request.method == "DELETE":
return domain.user_is_owner(request.user)
return super().has_object_permission(request, view, obj)
class MembershipPermission(BasePermission): class MembershipPermission(BasePermission):
get_permission = "view_membership" get_permission = "view_membership"

View File

@ -418,8 +418,8 @@ class ProjectsTestCase(test.TestCase):
password=self.user3.username) password=self.user3.username)
self.assertTrue(response) self.assertTrue(response)
response = self.client.delete(reverse("projects-detail", args=(self.project1.id,))) response = self.client.delete(reverse("projects-detail", args=(self.project1.id,)))
self.assertEqual(response.status_code, 204) self.assertEqual(response.status_code, 403)
self.assertEqual(Project.objects.all().count(), 3) self.assertEqual(Project.objects.all().count(), 4)
self.client.logout() self.client.logout()
def test_delete_project_by_not_membership(self): def test_delete_project_by_not_membership(self):

View File

@ -4,11 +4,11 @@ from greenmine.base import routers
from greenmine.base.auth.api import AuthViewSet from greenmine.base.auth.api import AuthViewSet
from greenmine.base.users.api import RolesViewSet, UsersViewSet from greenmine.base.users.api import RolesViewSet, UsersViewSet
from greenmine.base.searches.api import SearchViewSet from greenmine.base.searches.api import SearchViewSet
from greenmine.base.domains.api import DomainViewSet from greenmine.base.domains.api import DomainViewSet, DomainMembersViewSet
from greenmine.projects.api import (ProjectViewSet, MembershipViewSet, InvitationViewSet, from greenmine.projects.api import (ProjectViewSet, MembershipViewSet, InvitationViewSet,
UserStoryStatusViewSet, PointsViewSet, TaskStatusViewSet, UserStoryStatusViewSet, PointsViewSet, TaskStatusViewSet,
IssueStatusViewSet, IssueTypeViewSet, PriorityViewSet, IssueStatusViewSet, IssueTypeViewSet, PriorityViewSet,
SeverityViewSet) #, QuestionStatusViewSet) SeverityViewSet, ProjectAdminViewSet) #, QuestionStatusViewSet)
from greenmine.projects.milestones.api import MilestoneViewSet from greenmine.projects.milestones.api import MilestoneViewSet
from greenmine.projects.userstories.api import UserStoryViewSet, UserStoryAttachmentViewSet from greenmine.projects.userstories.api import UserStoryViewSet, UserStoryAttachmentViewSet
from greenmine.projects.tasks.api import TaskViewSet, TaskAttachmentViewSet from greenmine.projects.tasks.api import TaskViewSet, TaskAttachmentViewSet
@ -30,6 +30,8 @@ router.register(r"search", SearchViewSet, base_name="search")
# greenmine.base.domains # greenmine.base.domains
router.register(r"sites", DomainViewSet, base_name="sites") 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")
# greenmine.projects # greenmine.projects
router.register(r"projects", ProjectViewSet, base_name="projects") router.register(r"projects", ProjectViewSet, base_name="projects")