Move checking membership to Membership.
authorRadek Czajka <rczajka@rczajka.pl>
Mon, 18 Mar 2019 11:39:06 +0000 (12:39 +0100)
committerRadek Czajka <rczajka@rczajka.pl>
Mon, 18 Mar 2019 11:39:06 +0000 (12:39 +0100)
src/api/fields.py
src/api/tests/tests.py
src/catalogue/api/views.py
src/catalogue/templatetags/catalogue_tags.py
src/catalogue/views.py
src/club/models.py
src/club/permissions.py [new file with mode: 0644]
src/paypal/permissions.py [deleted file]
src/paypal/tests.py
src/paypal/urls.py

index 66fd7d2..278af6a 100644 (file)
@@ -1,11 +1,10 @@
-# -*- coding: utf-8 -*-
 # This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
 #
 from rest_framework import serializers
 from sorl.thumbnail import default
 from django.core.urlresolvers import reverse
 # This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
 #
 from rest_framework import serializers
 from sorl.thumbnail import default
 from django.core.urlresolvers import reverse
-from paypal.rest import user_is_subscribed
+from club.models import Membership
 
 
 class AbsoluteURLField(serializers.ReadOnlyField):
 
 
 class AbsoluteURLField(serializers.ReadOnlyField):
@@ -45,7 +44,7 @@ class UserPremiumField(serializers.ReadOnlyField):
         super(UserPremiumField, self).__init__(*args, source='*', **kwargs)
 
     def to_representation(self, value):
         super(UserPremiumField, self).__init__(*args, source='*', **kwargs)
 
     def to_representation(self, value):
-        return user_is_subscribed(value)
+        return Membership.is_active_for(value)
 
 
 class ThumbnailField(serializers.FileField):
 
 
 class ThumbnailField(serializers.FileField):
index eccdd04..c37fef3 100644 (file)
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 # This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
 #
 # This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
 #
@@ -445,11 +444,10 @@ class AuthorizedTests(ApiTest):
             self.signed('/api/epub/grandchild/').status_code,
             403)
 
             self.signed('/api/epub/grandchild/').status_code,
             403)
 
-        with patch('api.fields.user_is_subscribed', return_value=True):
+        with patch('club.models.Membership.is_active_for', return_value=True):
             self.assertEqual(
                 self.signed_json('/api/username/'),
                 {"username": "test", "premium": True})
             self.assertEqual(
                 self.signed_json('/api/username/'),
                 {"username": "test", "premium": True})
