Merge pull request #680 from taigaio/issue/4032/change_privacity_validation_error

Fix issue #4032: Wrong validation when change the project privacity
remotes/origin/issue/4795/notification_even_they_are_disabled
Jesús Espino 2016-03-31 08:53:47 +02:00
commit 28efb6dd68
5 changed files with 138 additions and 33 deletions

View File

@ -94,7 +94,7 @@ class ProjectImporterViewSet(mixins.ImportThrottlingPolicyMixin, CreateModelMixi
is_private = data.get('is_private', False)
(enough_slots, not_enough_slots_error) = users_service.has_available_slot_for_project(
self.request.user,
project=Project(is_private=is_private, id=None)
Project(is_private=is_private, id=None)
)
if not enough_slots:
raise exc.NotEnoughSlotsForProject(is_private, 1, not_enough_slots_error)
@ -115,11 +115,11 @@ class ProjectImporterViewSet(mixins.ImportThrottlingPolicyMixin, CreateModelMixi
# Create memberships
if "memberships" in data:
members = len(data['memberships'])
members = len([m for m in data.get("memberships", []) if m.get("email", None) != data["owner"]])
(enough_slots, not_enough_slots_error) = users_service.has_available_slot_for_project(
self.request.user,
project=Project(is_private=is_private, id=None),
members=max(members, 1)
Project(is_private=is_private, id=None),
members
)
if not enough_slots:
raise exc.NotEnoughSlotsForProject(is_private, max(members, 1), not_enough_slots_error)
@ -223,16 +223,18 @@ class ProjectImporterViewSet(mixins.ImportThrottlingPolicyMixin, CreateModelMixi
except Exception:
raise exc.WrongArguments(_("Invalid dump format"))
user = request.user
slug = dump.get('slug', None)
if slug is not None and Project.objects.filter(slug=slug).exists():
del dump['slug']
members = len(dump.get("memberships", []))
user = request.user
dump['owner'] = user.email
members = len([m for m in dump.get("memberships", []) if m.get("email", None) != dump["owner"]])
(enough_slots, not_enough_slots_error) = users_service.has_available_slot_for_project(
user,
project=Project(is_private=is_private, id=None),
members=max(members, 1)
Project(is_private=is_private, id=None),
members
)
if not enough_slots:
raise exc.NotEnoughSlotsForProject(is_private, max(members, 1), not_enough_slots_error)

View File

@ -91,11 +91,11 @@ def store_tags_colors(project, data):
def dict_to_project(data, owner=None):
if owner:
data["owner"] = owner.email
members = len(data.get("memberships", []))
members = len([m for m in data.get("memberships", []) if m.get("email", None) != data["owner"]])
(enough_slots, not_enough_slots_error) = users_service.has_available_slot_for_project(
owner,
project=Project(is_private=data.get("is_private", False), id=None),
members=members
Project(is_private=data.get("is_private", False), id=None),
members
)
if not enough_slots:
raise TaigaImportError(not_enough_slots_error)

View File

@ -380,8 +380,7 @@ class ProjectViewSet(LikedResourceMixin, HistoryResourceMixin,
(enough_slots, not_enough_slots_error) = users_service.has_available_slot_for_project(
request.user,
project=project,
members=0
project,
)
if not enough_slots:
members = project.memberships.count()
@ -419,16 +418,17 @@ class ProjectViewSet(LikedResourceMixin, HistoryResourceMixin,
permissions_service.set_base_permissions_for_project(obj)
def pre_save(self, obj):
user = self.request.user
(enough_slots, not_enough_slots_error) = users_service.has_available_slot_for_project(user, project=obj)
members = max(obj.memberships.count(), 1)
if not enough_slots:
raise exc.NotEnoughSlotsForProject(obj.is_private, members, not_enough_slots_error)
if not obj.id:
obj.owner = user
obj.owner = self.request.user
obj.template = self.request.QUERY_PARAMS.get('template', None)
# Validate if the owner have enought slots to create or update the project
# TODO: Move to the ProjectAdminSerializer
(enough_slots, not_enough_slots_error) = users_service.has_available_slot_for_project(obj.owner, obj)
if not enough_slots:
members = max(obj.memberships.count(), 1)
raise exc.NotEnoughSlotsForProject(obj.is_private, members, not_enough_slots_error)
self._set_base_permissions(obj)
super().pre_save(obj)
@ -635,8 +635,8 @@ class MembershipViewSet(BlockedByProjectMixin, ModelCrudViewSet):
members = len(data["bulk_memberships"])
(enough_slots, not_enough_slots_error) = users_service.has_available_slot_for_project(
project.owner,
project=project,
members=members
project,
members
)
if not enough_slots:
raise exc.NotEnoughSlotsForProject(project.is_private, members, not_enough_slots_error)
@ -672,8 +672,8 @@ class MembershipViewSet(BlockedByProjectMixin, ModelCrudViewSet):
members = 1
(enough_slots, not_enough_slots_error) = users_service.has_available_slot_for_project(
self.request.user,
project=obj.project,
members=members
obj.project,
members
)
if not enough_slots:
raise exc.NotEnoughSlotsForProject(obj.project.is_private, members, not_enough_slots_error)

View File

@ -575,41 +575,60 @@ def get_voted_list(for_user, from_user, type=None, q=None):
]
def has_available_slot_for_project(user, project, members=1):
def has_available_slot_for_project(user, project, new_members=0):
# TODO: Refactor: Create one service for every type of action and move to project services
#
# - has_available_slot_to_create_new_project()
# - has_available_slot_to_update_this_project()
# - has_available_slot_to_transfer_this_project()
# - has_available_slot_to_import_this_project()
# - has_available_slot_to_add_members_to_this_project()
(enough, error) = _has_available_slot_for_project_type(user, project)
if not enough:
return (enough, error)
return _has_available_slot_for_project_members(user, project, members)
return _has_available_slot_for_project_members(user, project, new_members)
def _has_available_slot_for_project_type(user, project):
if project.is_private:
if user.max_private_projects is None:
return (True, None)
elif user.owned_projects.filter(is_private=True).exclude(id=project.id).count() < user.max_private_projects:
current_private_projects = user.owned_projects.filter(is_private=True).exclude(id=project.id).count()
if current_private_projects < user.max_private_projects:
return (True, None)
return (False, _("You can't have more private projects"))
else:
if user.max_public_projects is None:
return (True, None)
elif user.owned_projects.filter(is_private=False).exclude(id=project.id).count() < user.max_public_projects:
current_public_project = user.owned_projects.filter(is_private=False).exclude(id=project.id).count()
if current_public_project < user.max_public_projects:
return (True, None)
return (False, _("You can't have more public projects"))
def _has_available_slot_for_project_members(user, project, members):
current_memberships = project.memberships.count()
def _has_available_slot_for_project_members(user, project, new_members):
current_memberships = max(project.memberships.count(), 1)
if project.is_private:
if user.max_memberships_private_projects is None:
return (True, None)
elif current_memberships + members <= user.max_memberships_private_projects:
if current_memberships + new_members <= user.max_memberships_private_projects:
return (True, None)
return (False, _("You have reached your current limit of memberships for private projects"))
else:
if user.max_memberships_public_projects is None:
return (True, None)
elif current_memberships + members <= user.max_memberships_public_projects:
if current_memberships + new_members <= user.max_memberships_public_projects:
return (True, None)
return (False, _("You have reached your current limit of memberships for public projects"))

View File

@ -1501,3 +1501,87 @@ def test_valid_dump_import_without_slug(client):
response = client.post(url, {'dump': data})
assert response.status_code == 201
def test_valid_dump_import_with_the_limit_of_membership_whit_you_for_private_project(client):
user = f.UserFactory.create(max_memberships_private_projects=5)
client.login(user)
url = reverse("importer-load-dump")
data = ContentFile(bytes(json.dumps({
"slug": "private-project-with-memberships-limit-with-you",
"name": "Valid project",
"description": "Valid project desc",
"is_private": True,
"memberships": [
{
"email": user.email,
"role": "Role",
},
{
"email": "test2@test.com",
"role": "Role",
},
{
"email": "test3@test.com",
"role": "Role",
},
{
"email": "test4@test.com",
"role": "Role",
},
{
"email": "test5@test.com",
"role": "Role",
},
],
"roles": [{"name": "Role"}]
}), "utf-8"))
data.name = "test"
response = client.post(url, {'dump': data})
assert response.status_code == 201
assert Project.objects.filter(slug="private-project-with-memberships-limit-with-you").count() == 1
def test_valid_dump_import_with_the_limit_of_membership_whit_you_for_public_project(client):
user = f.UserFactory.create(max_memberships_public_projects=5)
client.login(user)
url = reverse("importer-load-dump")
data = ContentFile(bytes(json.dumps({
"slug": "public-project-with-memberships-limit-with-you",
"name": "Valid project",
"description": "Valid project desc",
"is_private": False,
"memberships": [
{
"email": user.email,
"role": "Role",
},
{
"email": "test2@test.com",
"role": "Role",
},
{
"email": "test3@test.com",
"role": "Role",
},
{
"email": "test4@test.com",
"role": "Role",
},
{
"email": "test5@test.com",
"role": "Role",
},
],
"roles": [{"name": "Role"}]
}), "utf-8"))
data.name = "test"
response = client.post(url, {'dump': data})
assert response.status_code == 201
assert Project.objects.filter(slug="public-project-with-memberships-limit-with-you").count() == 1