Browse Source

Merge pull request #1226 from rafalp/move-apps-admin-logic

Move apps admin implementations to admin packages
Rafał Pitoń 6 years ago
parent
commit
c797dcc829
39 changed files with 305 additions and 278 deletions
  1. 0 0
      misago/acl/admin/__init__.py
  2. 2 2
      misago/acl/admin/forms.py
  3. 18 0
      misago/acl/admin/test.py
  4. 0 0
      misago/acl/admin/tests/__init__.py
  5. 104 0
      misago/acl/admin/tests/test_admin_views.py
  6. 1 1
      misago/acl/admin/tests/test_mock_role_admin_form_data.py
  7. 2 2
      misago/acl/admin/views.py
  8. 0 18
      misago/acl/test.py
  9. 0 123
      misago/acl/tests/test_roleadmin_views.py
  10. 2 2
      misago/categories/admin/__init__.py
  11. 5 5
      misago/categories/admin/forms.py
  12. 0 0
      misago/categories/admin/tests/__init__.py
  13. 6 6
      misago/categories/admin/tests/test_categories_admin_views.py
  14. 6 6
      misago/categories/admin/tests/test_permissions_admin_views.py
  15. 0 0
      misago/categories/admin/views/__init__.py
  16. 5 5
      misago/categories/admin/views/categories.py
  17. 7 7
      misago/categories/admin/views/perms.py
  18. 1 1
      misago/categories/urls/__init__.py
  19. 2 2
      misago/categories/views.py
  20. 1 1
      misago/legal/admin/__init__.py
  21. 2 2
      misago/legal/admin/forms.py
  22. 0 0
      misago/legal/admin/tests/__init__.py
  23. 6 4
      misago/legal/admin/tests/test_admin_views.py
  24. 98 0
      misago/legal/admin/tests/test_utils.py
  25. 19 0
      misago/legal/admin/utils.py
  26. 2 2
      misago/legal/admin/views.py
  27. 0 54
      misago/legal/tests/test_utils.py
  28. 0 18
      misago/legal/utils.py
  29. 2 2
      misago/legal/views.py
  30. 0 1
      misago/legal/views/__init__.py
  31. 2 2
      misago/threads/admin/__init__.py
  32. 1 1
      misago/threads/admin/forms.py
  33. 0 0
      misago/threads/admin/tests/__init__.py
  34. 3 3
      misago/threads/admin/tests/test_attachment_types_views.py
  35. 4 4
      misago/threads/admin/tests/test_attachments_views.py
  36. 0 0
      misago/threads/admin/views/__init__.py
  37. 1 1
      misago/threads/admin/views/attachments.py
  38. 1 1
      misago/threads/admin/views/attachmenttypes.py
  39. 2 2
      misago/users/admin/tests/test_bans.py

+ 0 - 0
misago/acl/admin.py → misago/acl/admin/__init__.py


+ 2 - 2
misago/acl/forms.py → misago/acl/admin/forms.py

@@ -1,8 +1,8 @@
 from django import forms
 from django import forms
 from django.utils.translation import gettext_lazy as _
 from django.utils.translation import gettext_lazy as _
 
 
-from .models import Role
-from .providers import providers
+from ..models import Role
+from ..providers import providers
 
 
 
 
 class RoleForm(forms.ModelForm):
 class RoleForm(forms.ModelForm):

+ 18 - 0
misago/acl/admin/test.py

@@ -0,0 +1,18 @@
+from .forms import get_permissions_forms
+
+
+def mock_role_form_data(model, data):
+    """
+    In order for form to don't fail submission, all permission fields need
+    to receive values. This function populates data dict with default values
+    for permissions, making form validation pass
+    """
+    for form in get_permissions_forms(model):
+        for field in form:
+            if field.value() is True:
+                data[field.html_name] = 1
+            elif field.value() is False:
+                data[field.html_name] = 0
+            else:
+                data[field.html_name] = field.value()
+    return data

+ 0 - 0
misago/categories/views/__init__.py → misago/acl/admin/tests/__init__.py


+ 104 - 0
misago/acl/admin/tests/test_admin_views.py