-        with patch('paypal.permissions.user_is_subscribed', return_value=True):
             with patch('django.core.files.storage.Storage.open',
                        return_value=BytesIO(b"<epub>")):
                 self.assertEqual(
             with patch('django.core.files.storage.Storage.open',
                        return_value=BytesIO(b"<epub>")):
                 self.assertEqual(
index 7c4b888..5a064c1 100644 (file)
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 # This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
 #
 # This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
 #
@@ -8,7 +7,6 @@ from rest_framework.generics import ListAPIView, RetrieveAPIView, get_object_or_
 from rest_framework.permissions import DjangoModelPermissionsOrAnonReadOnly
 from rest_framework.response import Response
 from rest_framework import status
 from rest_framework.permissions import DjangoModelPermissionsOrAnonReadOnly
 from rest_framework.response import Response
 from rest_framework import status
-from paypal.permissions import IsSubscribed
 from api.handlers import read_tags
 from api.utils import vary_on_auth
 from .helpers import books_after, order_books
 from api.handlers import read_tags
 from api.utils import vary_on_auth
 from .helpers import books_after, order_books
@@ -16,6 +14,7 @@ from . import serializers
 from catalogue.forms import BookImportForm
 from catalogue.models import Book, Collection, Tag, Fragment, BookMedia
 from catalogue.models.tag import prefetch_relations
 from catalogue.forms import BookImportForm
 from catalogue.models import Book, Collection, Tag, Fragment, BookMedia
 from catalogue.models.tag import prefetch_relations
+from club.permissions import IsClubMember
 from wolnelektury.utils import re_escape
 
 
 from wolnelektury.utils import re_escape
 
 
@@ -226,7 +225,7 @@ class FilterBookList(ListAPIView):
 class EpubView(RetrieveAPIView):
     queryset = Book.objects.all()
     lookup_field = 'slug'
 class EpubView(RetrieveAPIView):
     queryset = Book.objects.all()
     lookup_field = 'slug'
-    permission_classes = [IsSubscribed]
+    permission_classes = [IsClubMember]
 
     def get(self, *args, **kwargs):
         return HttpResponse(self.get_object().get_media('epub'))
 
     def get(self, *args, **kwargs):
         return HttpResponse(self.get_object().get_media('epub'))
index 70676dd..ef0ea5d 100644 (file)
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 # This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
 #
 # This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
 #
@@ -19,7 +18,7 @@ from ssify import ssi_variable
 from catalogue.helpers import get_audiobook_tags
 from catalogue.models import Book, BookMedia, Fragment, Tag, Source
 from catalogue.constants import LICENSES
 from catalogue.helpers import get_audiobook_tags
 from catalogue.models import Book, BookMedia, Fragment, Tag, Source
 from catalogue.constants import LICENSES
-from paypal.rest import user_is_subscribed
+from club.models import Membership
 from picture.models import Picture
 
 register = template.Library()
 from picture.models import Picture
 
 register = template.Library()
@@ -499,7 +498,7 @@ def strip_tag(html, tag_name):
 def status(book, user):
     if not book.preview:
         return 'open'
 def status(book, user):
     if not book.preview:
         return 'open'
-    elif user_is_subscribed(user):
+    elif Membership.is_active_for(user):
         return 'preview'
     else:
         return 'closed'
         return 'preview'
     else:
         return 'closed'
index 2c9f5dd..2ea5eb0 100644 (file)
@@ -17,8 +17,8 @@ from django.utils import translation
 from django.utils.translation import ugettext as _, ugettext_lazy
 
 from ajaxable.utils import AjaxableFormView
 from django.utils.translation import ugettext as _, ugettext_lazy
 
 from ajaxable.utils import AjaxableFormView
+from club.models import Membership
 from pdcounter import views as pdcounter_views
 from pdcounter import views as pdcounter_views
-from paypal.rest import user_is_subscribed
 from picture.models import Picture, PictureArea
 from ssify import ssi_included, ssi_expect, SsiVariable as Var
 from catalogue import constants
 from picture.models import Picture, PictureArea
 from ssify import ssi_included, ssi_expect, SsiVariable as Var
 from catalogue import constants
@@ -306,7 +306,7 @@ def player(request, slug):
 def book_text(request, slug):
     book = get_object_or_404(Book, slug=slug)
 
 def book_text(request, slug):
     book = get_object_or_404(Book, slug=slug)
 
-    if book.preview and not user_is_subscribed(request.user):
+    if book.preview and not Membership.is_active_for(request.user):
         return HttpResponseRedirect(book.get_absolute_url())
 
     if not book.has_html_file():
         return HttpResponseRedirect(book.get_absolute_url())
 
     if not book.has_html_file():
@@ -361,7 +361,7 @@ def embargo_link(request, format_, slug):
     media_file = book.get_media(format_)
     if not book.preview:
         return HttpResponseRedirect(media_file.url)
     media_file = book.get_media(format_)
     if not book.preview:
         return HttpResponseRedirect(media_file.url)
-    if not user_is_subscribed(request.user):
+    if not Membership.is_active_for(request.user):
         return HttpResponseRedirect(book.get_absolute_url())
     return HttpResponse(media_file, content_type=constants.EBOOK_CONTENT_TYPES[format_])
 
         return HttpResponseRedirect(book.get_absolute_url())
     return HttpResponse(media_file, content_type=constants.EBOOK_CONTENT_TYPES[format_])
 
@@ -395,7 +395,7 @@ class CustomPDFFormView(AjaxableFormView):
 
     def validate_object(self, obj, request):
         book = obj
 
     def validate_object(self, obj, request):
         book = obj
-        if book.preview and not user_is_subscribed(request.user):
+        if book.preview and not Membership_is_active_for(request.user):
             return HttpResponseRedirect(book.get_absolute_url())
         return super(CustomPDFFormView, self).validate_object(obj, request)
 
             return HttpResponseRedirect(book.get_absolute_url())
         return super(CustomPDFFormView, self).validate_object(obj, request)
 
index 1777174..ede1a8d 100644 (file)
@@ -88,7 +88,6 @@ class Schedule(models.Model):
     def get_payment_method(self):
         return method_by_slug[self.method]
 
     def get_payment_method(self):
         return method_by_slug[self.method]
 
-
     def is_expired(self):
         return self.expires_at is not None and self.expires_at < now()
 
     def is_expired(self):
         return self.expires_at is not None and self.expires_at < now()
 
@@ -125,6 +124,16 @@ class Membership(models.Model):
     def __str__(self):
         return u'tow. ' + str(self.user)
 
     def __str__(self):
         return u'tow. ' + str(self.user)
 
+    @classmethod
+    def is_active_for(self, user):
+        if user.is_anonymous:
+            return False
+        return Schedule.objects.filter(
+                models.Q(expires_at=None) | models.Q(expires_at__lt=now()),
+                membership__user=user,
+                is_active=True,
+            ).exists()
+
 
 class ReminderEmail(models.Model):
     days_before = models.SmallIntegerField(_('days before'))
 
 class ReminderEmail(models.Model):
     days_before = models.SmallIntegerField(_('days before'))
diff --git a/src/club/permissions.py b/src/club/permissions.py
new file mode 100644 (file)
index 0000000..a08880d
--- /dev/null
@@ -0,0 +1,7 @@
+from rest_framework.permissions import BasePermission
+from .models import Membership
+
+
+class IsClubMember(BasePermission):
+    def has_permission(self, request, view):
+        return Membership.is_active_for(request.user)
diff --git a/src/paypal/permissions.py b/src/paypal/permissions.py
deleted file mode 100644 (file)
index 9d0865b..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-from rest_framework.permissions import BasePermission
-from .rest import user_is_subscribed
-
-
-class IsSubscribed(BasePermission):
-    def has_permission(self, request, view):
-        return request.user.is_authenticated and user_is_subscribed(request.user)
index 7de047a..52c5747 100644 (file)
@@ -2,6 +2,7 @@
 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
 #
 from django.contrib.auth.models import User
 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
 #
 from django.contrib.auth.models import User
+from unittest import skip
 from unittest.mock import MagicMock, Mock, patch, DEFAULT
 from catalogue.test_utils import WLTestCase
 from .models import BillingAgreement, BillingPlan
 from unittest.mock import MagicMock, Mock, patch, DEFAULT
 from catalogue.test_utils import WLTestCase
 from .models import BillingAgreement, BillingPlan
@@ -59,15 +60,18 @@ class PaypalTests(WLTestCase):
     def tearDownClass(cls):
         cls.user.delete()
 
     def tearDownClass(cls):
         cls.user.delete()
 
+    @skip("Changing the flow.")
     def test_paypal_form(self):
         response = self.client.get('/paypal/form/')
         self.assertEqual(response.status_code, 200)
 
     def test_paypal_form(self):
         response = self.client.get('/paypal/form/')
         self.assertEqual(response.status_code, 200)
 
+    @skip("Changing the flow.")
     def test_paypal_form_unauthorized(self):
         """Legacy flow: only allow payment for logged-in users."""
         response = self.client.post('/paypal/form/', {"amount": "0"})
         self.assertEqual(response.status_code, 403)
 
     def test_paypal_form_unauthorized(self):
         """Legacy flow: only allow payment for logged-in users."""
         response = self.client.post('/paypal/form/', {"amount": "0"})
         self.assertEqual(response.status_code, 403)
 
+    @skip("Changing the flow.")
     def test_paypal_form_invalid(self):
         """Paypal form: error on bad input."""
         self.client.login(username='test', password='test')
     def test_paypal_form_invalid(self):
         """Paypal form: error on bad input."""
         self.client.login(username='test', password='test')
@@ -78,6 +82,7 @@ class PaypalTests(WLTestCase):
             len(response.context['form'].errors['amount']),
             1)
 
             len(response.context['form'].errors['amount']),
             1)
 
