Просмотр исходного кода

Backend logic for accept/reject new agreement

Rafał Pitoń 7 лет назад
Родитель
Сommit
9b5f2da6cc

+ 29 - 0
misago/legal/api.py

@@ -0,0 +1,29 @@
+from django.contrib.auth import logout
+from django.core.exceptions import PermissionDenied
+from django.shortcuts import get_object_or_404
+from django.utils.translation import ugettext as _
+
+from rest_framework.decorators import api_view
+from rest_framework.response import Response
+
+from .models import Agreement
+from .utils import save_user_agreement_acceptance
+
+
+@api_view(['POST'])
+def submit_agreement(request, pk):
+    agreement = get_object_or_404(Agreement, is_active=True, pk=pk)
+
+    if agreement.id in request.user.agreements:
+        raise PermissionDenied(_("You have already accepted this agreement."))
+
+    if request.data.get('accept') is True:
+        save_user_agreement_acceptance(request.user, agreement, commit=True)
+    elif request.data.get('accept') is False:
+        if not request.user.is_staff:
+            request.user.mark_for_delete()
+            logout(request)
+    else:
+        raise PermissionDenied(_("You need to submit a valid choice."))
+
+    return Response({'detail': 'ok'})

+ 2 - 1
misago/legal/context_processors.py

@@ -33,7 +33,8 @@ def legal_links(request):
 
     required_agreement = get_required_user_agreement(request.user, agreements)
     if required_agreement:
-        request.frontend_context['REQUIRED_AGREEMENT_ID'] = required_agreement.id
+        request.frontend_context['REQUIRED_AGREEMENT'] = reverse(
+            'misago:api:submit-agreement', kwargs={'pk': required_agreement.pk})
 
         legal_context['misago_agreement'] = {
             'title': required_agreement.get_final_title(),

+ 93 - 0
misago/legal/tests/test_api.py

@@ -0,0 +1,93 @@
+import json
+
+from django.urls import reverse
+
+from misago.legal.models import Agreement
+from misago.users.testutils import AuthenticatedUserTestCase
+
+
+class SubmitAgreementTests(AuthenticatedUserTestCase):
+    def setUp(self):
+        super(SubmitAgreementTests, self).setUp()
+
+        self.agreement = Agreement.objects.create(
+            type=Agreement.TYPE_TOS,
+            text='Lorem ipsum',
+            is_active=True,
+        )
+
+        self.api_link = reverse(
+            'misago:api:submit-agreement', kwargs={'pk': self.agreement.pk})
+
+    def post_json(self, data):
+        return self.client.post(
+            self.api_link, json.dumps(data), content_type='application/json')
+
+    def test_anonymous(self):
+        self.logout_user()
+
+        response = self.client.post(self.api_link)
+        self.assertEqual(response.status_code, 403)
+        self.assertEqual(response.json(), {
+            'detail': "This action is not available to guests.",
+        })
+
+    def test_get_request(self):
+        response = self.client.get(self.api_link)
+        self.assertEqual(response.status_code, 405)
+        self.assertEqual(response.json(), {
+            'detail': 'Method "GET" not allowed.',
+        })
+
+    def test_invalid_agreement_id(self):
+        api_link = reverse(
+            'misago:api:submit-agreement', kwargs={'pk': self.agreement.pk + 1})
+
+        response = self.client.post(api_link)
+        self.assertEqual(response.status_code, 404)
+        self.assertEqual(response.json(), {
+            'detail': "Not found.",
+        })
+
+    def test_agreement_already_accepted(self):
+        self.user.agreements.append(self.agreement.id)
+        self.user.save()
+
+        response = self.client.post(self.api_link)
+        self.assertEqual(response.status_code, 403)
+        self.assertEqual(response.json(), {
+            'detail': "You have already accepted this agreement.",
+        })
+
+    def test_no_accept_sent(self):
+        response = self.client.post(self.api_link)
+        self.assertEqual(response.status_code, 403)
+        self.assertEqual(response.json(), {
+            'detail': "You need to submit a valid choice.",
+        })
+
+    def test_invalid_accept_sent(self):
+        response = self.post_json({'accept': 1})
+        self.assertEqual(response.status_code, 403)
+        self.assertEqual(response.json(), {
+            'detail': "You need to submit a valid choice.",
+        })
+
+    def test_accept_false(self):
+        response = self.post_json({'accept': False})
+        self.assertEqual(response.status_code, 200)
+        self.assertEqual(response.json(), {'detail': 'ok'})
+
+        self.user.refresh_from_db()
+        self.assertTrue(self.user.is_deleting_account)
+        self.assertFalse(self.user.is_active)
+
+    def test_accept_true(self):
+        response = self.post_json({'accept': True})
+        self.assertEqual(response.status_code, 200)
+        self.assertEqual(response.json(), {'detail': 'ok'})
+
+        self.user.refresh_from_db()
+        self.assertEqual(self.user.agreements, [self.agreement.id])
+        self.assertFalse(self.user.is_deleting_account)
+        self.assertTrue(self.user.is_active)

+ 41 - 2
misago/legal/tests/test_utils.py

@@ -1,8 +1,9 @@
 from django.test import TestCase
 
-from misago.legal.models import Agreement
+from misago.legal.models import Agreement, UserAgreement
 from misago.legal.utils import (
-    get_parsed_agreement_text, get_required_user_agreement, set_agreement_as_active
+    get_parsed_agreement_text, get_required_user_agreement, save_user_agreement_acceptance,
+    set_agreement_as_active
 )
 from misago.users.testutils import UserTestCase
 
@@ -88,6 +89,44 @@ class GetRequiredUserAgreementTests(UserTestCase):
         self.assertIsNone(result)
 
 
+class SaveUserAgreementAcceptance(UserTestCase):
+    def test_no_commit(self):
+        user = self.get_authenticated_user()
+
+        agreement = Agreement.objects.create(
+            type=Agreement.TYPE_PRIVACY,
+            link='https://somewhre.com',
+            text='Lorem ipsum',
+        )
+
+        save_user_agreement_acceptance(user, agreement)
+        self.assertEqual(user.agreements, [agreement.id])
+
+        user.refresh_from_db()
+        self.assertEqual(user.agreements, [])
+
+        UserAgreement.objects.get(user=user, agreement=agreement)
+        self.assertEqual(UserAgreement.objects.count(), 1)
+
+    def test_commit(self):
+        user = self.get_authenticated_user()
+
+        agreement = Agreement.objects.create(
+            type=Agreement.TYPE_PRIVACY,
+            link='https://somewhre.com',
+            text='Lorem ipsum',
+        )
+
+        save_user_agreement_acceptance(user, agreement, commit=True)
+        self.assertEqual(user.agreements, [agreement.id])
+
+        user.refresh_from_db()
+        self.assertEqual(user.agreements, [agreement.id])
+
+        UserAgreement.objects.get(user=user, agreement=agreement)
+        self.assertEqual(UserAgreement.objects.count(), 1)
+
+
 class SetAgreementAsActiveTests(TestCase):
      def test_inactive_agreement(self):
         agreement = Agreement.objects.create(

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

@@ -1,6 +1,6 @@
 from django.conf.urls import url
 
-from .views import privacy_policy, terms_of_service
+from misago.legal.views import privacy_policy, terms_of_service
 
 
 urlpatterns = [

+ 8 - 0
misago/legal/urls/api.py

@@ -0,0 +1,8 @@
+from django.conf.urls import url
+
+from misago.legal.api import submit_agreement
+
+
+urlpatterns = [
+    url(r'^submit-agreement/(?P<pk>\d+)/$', submit_agreement, name='submit-agreement'),
+]

+ 10 - 2
misago/legal/utils.py

@@ -6,14 +6,14 @@ from django.utils.encoding import force_bytes
 from misago.core.cache import cache
 from misago.markup import common_flavour
 
-from .models import Agreement
+from .models import Agreement, UserAgreement
 
 
 def set_agreement_as_active(agreement, commit=False):
     agreement.is_active = True
     queryset = Agreement.objects.filter(type=agreement.type).exclude(pk=agreement.pk)
     queryset.update(is_active=False)
-    
+
     if commit:
         agreement.save(update_fields=['is_active'])
         Agreement.objects.invalidate_cache()
@@ -56,3 +56,11 @@ def get_parsed_agreement_text(request, agreement):
         }
         cache.set(cache_name, cached_content)
         return cached_content['parsed']
+
+
+def save_user_agreement_acceptance(user, agreement, commit=False):
+    user.agreements.append(agreement.id)
+    UserAgreement.objects.create(agreement=agreement, user=user)
+
+    if commit:
+        user.save(update_fields=['agreements'])

+ 1 - 0
misago/urls.py

@@ -32,6 +32,7 @@ urlpatterns = [
 # Register API
 apipatterns = [
     url(r'^', include('misago.categories.urls.api')),
+    url(r'^', include('misago.legal.urls.api')),
     url(r'^', include('misago.markup.urls')),
     url(r'^', include('misago.threads.urls.api')),
     url(r'^', include('misago.users.urls.api')),