@@ -0,0 +1,104 @@
+import pytest
+from django.urls import reverse
+
+from ....admin.test import AdminTestCase
+from ....cache.test import assert_invalidates_cache
+from ....test import assert_contains
+from ... import ACL_CACHE
+from ...models import Role
+from ..test import mock_role_form_data
+
+admin_link = reverse("misago:admin:permissions:index")
+
+
+def create_form_data(data_dict):
+    return mock_role_form_data(Role(), data_dict)
+
+
+def test_link_is_registered_in_admin_nav(admin_client):
+    response = admin_client.get(reverse("misago:admin:index"))
+    assert_contains(response, admin_link)
+
+
+def test_list_renders(admin_client):
+    response = admin_client.get(admin_link)
+    assert response.status_code == 200
+
+
+def test_new_role_form_renders(admin_client):
+    response = admin_client.get(reverse("misago:admin:permissions:new"))
+    assert response.status_code == 200
+
+
+def test_new_role_can_be_created(admin_client):
+    response = admin_client.post(
+        reverse("misago:admin:permissions:new"),
+        data=create_form_data({"name": "Test Role"}),
+    )
+
+    Role.objects.get(name="Test Role")
+
+
+@pytest.fixture
+def role(db):
+    return Role.objects.create(name="Test Role")
+
+
+def test_edit_role_form_renders(admin_client, role):
+    response = admin_client.get(
+        reverse("misago:admin:permissions:edit", kwargs={"pk": role.pk})
+    )
+    assert response.status_code == 200
+
+
+def test_role_can_be_edited(admin_client, role):
+    response = admin_client.post(
+        reverse("misago:admin:permissions:edit", kwargs={"pk": role.pk}),
+        data=create_form_data({"name": "Edited Role"}),
+    )
+
+    role.refresh_from_db()
+    assert role.name == "Edited Role"
+
+
+def test_editing_role_invalidates_acl_cache(admin_client, role):
+    with assert_invalidates_cache(ACL_CACHE):
+        admin_client.post(
+            reverse("misago:admin:permissions:edit", kwargs={"pk": role.pk}),
+            data=create_form_data({"name": "Role"}),
+        )
+
+
+def test_role_can_be_deleted(admin_client, role):
+    admin_client.post(
+        reverse("misago:admin:permissions:delete", kwargs={"pk": role.pk})
+    )
+
+    with pytest.raises(Role.DoesNotExist):
+        role.refresh_from_db()
+
+
+def test_special_role_cant_be_deleted(admin_client, role):
+    role.special_role = "Test"
+    role.save()
+
+    admin_client.post(
+        reverse("misago:admin:permissions:delete", kwargs={"pk": role.pk})
+    )
+
+    role.refresh_from_db()
+
+
+def test_deleting_role_invalidates_acl_cache(admin_client, role):
+    with assert_invalidates_cache(ACL_CACHE):
+        admin_client.post(
+            reverse("misago:admin:permissions:delete", kwargs={"pk": role.pk})
+        )
+
+
+def test_users_with_role_view_redirects_to_admin_users_list(admin_client, role):
+    response = admin_client.get(
+        reverse("misago:admin:permissions:users", kwargs={"pk": role.pk})
+    )
+    assert response.status_code == 302
+    assert reverse("misago:admin:users:index") in response["location"]

+ 1 - 1
misago/acl/tests/test_mock_role_admin_form_data.py → misago/acl/admin/tests/test_mock_role_admin_form_data.py

@@ -1,4 +1,4 @@
-from ..models import Role
+from ...models import Role
 from ..test import mock_role_form_data
 from ..test import mock_role_form_data
 
 
 
 

+ 2 - 2
misago/acl/views.py → misago/acl/admin/views.py

@@ -3,9 +3,9 @@ from django.shortcuts import redirect
 from django.urls import reverse
 from django.urls import reverse
 from django.utils.translation import gettext_lazy as _
 from django.utils.translation import gettext_lazy as _
 
 
-from ..admin.views import generic
+from ...admin.views import generic
+from ..models import Role
 from .forms import RoleForm, get_permissions_forms
 from .forms import RoleForm, get_permissions_forms
-from .models import Role
 
 
 
 
 class RoleAdmin(generic.AdminBaseMixin):
 class RoleAdmin(generic.AdminBaseMixin):

+ 0 - 18
misago/acl/test.py

@@ -1,7 +1,6 @@
 from contextlib import ContextDecorator, ExitStack, contextmanager
 from contextlib import ContextDecorator, ExitStack, contextmanager
 from unittest.mock import patch
 from unittest.mock import patch
 
 
-from .forms import get_permissions_forms
 from .useracl import get_user_acl
 from .useracl import get_user_acl
 
 
 __all__ = ["patch_user_acl"]
 __all__ = ["patch_user_acl"]
@@ -53,20 +52,3 @@ class patch_user_acl(ContextDecorator, ExitStack):
         return patch(
         return patch(
             "misago.acl.useracl.get_user_acl", side_effect=self.patched_get_user_acl
             "misago.acl.useracl.get_user_acl", side_effect=self.patched_get_user_acl
         )
         )
-
-
-def mock_role_form_data(model, data):
-    """
-    In order for form to don't fail submission, all permission fields need
-    to receive values. This function populates data dict with default values
-    for permissions, making form validation pass
-    """
-    for form in get_permissions_forms(model):
-        for field in form:
-            if field.value() is True:
-                data[field.html_name] = 1
-            elif field.value() is False:
-                data[field.html_name] = 0
-            else:
-                data[field.html_name] = field.value()
-    return data