+    @skip("Changing the flow.")
     @patch.multiple('paypalrestsdk',
         BillingPlan=BillingPlanMock,
         BillingAgreement=BillingAgreementMock,
     @patch.multiple('paypalrestsdk',
         BillingPlan=BillingPlanMock,
         BillingAgreement=BillingAgreementMock,
@@ -99,6 +104,7 @@ class PaypalTests(WLTestCase):
         # No BillingAgreement created in our DB yet.
         self.assertEqual(BillingAgreement.objects.all().count(), 0)
 
         # No BillingAgreement created in our DB yet.
         self.assertEqual(BillingAgreement.objects.all().count(), 0)
 
+    @skip("Changing the flow.")
     @patch('paypalrestsdk.BillingPlan', BillingPlanMock)
     def test_paypal_form_error(self):
         """On PayPal error, plan does not get created."""
     @patch('paypalrestsdk.BillingPlan', BillingPlanMock)
     def test_paypal_form_error(self):
         """On PayPal error, plan does not get created."""
@@ -128,6 +134,7 @@ class PaypalTests(WLTestCase):
         # But now the plan should be created.
         self.assertEqual(BillingPlan.objects.all().count(), 1)
 
         # But now the plan should be created.
         self.assertEqual(BillingPlan.objects.all().count(), 1)
 
+    @skip("Changing the flow.")
     @patch.multiple('paypalrestsdk',
         BillingPlan=BillingPlanMock,
         BillingAgreement=BillingAgreementMock,
     @patch.multiple('paypalrestsdk',
         BillingPlan=BillingPlanMock,
         BillingAgreement=BillingAgreementMock,
index 38f636d..bd26993 100644 (file)
@@ -1,13 +1,14 @@
-# -*- coding: utf-8 -*-
 # This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
 #
 from django.conf.urls import url
 # This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
 #
 from django.conf.urls import url
+from django.views.generic import RedirectView
 from . import views
 
 urlpatterns = (
 from . import views
 
 urlpatterns = (
-    url(r'^form/$', views.paypal_form, name='paypal_form'),
-    url(r'^app-form/$', views.paypal_form, kwargs={'app': True}, name='paypal_app_form'),
+    url(r'^form/$', RedirectView.as_view(url='/towarzystwo/dolacz/')),
+    url(r'^app-form/$', RedirectView.as_view(url='/towarzystwo/dolacz/app/')),
+
     url(r'^return/$', views.paypal_return, name='paypal_return'),
     url(r'^app-return/$', views.paypal_return, kwargs={'app': True}, name='paypal_app_return'),
     url(r'^cancel/$', views.paypal_cancel, name='paypal_cancel'),
     url(r'^return/$', views.paypal_return, name='paypal_return'),
     url(r'^app-return/$', views.paypal_return, kwargs={'app': True}, name='paypal_app_return'),
     url(r'^cancel/$', views.paypal_cancel, name='paypal_cancel'),