Merge pull request #736 from taigaio/Improving-project-deletion
Improving-project-deletionremotes/origin/issue/4795/notification_even_they_are_disabled
commit
a1f52e9a72
|
@ -20,6 +20,7 @@ from easy_thumbnails.source_generators import pil_image
|
||||||
from dateutil.relativedelta import relativedelta
|
from dateutil.relativedelta import relativedelta
|
||||||
|
|
||||||
from django.apps import apps
|
from django.apps import apps
|
||||||
|
from django.conf import settings
|
||||||
from django.db.models import signals, Prefetch
|
from django.db.models import signals, Prefetch
|
||||||
from django.db.models import Value as V
|
from django.db.models import Value as V
|
||||||
from django.db.models.functions import Coalesce
|
from django.db.models.functions import Coalesce
|
||||||
|
@ -442,9 +443,13 @@ class ProjectViewSet(LikedResourceMixin, HistoryResourceMixin,
|
||||||
|
|
||||||
self.pre_delete(obj)
|
self.pre_delete(obj)
|
||||||
self.pre_conditions_on_delete(obj)
|
self.pre_conditions_on_delete(obj)
|
||||||
obj.delete_related_content()
|
|
||||||
obj.delete()
|
services.orphan_project(obj)
|
||||||
self.post_delete(obj)
|
if settings.CELERY_ENABLED:
|
||||||
|
services.delete_project.delay(obj.id)
|
||||||
|
else:
|
||||||
|
services.delete_project(obj.id)
|
||||||
|
|
||||||
return response.NoContent()
|
return response.NoContent()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.9.2 on 2016-05-30 10:04
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('projects', '0042_auto_20160525_0911'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='project',
|
||||||
|
name='owner',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='owned_projects', to=settings.AUTH_USER_MODEL, verbose_name='owner'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -157,7 +157,7 @@ class Project(ProjectDefaults, TaggedMixin, models.Model):
|
||||||
default=timezone.now)
|
default=timezone.now)
|
||||||
modified_date = models.DateTimeField(null=False, blank=False,
|
modified_date = models.DateTimeField(null=False, blank=False,
|
||||||
verbose_name=_("modified date"))
|
verbose_name=_("modified date"))
|
||||||
owner = models.ForeignKey(settings.AUTH_USER_MODEL, null=False, blank=False,
|
owner = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True,
|
||||||
related_name="owned_projects", verbose_name=_("owner"))
|
related_name="owned_projects", verbose_name=_("owner"))
|
||||||
members = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name="projects",
|
members = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name="projects",
|
||||||
through="Membership", verbose_name=_("members"),
|
through="Membership", verbose_name=_("members"),
|
||||||
|
|
|
@ -47,6 +47,8 @@ from .projects import check_if_project_privacity_can_be_changed
|
||||||
from .projects import check_if_project_can_be_created_or_updated
|
from .projects import check_if_project_can_be_created_or_updated
|
||||||
from .projects import check_if_project_can_be_transfered
|
from .projects import check_if_project_can_be_transfered
|
||||||
from .projects import check_if_project_is_out_of_owner_limits
|
from .projects import check_if_project_is_out_of_owner_limits
|
||||||
|
from .projects import orphan_project
|
||||||
|
from .projects import delete_project
|
||||||
|
|
||||||
from .stats import get_stats_for_project_issues
|
from .stats import get_stats_for_project_issues
|
||||||
from .stats import get_stats_for_project
|
from .stats import get_stats_for_project
|
||||||
|
|
|
@ -15,8 +15,9 @@
|
||||||
# You should have received a copy of the GNU Affero General Public License
|
# 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/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
from django.apps import apps
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
|
from taiga.celery import app
|
||||||
|
|
||||||
ERROR_MAX_PUBLIC_PROJECTS_MEMBERSHIPS = 'max_public_projects_memberships'
|
ERROR_MAX_PUBLIC_PROJECTS_MEMBERSHIPS = 'max_public_projects_memberships'
|
||||||
ERROR_MAX_PRIVATE_PROJECTS_MEMBERSHIPS = 'max_private_projects_memberships'
|
ERROR_MAX_PRIVATE_PROJECTS_MEMBERSHIPS = 'max_private_projects_memberships'
|
||||||
|
@ -151,3 +152,21 @@ def check_if_project_is_out_of_owner_limits(project):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def orphan_project(project):
|
||||||
|
project.memberships.filter(user=project.owner).delete()
|
||||||
|
project.owner = None
|
||||||
|
project.save()
|
||||||
|
|
||||||
|
|
||||||
|
@app.task
|
||||||
|
def delete_project(project_id):
|
||||||
|
Project = apps.get_model("projects", "Project")
|
||||||
|
try:
|
||||||
|
project = Project.objects.get(id=project_id)
|
||||||
|
except Project.DoesNotExist:
|
||||||
|
return
|
||||||
|
|
||||||
|
project.delete_related_content()
|
||||||
|
project.delete()
|
||||||
|
|
|
@ -19,6 +19,8 @@ from easy_thumbnails.files import generate_all_aliases, get_thumbnailer
|
||||||
import os.path
|
import os.path
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from unittest import mock
|
||||||
|
|
||||||
pytestmark = pytest.mark.django_db
|
pytestmark = pytest.mark.django_db
|
||||||
|
|
||||||
class ExpiredSigner(signing.TimestampSigner):
|
class ExpiredSigner(signing.TimestampSigner):
|
||||||
|
@ -1814,3 +1816,36 @@ def test_public_project_when_project_has_unlimited_members(client):
|
||||||
project.owner.max_memberships_public_projects = None
|
project.owner.max_memberships_public_projects = None
|
||||||
|
|
||||||
assert check_if_project_is_out_of_owner_limits(project) == False
|
assert check_if_project_is_out_of_owner_limits(project) == False
|
||||||
|
|
||||||
|
|
||||||
|
def test_delete_project_with_celery_enabled(client, settings):
|
||||||
|
settings.CELERY_ENABLED = True
|
||||||
|
user = f.UserFactory.create()
|
||||||
|
project = f.ProjectFactory.create(owner=user)
|
||||||
|
role = f.RoleFactory.create(project=project, permissions=["view_project"])
|
||||||
|
membership = f.MembershipFactory.create(project=project, user=user, role=role, is_admin=True)
|
||||||
|
url = reverse("projects-detail", args=(project.id,))
|
||||||
|
client.login(user)
|
||||||
|
|
||||||
|
#delete_project task should have been launched
|
||||||
|
with mock.patch('taiga.projects.services.delete_project') as delete_project_mock:
|
||||||
|
response = client.json.delete(url)
|
||||||
|
assert response.status_code == 204
|
||||||
|
project = Project.objects.get(id=project.id)
|
||||||
|
assert project.owner == None
|
||||||
|
assert project.memberships.count() == 0
|
||||||
|
delete_project_mock.delay.assert_called_once_with(project.id)
|
||||||
|
|
||||||
|
|
||||||
|
def test_delete_project_with_celery_disabled(client, settings):
|
||||||
|
settings.CELERY_ENABLED = False
|
||||||
|
|
||||||
|
user = f.UserFactory.create()
|
||||||
|
project = f.ProjectFactory.create(owner=user)
|
||||||
|
role = f.RoleFactory.create(project=project, permissions=["view_project"])
|
||||||
|
membership = f.MembershipFactory.create(project=project, user=user, role=role, is_admin=True)
|
||||||
|
url = reverse("projects-detail", args=(project.id,))
|
||||||
|
client.login(user)
|
||||||
|
response = client.json.delete(url)
|
||||||
|
assert response.status_code == 204
|
||||||
|
assert Project.objects.filter(id=project.id).count() == 0
|
||||||
|
|
Loading…
Reference in New Issue