+ 0 - 123
misago/acl/tests/test_roleadmin_views.py

@@ -1,123 +0,0 @@
-from django.urls import reverse
-
-from .. import ACL_CACHE
-from ...admin.test import AdminTestCase
-from ...cache.test import assert_invalidates_cache
-from ..models import Role
-from ..test import mock_role_form_data
-
-
-def create_data(data_dict):
-    return mock_role_form_data(Role(), data_dict)
-
-
-class RoleAdminViewsTests(AdminTestCase):
-    def test_link_registered(self):
-        """admin nav contains user roles link"""
-        response = self.client.get(reverse("misago:admin:permissions:index"))
-        self.assertContains(response, reverse("misago:admin:permissions:index"))
-
-    def test_list_view(self):
-        """roles list view returns 200"""
-        response = self.client.get(reverse("misago:admin:permissions:index"))
-        self.assertEqual(response.status_code, 200)
-
-    def test_new_view(self):
-        """new role view has no showstoppers"""
-        response = self.client.get(reverse("misago:admin:permissions:new"))
-        self.assertEqual(response.status_code, 200)
-
-        response = self.client.post(
-            reverse("misago:admin:permissions:new"),
-            data=create_data({"name": "Test Role"}),
-        )
-        self.assertEqual(response.status_code, 302)
-
-        test_role = Role.objects.get(name="Test Role")
-        response = self.client.get(reverse("misago:admin:permissions:index"))
-        self.assertEqual(response.status_code, 200)
-        self.assertContains(response, test_role.name)
-
-    def test_edit_view(self):
-        """edit role view has no showstoppers"""
-        self.client.post(
-            reverse("misago:admin:permissions:new"),
-            data=create_data({"name": "Test Role"}),
-        )
-
-        test_role = Role.objects.get(name="Test Role")
-
-        response = self.client.get(
-            reverse("misago:admin:permissions:edit", kwargs={"pk": test_role.pk})
-        )
-        self.assertEqual(response.status_code, 200)
-        self.assertContains(response, "Test Role")
-
-        response = self.client.post(
-            reverse("misago:admin:permissions:edit", kwargs={"pk": test_role.pk}),
-            data=create_data({"name": "Top Lel"}),
-        )
-        self.assertEqual(response.status_code, 302)
-
-        test_role = Role.objects.get(name="Top Lel")
-        response = self.client.get(reverse("misago:admin:permissions:index"))
-        self.assertEqual(response.status_code, 200)
-        self.assertContains(response, test_role.name)
-
-    def test_editing_role_invalidates_acl_cache(self):
-        self.client.post(
-            reverse("misago:admin:permissions:new"),
-            data=create_data({"name": "Test Role"}),
-        )
-
-        test_role = Role.objects.get(name="Test Role")
-
-        with assert_invalidates_cache(ACL_CACHE):
-            self.client.post(
-                reverse("misago:admin:permissions:edit", kwargs={"pk": test_role.pk}),
-                data=create_data({"name": "Top Lel"}),
-            )
-
-    def test_users_view(self):
-        """users with this role view has no showstoppers"""
-        response = self.client.post(
-            reverse("misago:admin:permissions:new"),
-            data=create_data({"name": "Test Role"}),
-        )
-        test_role = Role.objects.get(name="Test Role")
-
-        response = self.client.get(
-            reverse("misago:admin:permissions:users", kwargs={"pk": test_role.pk})
-        )
-        self.assertEqual(response.status_code, 302)
-
-    def test_delete_view(self):
-        """delete role view has no showstoppers"""
-        self.client.post(
-            reverse("misago:admin:permissions:new"),
-            data=create_data({"name": "Test Role"}),
-        )
-
-        test_role = Role.objects.get(name="Test Role")
-        response = self.client.post(
-            reverse("misago:admin:permissions:delete", kwargs={"pk": test_role.pk})
-        )
-        self.assertEqual(response.status_code, 302)
-
-        # Get the page twice so no alert is renderered on second request
-        self.client.get(reverse("misago:admin:permissions:index"))
-        response = self.client.get(reverse("misago:admin:permissions:index"))
-        self.assertNotContains(response, test_role.name)
-
-    def test_deleting_role_invalidates_acl_cache(self):
-        self.client.post(
-            reverse("misago:admin:permissions:new"),
-            data=create_data({"name": "Test Role"}),
-        )
-
-        test_role = Role.objects.get(name="Test Role")
-
-        with assert_invalidates_cache(ACL_CACHE):
-            self.client.post(
-                reverse("misago:admin:permissions:delete", kwargs={"pk": test_role.pk})
-            )

+ 2 - 2
misago/categories/admin.py → misago/categories/admin/__init__.py

@@ -1,7 +1,7 @@
 from django.conf.urls import url
 from django.conf.urls import url
 from django.utils.translation import gettext_lazy as _
 from django.utils.translation import gettext_lazy as _
 
 
-from .views.categoriesadmin import (
+from .views.categories import (
     CategoriesList,
     CategoriesList,
     DeleteCategory,
     DeleteCategory,
     EditCategory,
     EditCategory,
@@ -9,7 +9,7 @@ from .views.categoriesadmin import (
     MoveUpCategory,
     MoveUpCategory,
     NewCategory,
     NewCategory,
 )
 )
-from .views.permsadmin import (
+from .views.perms import (
     CategoryPermissions,
     CategoryPermissions,
     CategoryRolesList,
     CategoryRolesList,
     DeleteCategoryRole,
     DeleteCategoryRole,

+ 5 - 5
misago/categories/forms.py → misago/categories/admin/forms.py

@@ -4,11 +4,11 @@ from django.utils.html import conditional_escape, mark_safe
 from django.utils.translation import gettext_lazy as _
 from django.utils.translation import gettext_lazy as _
 from mptt.forms import TreeNodeChoiceField, TreeNodeMultipleChoiceField
 from mptt.forms import TreeNodeChoiceField, TreeNodeMultipleChoiceField
 
 
-from . import THREADS_ROOT_NAME
-from ..admin.forms import YesNoSwitch
-from ..core.validators import validate_sluggable
-from ..threads.threadtypes import trees_map
-from .models import Category, CategoryRole
+from ...admin.forms import YesNoSwitch
+from ...core.validators import validate_sluggable
+from ...threads.threadtypes import trees_map
+from .. import THREADS_ROOT_NAME
+from ..models import Category, CategoryRole
 
 
 
 
 class AdminCategoryFieldMixin:
 class AdminCategoryFieldMixin:

+ 0 - 0
misago/threads/views/admin/__init__.py → misago/categories/admin/tests/__init__.py


+ 6 - 6
misago/categories/tests/test_categories_admin_views.py → misago/categories/admin/tests/test_categories_admin_views.py

@@ -1,11 +1,11 @@
 from django.urls import reverse
 from django.urls import reverse
 
 
-from ...acl import ACL_CACHE
-from ...admin.test import AdminTestCase
-from ...cache.test import assert_invalidates_cache
-from ...threads import test
-from ...threads.models import Thread
-from ..models import Category
+from ....acl import ACL_CACHE
+from ....admin.test import AdminTestCase
+from ....cache.test import assert_invalidates_cache
+from ....threads import test
+from ....threads.models import Thread
+from ...models import Category
 
 
 
 
 class CategoryAdminTestCase(AdminTestCase):
 class CategoryAdminTestCase(AdminTestCase):

+ 6 - 6
misago/categories/tests/test_permissions_admin_views.py → misago/categories/admin/tests/test_permissions_admin_views.py

@@ -1,11 +1,11 @@
 from django.urls import reverse
 from django.urls import reverse
 
 
-from ...acl import ACL_CACHE
-from ...acl.models import Role
-from ...acl.test import mock_role_form_data
-from ...admin.test import AdminTestCase
-from ...cache.test import assert_invalidates_cache
-from ..models import Category, CategoryRole
+from ....acl import ACL_CACHE
+from ....acl.models import Role
+from ....acl.admin.test import mock_role_form_data
+from ....admin.test import AdminTestCase
+from ....cache.test import assert_invalidates_cache
+from ...models import Category, CategoryRole
 
 
 
 
 def create_data(data_dict):
 def create_data(data_dict):

+ 0 - 0
misago/categories/admin/views/__init__.py


+ 5 - 5
misago/categories/views/categoriesadmin.py → misago/categories/admin/views/categories.py

@@ -2,12 +2,12 @@ from django.contrib import messages
 from django.shortcuts import redirect
 from django.shortcuts import redirect
 from django.utils.translation import gettext_lazy as _
 from django.utils.translation import gettext_lazy as _
 
 
-from .. import THREADS_ROOT_NAME
-from ...acl.cache import clear_acl_cache
-from ...admin.views import generic
-from ...threads.threadtypes import trees_map
+from ... import THREADS_ROOT_NAME
+from ....acl.cache import clear_acl_cache
+from ....admin.views import generic
+from ....threads.threadtypes import trees_map
+from ...models import Category, RoleCategoryACL
 from ..forms import CategoryFormFactory, DeleteFormFactory
 from ..forms import CategoryFormFactory, DeleteFormFactory
-from ..models import Category, RoleCategoryACL
 
 
 
 
 class CategoryAdmin(generic.AdminBaseMixin):
 class CategoryAdmin(generic.AdminBaseMixin):

+ 7 - 7
misago/categories/views/permsadmin.py → misago/categories/admin/views/perms.py

@@ -2,18 +2,18 @@ from django.contrib import messages
 from django.shortcuts import redirect
 from django.shortcuts import redirect
 from django.utils.translation import gettext_lazy as _
 from django.utils.translation import gettext_lazy as _
 
 
-from ...acl.cache import clear_acl_cache
-from ...acl.forms import get_permissions_forms
-from ...acl.models import Role
-from ...acl.views import RoleAdmin, RolesList
-from ...admin.views import generic
+from ....acl.admin.forms import get_permissions_forms
+from ....acl.admin.views import RoleAdmin, RolesList
+from ....acl.cache import clear_acl_cache
+from ....acl.models import Role
+from ....admin.views import generic
+from ...models import Category, CategoryRole, RoleCategoryACL
 from ..forms import (
 from ..forms import (
     CategoryRoleForm,
     CategoryRoleForm,
     CategoryRolesACLFormFactory,
     CategoryRolesACLFormFactory,
     RoleCategoryACLFormFactory,
     RoleCategoryACLFormFactory,
 )
 )
-from ..models import Category, CategoryRole, RoleCategoryACL
-from .categoriesadmin import CategoriesList, CategoryAdmin
+from .categories import CategoriesList, CategoryAdmin
 
 
 
 
 class CategoryRoleAdmin(generic.AdminBaseMixin):
 class CategoryRoleAdmin(generic.AdminBaseMixin):

+ 1 - 1
misago/categories/urls/__init__.py

@@ -3,7 +3,7 @@ from django.conf.urls import url
 from ...conf import settings
 from ...conf import settings
 from ...core.views import home_redirect
 from ...core.views import home_redirect
 
 
-from ..views.categorieslist import categories
+from ..views import categories
 
 
 if settings.MISAGO_THREADS_ON_INDEX:
 if settings.MISAGO_THREADS_ON_INDEX:
     URL_PATH = r"^categories/$"
     URL_PATH = r"^categories/$"

+ 2 - 2
misago/categories/views/categorieslist.py → misago/categories/views.py

@@ -1,8 +1,8 @@
 from django.shortcuts import render
 from django.shortcuts import render
 from django.urls import reverse
 from django.urls import reverse
 
 
-from ..serializers import CategoryWithPosterSerializer as CategorySerializer
-from ..utils import get_categories_tree
+from .serializers import CategoryWithPosterSerializer as CategorySerializer
+from .utils import get_categories_tree
 
 
 
 
 def categories(request):
 def categories(request):

+ 1 - 1
misago/legal/admin.py → misago/legal/admin/__init__.py

@@ -1,7 +1,7 @@
 from django.conf.urls import url
 from django.conf.urls import url
 from django.utils.translation import gettext_lazy as _
 from django.utils.translation import gettext_lazy as _
 
 
-from .views.admin import (
+from .views import (
     AgreementsList,
     AgreementsList,
     DeleteAgreement,
     DeleteAgreement,
     DisableAgreement,
     DisableAgreement,

+ 2 - 2
misago/legal/forms.py → misago/legal/admin/forms.py

@@ -2,8 +2,8 @@ from django import forms
 from django.db.models import Q
 from django.db.models import Q
 from django.utils.translation import gettext_lazy as _
 from django.utils.translation import gettext_lazy as _
 
 
-from ..admin.forms import YesNoSwitch
-from .models import Agreement
+from ...admin.forms import YesNoSwitch
+from ..models import Agreement
 from .utils import disable_agreement, set_agreement_as_active
 from .utils import disable_agreement, set_agreement_as_active
 
 
 
 

+ 0 - 0
misago/legal/admin/tests/__init__.py


+ 6 - 4
misago/legal/tests/test_agreements_admin.py → misago/legal/admin/tests/test_admin_views.py

@@ -1,9 +1,9 @@
 import pytest
 import pytest
 from django.urls import reverse
 from django.urls import reverse
 
 
-from ...admin.test import AdminTestCase
-from ...test import assert_contains
-from ..models import Agreement
+from ....admin.test import AdminTestCase
+from ....test import assert_contains
+from ...models import Agreement
 
 
 
 
 @pytest.fixture
 @pytest.fixture
@@ -115,7 +115,9 @@ def test_form_sets_new_agreement_creator(admin_client, superuser):
 
 
 
 
 def test_form_creates_active_agreement(mocker, admin_client):
 def test_form_creates_active_agreement(mocker, admin_client):
-    set_agreement_as_active = mocker.patch("misago.legal.forms.set_agreement_as_active")
+    set_agreement_as_active = mocker.patch(
+        "misago.legal.admin.forms.set_agreement_as_active"
+    )
     response = admin_client.post(
     response = admin_client.post(
         reverse("misago:admin:settings:agreements:new"),
         reverse("misago:admin:settings:agreements:new"),
         {
         {

+ 98 - 0
misago/legal/admin/tests/test_utils.py

@@ -0,0 +1,98 @@
+from ...models import Agreement, UserAgreement
+from ..utils import disable_agreement, set_agreement_as_active
+
+
+def test_activating_inactive_agreement_updates_its_flag_but_doesnt_commit_to_db(db):
+    agreement = Agreement.objects.create(
+        type=Agreement.TYPE_PRIVACY, link="https://somewhre.com", text="Lorem ipsum"
+    )
+
+    set_agreement_as_active(agreement)
+    assert agreement.is_active
+
+    agreement.refresh_from_db()
+    assert not agreement.is_active
+
+
+def test_activating_inactive_agreement_with_commit_updates_its_flag_in_db(db):
+    agreement = Agreement.objects.create(
+        type=Agreement.TYPE_PRIVACY, link="https://somewhre.com", text="Lorem ipsum"
+    )
+
+    set_agreement_as_active(agreement, commit=True)
+    assert agreement.is_active
+
+    agreement.refresh_from_db()
+    assert agreement.is_active
+
+
+def test_activating_agreement_deactivates_other_active_agreement_of_same_type(db):
+    old_agreement = Agreement.objects.create(
+        type=Agreement.TYPE_PRIVACY,
+        link="https://somewhre.com",
+        text="Lorem ipsum",
+        is_active=True,
+    )
+
+    new_agreement = Agreement.objects.create(
+        type=Agreement.TYPE_PRIVACY, link="https://somewhre.com", text="Lorem ipsum"
+    )
+
+    set_agreement_as_active(new_agreement, commit=True)
+
+    old_agreement.refresh_from_db()
+    new_agreement.refresh_from_db()
+
+    assert not old_agreement.is_active
+    assert new_agreement.is_active
+
+
+def test_activating_agreement_doesnt_deactivate_agreement_of_other_type(db):
+    agreement = Agreement.objects.create(
+        type=Agreement.TYPE_PRIVACY, link="https://somewhre.com", text="Lorem ipsum"
+    )
+
+    other_type_agreement = Agreement.objects.create(
+        type=Agreement.TYPE_TOS,
+        link="https://somewhre.com",
+        text="Lorem ipsum",
+        is_active=True,
+    )
+
+    set_agreement_as_active(agreement, commit=True)
+
+    agreement.refresh_from_db()
+    other_type_agreement.refresh_from_db()
+
+    assert agreement.is_active
+    assert other_type_agreement.is_active
+
+
+def test_disabling_active_agreement_updates_its_flag_but_doesnt_commit_to_db(db):
+    agreement = Agreement.objects.create(
+        type=Agreement.TYPE_PRIVACY,
+        link="https://somewhre.com",
+        text="Lorem ipsum",
+        is_active=True,
+    )
+
+    disable_agreement(agreement)
+    assert not agreement.is_active
+
+    agreement.refresh_from_db()
+    assert agreement.is_active
+
+
+def test_disabling_active_agreement_with_commit_updates_its_flag_in_db(db):
+    agreement = Agreement.objects.create(
+        type=Agreement.TYPE_PRIVACY,
+        link="https://somewhre.com",
+        text="Lorem ipsum",
+        is_active=True,
+    )
+
+    disable_agreement(agreement, commit=True)
+    assert not agreement.is_active
+
+    agreement.refresh_from_db()
+    assert not agreement.is_active

+ 19 - 0
misago/legal/admin/utils.py

@@ -0,0 +1,19 @@
+from ..models import Agreement
+
+
+def set_agreement_as_active(agreement, commit=False):
+    agreement.is_active = True
+    Agreement.objects.filter(type=agreement.type).exclude(pk=agreement.pk).update(
+        is_active=False
+    )
+
+    if commit:
+        agreement.save(update_fields=["is_active"])
+        Agreement.objects.invalidate_cache()
+
+
+def disable_agreement(agreement, commit=False):
+    agreement.is_active = False
+    if commit:
+        agreement.save(update_fields=["is_active"])
+        Agreement.objects.invalidate_cache()

+ 2 - 2
misago/legal/views/admin.py → misago/legal/admin/views.py

@@ -3,9 +3,9 @@ from django.utils import timezone
 from django.utils.translation import gettext_lazy as _
 from django.utils.translation import gettext_lazy as _
 
 
 from ...admin.views import generic
 from ...admin.views import generic
-from ..forms import AgreementForm, FilterAgreementsForm
 from ..models import Agreement
 from ..models import Agreement
-from ..utils import disable_agreement, set_agreement_as_active
+from .forms import AgreementForm, FilterAgreementsForm
+from .utils import disable_agreement, set_agreement_as_active
 
 
 
 
 class AgreementAdmin(generic.AdminBaseMixin):
 class AgreementAdmin(generic.AdminBaseMixin):

+ 0 - 54
misago/legal/tests/test_utils.py

@@ -6,7 +6,6 @@ from ..utils import (
     get_parsed_agreement_text,
     get_parsed_agreement_text,
     get_required_user_agreement,
     get_required_user_agreement,
     save_user_agreement_acceptance,
     save_user_agreement_acceptance,
-    set_agreement_as_active,
 )
 )
 
 
 
 
@@ -134,56 +133,3 @@ class SaveUserAgreementAcceptance(UserTestCase):
 
 
         UserAgreement.objects.get(user=user, agreement=agreement)
         UserAgreement.objects.get(user=user, agreement=agreement)
         self.assertEqual(UserAgreement.objects.count(), 1)
         self.assertEqual(UserAgreement.objects.count(), 1)
-
-
-class SetAgreementAsActiveTests(TestCase):
-    def test_inactive_agreement(self):
-        agreement = Agreement.objects.create(
-            type=Agreement.TYPE_PRIVACY, link="https://somewhre.com", text="Lorem ipsum"
-        )
-
-        set_agreement_as_active(agreement)
-        self.assertTrue(agreement.is_active)
-
-        agreement.refresh_from_db()
-        self.assertFalse(agreement.is_active)
-
-    def test_inactive_agreement_commit(self):
-        agreement = Agreement.objects.create(
-            type=Agreement.TYPE_PRIVACY, link="https://somewhre.com", text="Lorem ipsum"
-        )
-
-        set_agreement_as_active(agreement, commit=True)
-        self.assertTrue(agreement.is_active)
-
-        agreement.refresh_from_db()
-        self.assertTrue(agreement.is_active)
-
-    def test_change_active_agreement(self):
-        old_agreement = Agreement.objects.create(
-            type=Agreement.TYPE_PRIVACY,
-            link="https://somewhre.com",
-            text="Lorem ipsum",
-            is_active=True,
-        )
-
-        new_agreement = Agreement.objects.create(
-            type=Agreement.TYPE_PRIVACY, link="https://somewhre.com", text="Lorem ipsum"
-        )
-
-        other_type_agreement = Agreement.objects.create(
-            type=Agreement.TYPE_TOS,
-            link="https://somewhre.com",
-            text="Lorem ipsum",
-            is_active=True,
-        )
-
-        set_agreement_as_active(new_agreement, commit=True)
-
-        old_agreement.refresh_from_db()
-        new_agreement.refresh_from_db()
-        other_type_agreement.refresh_from_db()
-
-        self.assertFalse(old_agreement.is_active)
-        self.assertTrue(new_agreement.is_active)
-        self.assertTrue(other_type_agreement.is_active)

+ 0 - 18
misago/legal/utils.py

@@ -8,24 +8,6 @@ from ..markup import common_flavour
 from .models import Agreement, UserAgreement
 from .models import Agreement, UserAgreement
 
 
 
 
-def set_agreement_as_active(agreement, commit=False):
-    agreement.is_active = True
-    Agreement.objects.filter(type=agreement.type).exclude(pk=agreement.pk).update(
-        is_active=False
-    )
-
-    if commit:
-        agreement.save(update_fields=["is_active"])
-        Agreement.objects.invalidate_cache()
-
-
-def disable_agreement(agreement, commit=False):
-    agreement.is_active = False
-    if commit:
-        agreement.save(update_fields=["is_active"])
-        Agreement.objects.invalidate_cache()
-
-
 def get_required_user_agreement(user, agreements):
 def get_required_user_agreement(user, agreements):
     if user.is_anonymous:
     if user.is_anonymous:
         return None
         return None

+ 2 - 2
misago/legal/views/legal.py → misago/legal/views.py

@@ -1,7 +1,7 @@
 from django.shortcuts import get_object_or_404, redirect, render
 from django.shortcuts import get_object_or_404, redirect, render
 
 
-from ..models import Agreement
-from ..utils import get_parsed_agreement_text
+from .models import Agreement
+from .utils import get_parsed_agreement_text
 
 
 
 
 def legal_view(request, agreement_type):
 def legal_view(request, agreement_type):

+ 0 - 1
misago/legal/views/__init__.py

@@ -1 +0,0 @@
-from .legal import privacy_policy, terms_of_service

+ 2 - 2
misago/threads/admin.py → misago/threads/admin/__init__.py

@@ -1,8 +1,8 @@
 from django.conf.urls import url
 from django.conf.urls import url
 from django.utils.translation import gettext_lazy as _
 from django.utils.translation import gettext_lazy as _
 
 
-from .views.admin.attachments import AttachmentsList, DeleteAttachment
-from .views.admin.attachmenttypes import (
+from .views.attachments import AttachmentsList, DeleteAttachment
+from .views.attachmenttypes import (
     AttachmentTypesList,
     AttachmentTypesList,
     DeleteAttachmentType,
     DeleteAttachmentType,
     EditAttachmentType,
     EditAttachmentType,

+ 1 - 1
misago/threads/forms.py → misago/threads/admin/forms.py

@@ -1,7 +1,7 @@
 from django import forms
 from django import forms
 from django.utils.translation import gettext as _
 from django.utils.translation import gettext as _
 
 
-from .models import AttachmentType
+from ..models import AttachmentType
 
 
 
 
 def get_searchable_filetypes():
 def get_searchable_filetypes():

+ 0 - 0
misago/threads/admin/tests/__init__.py


+ 3 - 3
misago/threads/tests/test_attachmenttypeadmin_views.py → misago/threads/admin/tests/test_attachment_types_views.py

@@ -1,8 +1,8 @@
 from django.urls import reverse
 from django.urls import reverse
 
 
-from ...acl.models import Role
-from ...admin.test import AdminTestCase
-from ..models import AttachmentType
+from ....acl.models import Role
+from ....admin.test import AdminTestCase
+from ...models import AttachmentType
 
 
 
 
 class AttachmentTypeAdminViewsTests(AdminTestCase):
 class AttachmentTypeAdminViewsTests(AdminTestCase):

+ 4 - 4
misago/threads/tests/test_attachmentadmin_views.py → misago/threads/admin/tests/test_attachments_views.py

@@ -1,9 +1,9 @@
 from django.urls import reverse
 from django.urls import reverse
 
 
-from .. import test
-from ...admin.test import AdminTestCase
-from ...categories.models import Category
-from ..models import Attachment, AttachmentType
+from ... import test
+from ....admin.test import AdminTestCase
+from ....categories.models import Category
+from ...models import Attachment, AttachmentType
 
 
 
 
 class AttachmentAdminViewsTests(AdminTestCase):
 class AttachmentAdminViewsTests(AdminTestCase):

+ 0 - 0
misago/threads/admin/views/__init__.py


+ 1 - 1
misago/threads/views/admin/attachments.py → misago/threads/admin/views/attachments.py

@@ -3,8 +3,8 @@ from django.db import transaction
 from django.utils.translation import gettext_lazy as _
 from django.utils.translation import gettext_lazy as _
 
 
 from ....admin.views import generic
 from ....admin.views import generic
-from ...forms import FilterAttachmentsForm
 from ...models import Attachment, Post
 from ...models import Attachment, Post
+from ..forms import FilterAttachmentsForm
 
 
 
 
 class AttachmentAdmin(generic.AdminBaseMixin):
 class AttachmentAdmin(generic.AdminBaseMixin):

+ 1 - 1
misago/threads/views/admin/attachmenttypes.py → misago/threads/admin/views/attachmenttypes.py

@@ -3,8 +3,8 @@ from django.db.models import Count
 from django.utils.translation import gettext_lazy as _
 from django.utils.translation import gettext_lazy as _
 
 
 from ....admin.views import generic
 from ....admin.views import generic
-from ...forms import AttachmentTypeForm
 from ...models import AttachmentType
 from ...models import AttachmentType
+from ..forms import AttachmentTypeForm
 
 
 
 
 class AttachmentTypeAdmin(generic.AdminBaseMixin):
 class AttachmentTypeAdmin(generic.AdminBaseMixin):

+ 2 - 2
misago/users/admin/tests/test_bans.py

@@ -53,14 +53,14 @@ def test_mass_deleting_bans_invalidates_bans_cache(admin_client, admin_link, ban
         )
         )
 
 
 
 
-def test_ban_can_be_deleted(admin_client, admin_link, ban):
+def test_ban_can_be_deleted(admin_client, ban):
     admin_client.post(reverse("misago:admin:users:bans:delete", kwargs={"pk": ban.pk}))
     admin_client.post(reverse("misago:admin:users:bans:delete", kwargs={"pk": ban.pk}))
 
 
     with pytest.raises(Ban.DoesNotExist):
     with pytest.raises(Ban.DoesNotExist):
         ban.refresh_from_db()
         ban.refresh_from_db()
 
 
 
 
-def test_deleting_ban_invalidates_bans_cache(admin_client, admin_link, ban):
+def test_deleting_ban_invalidates_bans_cache(admin_client, ban):
     with assert_invalidates_cache(BANS_CACHE):
     with assert_invalidates_cache(BANS_CACHE):
         admin_client.post(
         admin_client.post(
             reverse("misago:admin:users:bans:delete", kwargs={"pk": ban.pk})
             reverse("misago:admin:users:bans:delete", kwargs={"pk": ban.pk})