From 16ea958a176bc60e669944edd5d1eb393d6aa90c Mon Sep 17 00:00:00 2001 From: Jan Szejko Date: Tue, 12 Jun 2018 11:16:35 +0200 Subject: [PATCH 01/16] fix layout in book box --- src/catalogue/templates/catalogue/book_short.html | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/catalogue/templates/catalogue/book_short.html b/src/catalogue/templates/catalogue/book_short.html index b69329bc4..0361e8d5d 100644 --- a/src/catalogue/templates/catalogue/book_short.html +++ b/src/catalogue/templates/catalogue/book_short.html @@ -145,11 +145,6 @@ {% block book-box-extra-info %}{% endblock %} {% block box-append %}{% endblock %} - {% if book.abstract %} -
- {{ book.abstract|safe }} -
- {% endif %} {% endwith %} {% block right-column %} @@ -159,6 +154,11 @@ {% endif %} {% endblock %} + {% if book.abstract %} +
+ {{ book.abstract|safe }} +
+ {% endif %}
-- 2.20.1 From 1f809233f650a7fb6528fdec525fca49df415e3d Mon Sep 17 00:00:00 2001 From: Jan Szejko Date: Tue, 12 Jun 2018 12:37:21 +0200 Subject: [PATCH 02/16] allow custom scheme for redirect to app --- src/api/piston_patch.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/api/piston_patch.py b/src/api/piston_patch.py index 4bf9b615d..fb3987bde 100644 --- a/src/api/piston_patch.py +++ b/src/api/piston_patch.py @@ -15,6 +15,10 @@ from piston import oauth from piston.authentication import initialize_server_request, INVALID_PARAMS_RESPONSE, send_oauth_error +class HttpResponseAppRedirect(HttpResponseRedirect): + allowed_schemes = HttpResponseRedirect.allowed_schemes + ['wolnelekturyapp'] + + class OAuthAuthenticationForm(forms.Form): oauth_token = forms.CharField(widget=forms.HiddenInput) oauth_callback = forms.CharField(widget=forms.HiddenInput) # changed from URLField - too strict @@ -52,6 +56,7 @@ class OAuthAuthenticationForm(forms.Form): # The only thing changed in the views below is the form used +# and also the Http Redirect class def oauth_auth_view(request, token, callback, params): @@ -102,7 +107,7 @@ def oauth_user_auth(request): callback = getattr(settings, 'OAUTH_CALLBACK_VIEW') return get_callable(callback)(request, token) - response = HttpResponseRedirect(callback + args) + response = HttpResponseAppRedirect(callback + args) except oauth.OAuthError, err: response = send_oauth_error(err) -- 2.20.1 From 1624704136cd3bbf1dba92be38f11c26addc1e0d Mon Sep 17 00:00:00 2001 From: Jan Szejko Date: Tue, 12 Jun 2018 15:39:35 +0200 Subject: [PATCH 03/16] update librarian --- lib/librarian | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/librarian b/lib/librarian index c220cf276..0c0fdd2db 160000 --- a/lib/librarian +++ b/lib/librarian @@ -1 +1 @@ -Subproject commit c220cf2765a0c7bef4094b2628a20f45d99f6068 +Subproject commit 0c0fdd2db2c1277108b03a1acf88ad63d140451a -- 2.20.1 From f25170a0a98f982f01ca5d93425ce85477bd930e Mon Sep 17 00:00:00 2001 From: Jan Szejko Date: Mon, 18 Jun 2018 10:41:28 +0200 Subject: [PATCH 04/16] api for username --- src/api/handlers.py | 10 ++++++---- src/api/urls.py | 1 + 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/api/handlers.py b/src/api/handlers.py index 3e4e0931a..ab1a44a52 100644 --- a/src/api/handlers.py +++ b/src/api/handlers.py @@ -656,16 +656,18 @@ class PictureHandler(BaseHandler): class UserDataHandler(BaseHandler): model = BookUserData - fields = ('state',) + fields = ('state', 'username') allowed_methods = ('GET', 'POST') - def read(self, request, slug): + def read(self, request, slug=None): + if not request.user.is_authenticated(): + return rc.FORBIDDEN + if slug is None: + return {'username': request.user.username} try: book = Book.objects.get(slug=slug) except Book.DoesNotExist: return rc.NOT_FOUND - if not request.user.is_authenticated(): - return rc.FORBIDDEN try: data = BookUserData.objects.get(book=book, user=request.user) except BookUserData.DoesNotExist: diff --git a/src/api/urls.py b/src/api/urls.py index 006abed60..f90ec3e7d 100644 --- a/src/api/urls.py +++ b/src/api/urls.py @@ -80,6 +80,7 @@ urlpatterns = [ url(r'^reading/(?P[a-z0-9-]+)/$', reading_resource, name='api_reading'), url(r'^reading/(?P[a-z0-9-]+)/(?P[a-z]+)/$', reading_resource, name='api_reading'), url(r'^shelf/(?P[a-z]+)/$', shelf_resource, name='api_shelf'), + url(r'^username/$', reading_resource, name='api_username'), # objects details url(r'^books/(?P[a-z0-9-]+)/$', book_resource, name="api_book"), -- 2.20.1 From fa8ae232e2fb6480e1f99bdb8393eaf682c0d2d1 Mon Sep 17 00:00:00 2001 From: Jan Szejko Date: Tue, 19 Jun 2018 10:01:47 +0200 Subject: [PATCH 05/16] set authenticaton for api resources --- src/api/urls.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/api/urls.py b/src/api/urls.py index f90ec3e7d..c5ede8fcb 100644 --- a/src/api/urls.py +++ b/src/api/urls.py @@ -20,10 +20,10 @@ ebook_list_resource = Resource(handler=handlers.EBooksHandler) # book_list_resource = Resource(handler=handlers.BooksHandler) book_resource = Resource(handler=handlers.BookDetailHandler) filter_book_resource = Resource(handler=handlers.FilterBooksHandler) -epub_resource = Resource(handler=handlers.EpubHandler) +epub_resource = Resource(handler=handlers.EpubHandler, authentication=auth) -reading_resource = CsrfExemptResource(handler=handlers.UserDataHandler) -shelf_resource = Resource(handler=handlers.UserShelfHandler) +reading_resource = CsrfExemptResource(handler=handlers.UserDataHandler, authentication=auth) +shelf_resource = Resource(handler=handlers.UserShelfHandler, authentication=auth) collection_resource = Resource(handler=handlers.CollectionDetailHandler) collection_list_resource = Resource(handler=handlers.CollectionsHandler) -- 2.20.1 From 7b2b19383cd52f1df1e7d1bb8a29d6e099ce7a5b Mon Sep 17 00:00:00 2001 From: Jan Szejko Date: Tue, 19 Jun 2018 11:47:01 +0200 Subject: [PATCH 06/16] accept https in dc:rights.license --- src/catalogue/constants.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/catalogue/constants.py b/src/catalogue/constants.py index c16e3f7bb..60cecebe5 100644 --- a/src/catalogue/constants.py +++ b/src/catalogue/constants.py @@ -17,6 +17,9 @@ LICENSES = { LICENSES['http://creativecommons.org/licenses/by-sa/3.0/deed.pl'] = \ LICENSES['http://creativecommons.org/licenses/by-sa/3.0/'] +for license, data in LICENSES.items(): + LICENSES[license.replace('http://', 'https://')] = data + # Those will be generated only for books with own HTML. EBOOK_FORMATS_WITHOUT_CHILDREN = ['txt', 'fb2'] # Those will be generated for all books. -- 2.20.1 From 2983d583c9de4c33ed73d6c06d9d6eb54b30f15e Mon Sep 17 00:00:00 2001 From: Jan Szejko Date: Tue, 3 Jul 2018 12:09:29 +0200 Subject: [PATCH 07/16] vary template cache on language --- src/catalogue/templates/catalogue/book_detail.html | 3 ++- src/catalogue/templates/catalogue/book_text.html | 3 ++- src/catalogue/templates/catalogue/work-list.html | 12 +++++++++++- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/catalogue/templates/catalogue/book_detail.html b/src/catalogue/templates/catalogue/book_detail.html index 3b13624a4..cf26b125a 100644 --- a/src/catalogue/templates/catalogue/book_detail.html +++ b/src/catalogue/templates/catalogue/book_detail.html @@ -13,7 +13,8 @@ {% block bodyid %}book-detail{% endblock %} {% block body %} - {% cache 86400 book_wide book.pk book|status:user %} + {% get_current_language as LANGUAGE_CODE %} + {% cache 86400 book_wide book.pk book|status:user LANGUAGE_CODE %} {% include 'catalogue/book_wide.html' %} {% endcache %} diff --git a/src/catalogue/templates/catalogue/book_text.html b/src/catalogue/templates/catalogue/book_text.html index f697e4ee2..5894fe825 100644 --- a/src/catalogue/templates/catalogue/book_text.html +++ b/src/catalogue/templates/catalogue/book_text.html @@ -110,7 +110,8 @@
- {% cache 86400 catalogue_book_short book.pk book|status:user %} + {% get_current_language as LANGUAGE_CODE %} + {% cache 86400 book_short book.pk book|status:user LANGUAGE_CODE %} {% include 'catalogue/book_short.html' %} {% endcache %}
diff --git a/src/catalogue/templates/catalogue/work-list.html b/src/catalogue/templates/catalogue/work-list.html index 03833c16f..178654c70 100755 --- a/src/catalogue/templates/catalogue/work-list.html +++ b/src/catalogue/templates/catalogue/work-list.html @@ -1,7 +1,10 @@ {% spaceless %} + {% load cache %} + {% load i18n %} {% load pagination_tags %} {% load class_name from catalogue_tags %} + {% load status from catalogue_tags %} {% load ssi_include from ssify %} {% autopaginate object_list 10 %} @@ -9,7 +12,14 @@
    {% for item in object_list %}
  1. - {% ssi_include item.short_html_url_name pk=item.pk %} + {% if item|class_name == 'Book' %} + {% get_current_language as LANGUAGE_CODE %} + {% cache 86400 book_short item.pk item|status:request.user LANGUAGE_CODE %} + {% include "catalogue/book_short.html" with book=item %} + {% endcache %} + {% else %} + {% ssi_include item.short_html_url_name pk=item.pk %} + {% endif %}
  2. {% endfor %}
-- 2.20.1 From 10b367c03d1e3a8ad6e365b02a7234bfd640e3f6 Mon Sep 17 00:00:00 2001 From: Jan Szejko Date: Wed, 4 Jul 2018 09:23:42 +0200 Subject: [PATCH 08/16] clear cached template fragments on book save --- src/catalogue/fields.py | 5 ++++- src/catalogue/signals.py | 6 ++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/catalogue/fields.py b/src/catalogue/fields.py index 1ed34e2c5..92e8de48c 100644 --- a/src/catalogue/fields.py +++ b/src/catalogue/fields.py @@ -109,9 +109,12 @@ class BuildEbook(Task): fieldfile.save(None, File(open(out.get_filename())), save=False) self.set_file_permissions(fieldfile) if book.pk is not None: - type(book).objects.filter(pk=book.pk).update(**{ + books = type(book).objects.filter(pk=book.pk) + books.update(**{ fieldfile.field.attname: fieldfile }) + for book in books: + book.save() # just to trigger post-save if fieldfile.field.format_name in app_settings.FORMAT_ZIPS: remove_zip(app_settings.FORMAT_ZIPS[fieldfile.field.format_name]) # Don't decorate BuildEbook, because we want to subclass it. diff --git a/src/catalogue/signals.py b/src/catalogue/signals.py index f3cacaaf7..28d84bece 100644 --- a/src/catalogue/signals.py +++ b/src/catalogue/signals.py @@ -4,6 +4,7 @@ # from django.conf import settings from django.core.cache import caches +from django.core.exceptions import ImproperlyConfigured from django.db.models.signals import post_save, post_delete from django.dispatch import receiver from ssify import flush_ssi_includes @@ -51,6 +52,11 @@ def collection_delete(sender, instance, **kwargs): def book_save(sender, instance, **kwargs): # Books come out anywhere. caches[settings.CACHE_MIDDLEWARE_ALIAS].clear() + # deleting selectively is too much work + try: + caches['template_fragments'].clear() + except ImproperlyConfigured: + pass instance.flush_includes() -- 2.20.1 From 468c2e650c854ffa691afc73aa1f41ce61283cdb Mon Sep 17 00:00:00 2001 From: Jan Szejko Date: Wed, 4 Jul 2018 16:28:35 +0200 Subject: [PATCH 09/16] paypal subscriptions - stub --- requirements/requirements.txt | 6 +- src/paypal/__init__.py | 0 src/paypal/forms.py | 10 ++ src/paypal/migrations/0001_initial.py | 41 +++++++ .../migrations/0002_billingagreement_token.py | 20 +++ src/paypal/migrations/__init__.py | 0 src/paypal/models.py | 20 +++ src/paypal/nvp_soap.py | 61 ++++++++++ src/paypal/rest.py | 114 ++++++++++++++++++ src/paypal/templates/paypal/cancel.html | 6 + src/paypal/templates/paypal/error.html | 5 + src/paypal/templates/paypal/error_page.html | 5 + src/paypal/templates/paypal/form.html | 13 ++ src/paypal/templates/paypal/return.html | 10 ++ src/paypal/urls.py | 12 ++ src/paypal/views.py | 50 ++++++++ src/wolnelektury/settings/__init__.py | 1 + src/wolnelektury/settings/contrib.py | 6 + src/wolnelektury/urls.py | 1 + 19 files changed, 380 insertions(+), 1 deletion(-) create mode 100644 src/paypal/__init__.py create mode 100644 src/paypal/forms.py create mode 100644 src/paypal/migrations/0001_initial.py create mode 100644 src/paypal/migrations/0002_billingagreement_token.py create mode 100644 src/paypal/migrations/__init__.py create mode 100644 src/paypal/models.py create mode 100644 src/paypal/nvp_soap.py create mode 100644 src/paypal/rest.py create mode 100644 src/paypal/templates/paypal/cancel.html create mode 100644 src/paypal/templates/paypal/error.html create mode 100644 src/paypal/templates/paypal/error_page.html create mode 100644 src/paypal/templates/paypal/form.html create mode 100644 src/paypal/templates/paypal/return.html create mode 100644 src/paypal/urls.py create mode 100644 src/paypal/views.py diff --git a/requirements/requirements.txt b/requirements/requirements.txt index 06ea294a6..631f473f0 100644 --- a/requirements/requirements.txt +++ b/requirements/requirements.txt @@ -62,4 +62,8 @@ django-ssify>=0.2.1,<0.3 raven -mailchimp3 \ No newline at end of file +mailchimp3 + +requests + +paypalrestsdk \ No newline at end of file diff --git a/src/paypal/__init__.py b/src/paypal/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/paypal/forms.py b/src/paypal/forms.py new file mode 100644 index 000000000..849810d1a --- /dev/null +++ b/src/paypal/forms.py @@ -0,0 +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 django import forms +from django.utils.translation import ugettext_lazy as _ + + +class PaypalSubscriptionForm(forms.Form): + amount = forms.IntegerField(min_value=10, max_value=30000, initial=20, label=_('amount')) diff --git a/src/paypal/migrations/0001_initial.py b/src/paypal/migrations/0001_initial.py new file mode 100644 index 000000000..879901ae8 --- /dev/null +++ b/src/paypal/migrations/0001_initial.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models +from django.conf import settings + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='BillingAgreement', + fields=[ + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('agreement_id', models.CharField(max_length=32)), + ('active', models.BooleanField(max_length=32)), + ], + ), + migrations.CreateModel( + name='BillingPlan', + fields=[ + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('plan_id', models.CharField(max_length=32)), + ('amount', models.IntegerField(unique=True, db_index=True)), + ], + ), + migrations.AddField( + model_name='billingagreement', + name='plan', + field=models.ForeignKey(to='paypal.BillingPlan'), + ), + migrations.AddField( + model_name='billingagreement', + name='user', + field=models.ForeignKey(to=settings.AUTH_USER_MODEL), + ), + ] diff --git a/src/paypal/migrations/0002_billingagreement_token.py b/src/paypal/migrations/0002_billingagreement_token.py new file mode 100644 index 000000000..7a45e87a0 --- /dev/null +++ b/src/paypal/migrations/0002_billingagreement_token.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('paypal', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='billingagreement', + name='token', + field=models.CharField(default='', max_length=32), + preserve_default=False, + ), + ] diff --git a/src/paypal/migrations/__init__.py b/src/paypal/migrations/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/paypal/models.py b/src/paypal/models.py new file mode 100644 index 000000000..527e80458 --- /dev/null +++ b/src/paypal/models.py @@ -0,0 +1,20 @@ +# -*- 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.contrib.auth import get_user_model +from django.contrib.auth.models import User +from django.db import models + + +class BillingPlan(models.Model): + plan_id = models.CharField(max_length=32) + amount = models.IntegerField(db_index=True, unique=True) + + +class BillingAgreement(models.Model): + agreement_id = models.CharField(max_length=32) + user = models.ForeignKey(User) + plan = models.ForeignKey(BillingPlan) + active = models.BooleanField(max_length=32) + token = models.CharField(max_length=32) diff --git a/src/paypal/nvp_soap.py b/src/paypal/nvp_soap.py new file mode 100644 index 000000000..09f8f62f3 --- /dev/null +++ b/src/paypal/nvp_soap.py @@ -0,0 +1,61 @@ +# -*- 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. +# +# UNUSED +import requests +import urlparse +from django.conf import settings + +DESC = 'Wolne Lektury subscription' + + +def paypal_request(data): + request_data = { + 'USER': settings.PAYPAL['user'], + 'PWD': settings.PAYPAL['password'], + 'SIGNATURE': settings.PAYPAL['signature'], + 'SUBJECT': settings.PAYPAL['email'], + 'VERSION': 93, + } + request_data.update(data) + + response = requests.post(settings.PAYPAL['api-url'], data=request_data) + return dict(urlparse.parse_qsl(response.text)) + + +def set_express_checkout(amount): + response = paypal_request({ + 'METHOD': 'SetExpressCheckout', + 'PAYMENTREQUEST_0_PAYMENTACTION': 'SALE', + 'PAYMENTREQUEST_0_AMT': amount, + 'PAYMENTREQUEST_0_CURRENCYCODE': 'PLN', + 'L_BILLINGTYPE0': 'RecurringPayments', + 'L_BILLINGAGREEMENTDESCRIPTION0': DESC, + 'RETURNURL': settings.PAYPAL['return-url'], + 'CANCELURL': settings.PAYPAL['cancel-url'], + }) + return response.get('TOKEN') + + +def create_profile(token, amount): + response = paypal_request({ + 'METHOD': 'CreateRecurringPaymentsProfile', + 'TOKEN': token, + 'PROFILESTARTDATE': '2011-03-11T00:00:00Z', + 'DESC': DESC, + 'MAXFAILEDPAYMENTS': 3, + 'AUTOBILLAMT': 'AddToNextBilling', + 'BILLINGPERIOD': 'Month', # or 30 Days? + 'BILLINGFREQUENCY': 1, + 'AMT': amount, + 'CURRENCYCODE': 'PLN', + 'L_PAYMENTREQUEST_0_ITEMCATEGORY0': 'Digital', + 'L_PAYMENTREQUEST_0_NAME0': 'Subskrypcja Wolnych Lektur', + 'L_PAYMENTREQUEST_0_AMT0': amount, + 'L_PAYMENTREQUEST_0_QTY0': 1, + }) + return response.get('PROFILEID') + + +# min amount: 10, max amount: 30000 diff --git a/src/paypal/rest.py b/src/paypal/rest.py new file mode 100644 index 000000000..68ac30ff5 --- /dev/null +++ b/src/paypal/rest.py @@ -0,0 +1,114 @@ +# -*- 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 datetime import timedelta + +import paypalrestsdk +import pytz +from django.contrib.sites.models import Site +from django.core.urlresolvers import reverse +from django.utils import timezone +from paypalrestsdk import BillingPlan, BillingAgreement, ResourceNotFound +from django.conf import settings +from .models import BillingPlan as BillingPlanModel + +paypalrestsdk.configure(settings.PAYPAL_CONFIG) + + +class PaypalError(Exception): + pass + + +def absolute_url(url_name): + return "http://%s%s" % (Site.objects.get_current().domain, reverse(url_name)) + + +def create_plan(amount): + billing_plan = BillingPlan({ + "name": "Cykliczna darowizna na Wolne Lektury: %s zł" % amount, + "description": "Cykliczna darowizna na wsparcie Wolnych Lektur", + "merchant_preferences": { + "auto_bill_amount": "yes", + "return_url": absolute_url('paypal_return'), + "cancel_url": absolute_url('paypal_cancel'), + # "initial_fail_amount_action": "continue", + "max_fail_attempts": "3", + }, + "payment_definitions": [ + { + "amount": { + "currency": "PLN", + "value": str(amount), + }, + "cycles": "0", + "frequency": "MONTH", + "frequency_interval": "1", + "name": "Cykliczna darowizna", + "type": "REGULAR", + } + ], + "type": "INFINITE", + }) + + if not billing_plan.create(): + raise PaypalError(billing_plan.error) + if not billing_plan.activate(): + raise PaypalError(billing_plan.error) + plan, created = BillingPlanModel.objects.get_or_create(amount=amount, defaults={'plan_id': billing_plan.id}) + return plan.plan_id + + +def get_link(links, rel): + for link in links: + if link.rel == rel: + return link.href + + +def create_agreement(amount): + try: + plan = BillingPlanModel.objects.get(amount=amount) + except BillingPlanModel.DoesNotExist: + plan_id = create_plan(amount) + else: + plan_id = plan.plan_id + start = (timezone.now() + timedelta(0, 3600*24)).astimezone(pytz.utc).strftime('%Y-%m-%dT%H:%M:%SZ') + billing_agreement = BillingAgreement({ + "name": "Subskrypcja klubu WL", + "description": "Cykliczne wspieranie Wolnych Lektur kwotą %s złotych" % amount, + "start_date": start, + "plan": { + "id": plan_id, + }, + "payer": { + "payment_method": "paypal" + }, + }) + + response = billing_agreement.create() + if response: + return billing_agreement + else: + raise PaypalError(billing_agreement.error) + + +def agreement_approval_url(amount): + agreement = create_agreement(amount) + return get_link(agreement.links, 'approval_url') + + +def get_agreement(agreement_id): + try: + return BillingAgreement.find(agreement_id) + except ResourceNotFound: + return None + + +def check_agreement(agreement_id): + a = get_agreement(agreement_id) + if a: + return a.state == 'Active' + + +def execute_agreement(token): + return BillingAgreement.execute(token) diff --git a/src/paypal/templates/paypal/cancel.html b/src/paypal/templates/paypal/cancel.html new file mode 100644 index 000000000..459979975 --- /dev/null +++ b/src/paypal/templates/paypal/cancel.html @@ -0,0 +1,6 @@ +{% extends "base/base.html" %} +{% load i18n %} + +{% block body %} +

{% trans "Zrezygnowano z płatności :(" %}

+{% endblock %} \ No newline at end of file diff --git a/src/paypal/templates/paypal/error.html b/src/paypal/templates/paypal/error.html new file mode 100644 index 000000000..557284ba8 --- /dev/null +++ b/src/paypal/templates/paypal/error.html @@ -0,0 +1,5 @@ +

{% trans "PayPal Error" %}: {{ error.message }}

+{% for detail in error.details %} +

{{ detail.field }}: {{ detail.issue }}

+{% endfor %} +

{% trans "Learn more" %}

\ No newline at end of file diff --git a/src/paypal/templates/paypal/error_page.html b/src/paypal/templates/paypal/error_page.html new file mode 100644 index 000000000..499ae1d56 --- /dev/null +++ b/src/paypal/templates/paypal/error_page.html @@ -0,0 +1,5 @@ +{% extends "base/base.html" %} + +{% block body %} + {% include "paypal/error.html" %} +{% endblock %} \ No newline at end of file diff --git a/src/paypal/templates/paypal/form.html b/src/paypal/templates/paypal/form.html new file mode 100644 index 000000000..b2e6a0c7d --- /dev/null +++ b/src/paypal/templates/paypal/form.html @@ -0,0 +1,13 @@ +{% extends "base/base.html" %} +{% load i18n %} + +{% block title %}{% trans "Subscription" %}{% endblock %} + +{% block body %} +
+ {% csrf_token %} + {{ form.as_p }} + {# paypal submit button #} + +
+{% endblock %} \ No newline at end of file diff --git a/src/paypal/templates/paypal/return.html b/src/paypal/templates/paypal/return.html new file mode 100644 index 000000000..4e967fb0f --- /dev/null +++ b/src/paypal/templates/paypal/return.html @@ -0,0 +1,10 @@ +{% extends "base/base.html" %} +{% load i18n %} + +{% block body %} + {% if resource.error %} + {% include "paypal/error.html" with error=resource.error %} + {% else %} +

{% trans "Płatność potwierdzona i zlecona do wykonania." %}

+ {% endif %} +{% endblock %} \ No newline at end of file diff --git a/src/paypal/urls.py b/src/paypal/urls.py new file mode 100644 index 000000000..924cedd95 --- /dev/null +++ b/src/paypal/urls.py @@ -0,0 +1,12 @@ +# -*- 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 +from . import views + +urlpatterns = ( + url(r'^form/$', views.paypal_form, name='paypal_form'), + url(r'^return/$', views.paypal_return, name='paypal_return'), + url(r'^cancel/$', views.paypal_cancel, name='paypal_cancel'), +) diff --git a/src/paypal/views.py b/src/paypal/views.py new file mode 100644 index 000000000..a4c04ce08 --- /dev/null +++ b/src/paypal/views.py @@ -0,0 +1,50 @@ +# -*- 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 decimal import Decimal + +from django.contrib.auth.decorators import login_required +from django.http import Http404 +from django.http.response import HttpResponseRedirect +from django.shortcuts import render + +from paypal.forms import PaypalSubscriptionForm +from paypal.rest import execute_agreement, check_agreement, agreement_approval_url, PaypalError +from paypal.models import BillingAgreement as BillingAgreementModel, BillingPlan + + +@login_required +def paypal_form(request): + if request.POST: + form = PaypalSubscriptionForm(data=request.POST) + if form.is_valid(): + amount = form.cleaned_data['amount'] + try: + approval_url = agreement_approval_url(amount) + except PaypalError as e: + return render(request, 'paypal/error_page.html', {'error': e.message}) + return HttpResponseRedirect(approval_url) + else: + form = PaypalSubscriptionForm() + return render(request, 'paypal/form.html', {'form': form}) + + +@login_required +def paypal_return(request): + token = request.GET.get('token') + if not token: + raise Http404 + if not BillingAgreementModel.objects.filter(token=token): + resource = execute_agreement(token) + if resource.id: + amount = int(Decimal(resource.plan.payment_definitions[0].amount['value'])) + plan = BillingPlan.objects.get(amount=amount) + active = check_agreement(resource.id) + BillingAgreementModel.objects.create( + agreement_id=resource.id, user=request.user, plan=plan, active=active, token=token) + return render(request, 'paypal/return.html', {'resource': resource}) + + +def paypal_cancel(request): + return render(request, 'paypal/cancel.html', {}) diff --git a/src/wolnelektury/settings/__init__.py b/src/wolnelektury/settings/__init__.py index ca70e9f6d..54dca9c09 100644 --- a/src/wolnelektury/settings/__init__.py +++ b/src/wolnelektury/settings/__init__.py @@ -60,6 +60,7 @@ INSTALLED_APPS_OUR = [ 'newsletter', 'contact', 'isbn', + 'paypal', ] GETPAID_BACKENDS = ( diff --git a/src/wolnelektury/settings/contrib.py b/src/wolnelektury/settings/contrib.py index 4bb78bcfe..e7047c006 100644 --- a/src/wolnelektury/settings/contrib.py +++ b/src/wolnelektury/settings/contrib.py @@ -19,3 +19,9 @@ GETPAID_ORDER_DESCRIPTION = "{% load funding_tags %}{{ order|sanitize_payment_ti PIWIK_URL = '' PIWIK_SITE_ID = 0 PIWIK_TOKEN = '' + +PAYPAL_CONFIG = { + 'mode': 'sandbox', # sandbox or live + 'client_id': '', + 'client_secret': '', +} diff --git a/src/wolnelektury/urls.py b/src/wolnelektury/urls.py index bf95306d9..0cbaced13 100644 --- a/src/wolnelektury/urls.py +++ b/src/wolnelektury/urls.py @@ -50,6 +50,7 @@ urlpatterns += [ url(r'^newsletter/', include('newsletter.urls')), url(r'^formularz/', include('contact.urls')), url(r'^isbn/', include('isbn.urls')), + url(r'^paypal/', include('paypal.urls')), # Admin panel url(r'^admin/catalogue/book/import$', catalogue.views.import_book, name='import_book'), -- 2.20.1 From 4167e7083d75589e9dbbaa05bfcb43d27b60b42f Mon Sep 17 00:00:00 2001 From: Jan Szejko Date: Thu, 5 Jul 2018 15:26:18 +0200 Subject: [PATCH 10/16] fix overlapping abstract --- .../templates/catalogue/book_short.html | 18 +++++++++--------- .../static/scss/main/book_box.scss | 8 ++++---- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/catalogue/templates/catalogue/book_short.html b/src/catalogue/templates/catalogue/book_short.html index 0361e8d5d..c8dd95f94 100644 --- a/src/catalogue/templates/catalogue/book_short.html +++ b/src/catalogue/templates/catalogue/book_short.html @@ -19,6 +19,15 @@ {% block book-box-body-pre %} {% endblock %} +
+ {% if book.cover_thumb %} + + Cover + + {% endif %} + {% block cover-area-extra %}{% endblock %} +
+
{% for tag in tags.author %} @@ -36,15 +45,6 @@ {% endif %}
-
- {% if book.cover_thumb %} - - Cover - - {% endif %} - {% block cover-area-extra %}{% endblock %} -
-
{% trans "Epoch" %}:  diff --git a/src/wolnelektury/static/scss/main/book_box.scss b/src/wolnelektury/static/scss/main/book_box.scss index ddb4b7218..9e87e3917 100755 --- a/src/wolnelektury/static/scss/main/book_box.scss +++ b/src/wolnelektury/static/scss/main/book_box.scss @@ -15,10 +15,10 @@ @include size(margin-bottom, 5px); @include min-screen($S_BOOK_SHORT_FULL) { - position: absolute; - top: 0; - left: 0; - margin-right: 0; + //position: absolute; + //top: 0; + //left: 0; + //margin-right: 0; } img.cover { -- 2.20.1 From ef93835e5356373ea5190e070cce80d42df616b0 Mon Sep 17 00:00:00 2001 From: Jan Szejko Date: Thu, 5 Jul 2018 15:36:30 +0200 Subject: [PATCH 11/16] hide "(none)" --- src/social/templatetags/social_tags.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/social/templatetags/social_tags.py b/src/social/templatetags/social_tags.py index 4cc621525..ce61985d2 100755 --- a/src/social/templatetags/social_tags.py +++ b/src/social/templatetags/social_tags.py @@ -34,7 +34,7 @@ def render_cite(cite): @ssi_variable(register, patch_response=[ssi_vary_on_cookie]) def book_shelf_tags(request, book_id): if not request.user.is_authenticated(): - return None + return '' book = Book.objects.get(pk=book_id) lks = likes(request.user, book, request) -- 2.20.1 From 13cee1a7148844ffcd026ad0b541a3289fbafefa Mon Sep 17 00:00:00 2001 From: Jan Szejko Date: Thu, 5 Jul 2018 15:40:14 +0200 Subject: [PATCH 12/16] prevent abstract from flowing around cover --- src/wolnelektury/static/scss/main/book_box.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/src/wolnelektury/static/scss/main/book_box.scss b/src/wolnelektury/static/scss/main/book_box.scss index 9e87e3917..6e71fbca5 100755 --- a/src/wolnelektury/static/scss/main/book_box.scss +++ b/src/wolnelektury/static/scss/main/book_box.scss @@ -526,6 +526,7 @@ @include size(margin-top, 18px); overflow: hidden; position: relative; + clear: left; @media screen and (min-width: 62.5em) { @include size(width, 536px); -- 2.20.1 From 1a12630094dd57a463a8a1d4f64744e3b3b245af Mon Sep 17 00:00:00 2001 From: Jan Szejko Date: Fri, 6 Jul 2018 14:00:54 +0200 Subject: [PATCH 13/16] minor changes in paypal --- src/paypal/rest.py | 4 ++-- src/paypal/templates/paypal/form.html | 17 +++++++++++------ src/paypal/views.py | 5 +++-- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/paypal/rest.py b/src/paypal/rest.py index 68ac30ff5..a2e22322f 100644 --- a/src/paypal/rest.py +++ b/src/paypal/rest.py @@ -74,8 +74,8 @@ def create_agreement(amount): plan_id = plan.plan_id start = (timezone.now() + timedelta(0, 3600*24)).astimezone(pytz.utc).strftime('%Y-%m-%dT%H:%M:%SZ') billing_agreement = BillingAgreement({ - "name": "Subskrypcja klubu WL", - "description": "Cykliczne wspieranie Wolnych Lektur kwotą %s złotych" % amount, + "name": u"Subskrypcja klubu WL", + "description": u"Stałe wsparcie Wolnych Lektur kwotą %s złotych" % amount, "start_date": start, "plan": { "id": plan_id, diff --git a/src/paypal/templates/paypal/form.html b/src/paypal/templates/paypal/form.html index b2e6a0c7d..de8382f26 100644 --- a/src/paypal/templates/paypal/form.html +++ b/src/paypal/templates/paypal/form.html @@ -4,10 +4,15 @@ {% block title %}{% trans "Subscription" %}{% endblock %} {% block body %} -
- {% csrf_token %} - {{ form.as_p }} - {# paypal submit button #} - -
+ {# https://www.facebook.com/sharer/sharer.php?u=https%3A//wolnelektury.pl{% url 'paypal_form' %} #} + {% if user.is_authenticated %} +
+ {% csrf_token %} + {{ form.as_p }} + {# paypal submit button #} + +
+ {% else %} + {% trans "You must be logged in to subscribe." %} + {% endif %} {% endblock %} \ No newline at end of file diff --git a/src/paypal/views.py b/src/paypal/views.py index a4c04ce08..81c198559 100644 --- a/src/paypal/views.py +++ b/src/paypal/views.py @@ -6,7 +6,7 @@ from decimal import Decimal from django.contrib.auth.decorators import login_required from django.http import Http404 -from django.http.response import HttpResponseRedirect +from django.http.response import HttpResponseRedirect, HttpResponseForbidden from django.shortcuts import render from paypal.forms import PaypalSubscriptionForm @@ -14,9 +14,10 @@ from paypal.rest import execute_agreement, check_agreement, agreement_approval_u from paypal.models import BillingAgreement as BillingAgreementModel, BillingPlan -@login_required def paypal_form(request): if request.POST: + if not request.user.is_authenticated(): + return HttpResponseForbidden() form = PaypalSubscriptionForm(data=request.POST) if form.is_valid(): amount = form.cleaned_data['amount'] -- 2.20.1 From d7514c43c88858198e1c83b736caab182da3fdd9 Mon Sep 17 00:00:00 2001 From: Jan Szejko Date: Tue, 10 Jul 2018 12:17:10 +0200 Subject: [PATCH 14/16] text on return from paypal --- src/paypal/templates/paypal/return.html | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/paypal/templates/paypal/return.html b/src/paypal/templates/paypal/return.html index 4e967fb0f..7c6d576b0 100644 --- a/src/paypal/templates/paypal/return.html +++ b/src/paypal/templates/paypal/return.html @@ -5,6 +5,9 @@ {% if resource.error %} {% include "paypal/error.html" with error=resource.error %} {% else %} -

{% trans "Płatność potwierdzona i zlecona do wykonania." %}

+

{% trans "Dziękujemy, że jesteś z nami i pomagasz nam rozwijać Wolne Lektury!" %}

+

{% trans "Pamiętaj, że zawsze możesz się z nami skontaktować:" %} wolnelektury@nowoczesnapolska.org.pl

+ +

{% trans "Do przeczytania!" %}
{% trans "Zespół Wolnych Lektur" %}

{% endif %} {% endblock %} \ No newline at end of file -- 2.20.1 From 538526fc82b771cbc149743f129d8b1ae7e3cba2 Mon Sep 17 00:00:00 2001 From: Jan Szejko Date: Tue, 10 Jul 2018 14:54:06 +0200 Subject: [PATCH 15/16] upgrade fnpdjango --- requirements/requirements.txt | 2 +- src/catalogue/fields.py | 4 ++-- src/catalogue/models/bookmedia.py | 6 +++--- src/catalogue/models/tag.py | 4 ++-- src/catalogue/test_utils.py | 4 ++-- src/funding/utils.py | 2 +- src/isbn/forms.py | 4 ++-- src/picture/models.py | 6 +++--- src/sortify.py | 2 +- 9 files changed, 17 insertions(+), 17 deletions(-) diff --git a/requirements/requirements.txt b/requirements/requirements.txt index 631f473f0..20c7c97df 100644 --- a/requirements/requirements.txt +++ b/requirements/requirements.txt @@ -2,7 +2,7 @@ # django Django>=1.8,<1.9 -fnpdjango>=0.1.15,<0.2 +fnpdjango>=0.2.8,<0.3 django-pipeline>=1.6,<1.7 jsmin fnp-django-pagination diff --git a/src/catalogue/fields.py b/src/catalogue/fields.py index 92e8de48c..378377ced 100644 --- a/src/catalogue/fields.py +++ b/src/catalogue/fields.py @@ -163,7 +163,7 @@ class BuildMobi(BuildEbook): class BuildHtml(BuildEbook): def build(self, fieldfile): from django.core.files.base import ContentFile - from fnpdjango.utils.text.slughifi import slughifi + from slugify import slugify from sortify import sortify from librarian import html from catalogue.models import Fragment, Tag @@ -204,7 +204,7 @@ class BuildHtml(BuildEbook): if lang == settings.LANGUAGE_CODE: # Allow creating themes if book in default language. tag, created = Tag.objects.get_or_create( - slug=slughifi(theme_name), + slug=slugify(theme_name), category='theme') if created: tag.name = theme_name diff --git a/src/catalogue/models/bookmedia.py b/src/catalogue/models/bookmedia.py index 6b650806c..377dbc43c 100644 --- a/src/catalogue/models/bookmedia.py +++ b/src/catalogue/models/bookmedia.py @@ -8,14 +8,14 @@ from collections import namedtuple from django.db import models from django.utils.translation import ugettext_lazy as _ import jsonfield -from fnpdjango.utils.text.slughifi import slughifi +from slugify import slugify from mutagen import MutagenError from catalogue.fields import OverwriteStorage def _file_upload_to(i, _n): - return 'book/%(ext)s/%(name)s.%(ext)s' % {'ext': i.ext(), 'name': slughifi(i.name)} + return 'book/%(ext)s/%(name)s.%(ext)s' % {'ext': i.ext(), 'name': slugify(i.name)} class BookMedia(models.Model): @@ -66,7 +66,7 @@ class BookMedia(models.Model): old = None else: # if name changed, change the file name, too - if slughifi(self.name) != slughifi(old.name): + if slugify(self.name) != slugify(old.name): self.file.save(None, ExistingFile(self.file.path), save=False) super(BookMedia, self).save(*args, **kwargs) diff --git a/src/catalogue/models/tag.py b/src/catalogue/models/tag.py index 830f29f69..7e15636da 100644 --- a/src/catalogue/models/tag.py +++ b/src/catalogue/models/tag.py @@ -230,7 +230,7 @@ class Tag(TagBase): @staticmethod def tags_from_info(info): - from fnpdjango.utils.text.slughifi import slughifi + from slugify import slugify from sortify import sortify meta_tags = [] categories = (('kinds', 'kind'), ('genres', 'genre'), ('authors', 'author'), ('epochs', 'epoch')) @@ -251,7 +251,7 @@ class Tag(TagBase): tag_name = tag_name.readable() if lang == settings.LANGUAGE_CODE: # Allow creating new tag, if it's in default language. - tag, created = Tag.objects.get_or_create(slug=slughifi(tag_name), category=category) + tag, created = Tag.objects.get_or_create(slug=slugify(tag_name), category=category) if created: tag_name = unicode(tag_name) tag.name = tag_name diff --git a/src/catalogue/test_utils.py b/src/catalogue/test_utils.py index 497b99545..18c567406 100644 --- a/src/catalogue/test_utils.py +++ b/src/catalogue/test_utils.py @@ -7,7 +7,7 @@ import tempfile from traceback import extract_stack from django.test import TestCase from django.test.utils import override_settings -from fnpdjango.utils.text.slughifi import slughifi +from slugify import slugify from librarian import WLURI from django.conf import settings @@ -72,7 +72,7 @@ class BookInfoStub(object): def info_args(title, language=None): """ generate some keywords for comfortable BookInfoCreation """ - slug = unicode(slughifi(title)) + slug = unicode(slugify(title)) if language is None: language = u'pol' return { diff --git a/src/funding/utils.py b/src/funding/utils.py index 77d8981f5..580833699 100644 --- a/src/funding/utils.py +++ b/src/funding/utils.py @@ -4,7 +4,7 @@ # import re import string -from fnpdjango.utils.text.slughifi import char_map +from fnpdjango.utils.text import char_map # PayU chokes on non-Polish diacritics. # Punctuation is handled correctly and escaped as needed, diff --git a/src/isbn/forms.py b/src/isbn/forms.py index 558cd9aa0..2acc34a8e 100644 --- a/src/isbn/forms.py +++ b/src/isbn/forms.py @@ -4,7 +4,7 @@ from urllib2 import urlopen from django import forms from django.utils.translation import ugettext_lazy as _ -from fnpdjango.utils.text.slughifi import slughifi +from slugify import slugify from isbn.management.commands.import_onix import UNKNOWN from isbn.models import ONIXRecord, ISBNPool @@ -86,7 +86,7 @@ class FNPISBNForm(forms.Form): return {'role': 'A01', 'name': output_name} def slug(self): - return slughifi('fnp %s %s' % (self.cleaned_data['authors'], self.cleaned_data['title'])) + return slugify('fnp %s %s' % (self.cleaned_data['authors'], self.cleaned_data['title'])) def save(self): data = { diff --git a/src/picture/models.py b/src/picture/models.py index 455ed10f4..643149a1a 100644 --- a/src/picture/models.py +++ b/src/picture/models.py @@ -10,7 +10,7 @@ from django.conf import settings from django.contrib.contenttypes.fields import GenericRelation from django.core.files.storage import FileSystemStorage from django.utils.datastructures import SortedDict -from fnpdjango.utils.text.slughifi import slughifi +from slugify import slugify from ssify import flush_ssi_includes from catalogue.models.tag import prefetched_relations @@ -224,7 +224,7 @@ class Picture(models.Model): # str.capitalize() is wrong, because it also lowers letters objname = objname[0].upper() + objname[1:] tag, created = catalogue.models.Tag.objects.get_or_create( - slug=slughifi(objname), category='thing') + slug=slugify(objname), category='thing') if created: tag.name = objname setattr(tag, 'name_%s' % lang, tag.name) @@ -249,7 +249,7 @@ class Picture(models.Model): for motifs in part['themes']: for motif in motifs.split(','): tag, created = catalogue.models.Tag.objects.get_or_create( - slug=slughifi(motif), category='theme') + slug=slugify(motif), category='theme') if created: tag.name = motif tag.sort_key = sortify(tag.name) diff --git a/src/sortify.py b/src/sortify.py index f1e8bcb8b..61cb9d892 100644 --- a/src/sortify.py +++ b/src/sortify.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- import re -from fnpdjango.utils.text.slughifi import char_map +from fnpdjango.utils.text import char_map # Specifies diacritics order. -- 2.20.1 From c6a911e651e4ddb8d817b458f94dc62e10dab419 Mon Sep 17 00:00:00 2001 From: Jan Szejko Date: Tue, 10 Jul 2018 16:32:38 +0200 Subject: [PATCH 16/16] add migdal blog --- requirements/requirements.txt | 7 ++++++- src/wolnelektury/settings/__init__.py | 4 ++++ src/wolnelektury/settings/contrib.py | 6 ++++++ src/wolnelektury/settings/static.py | 2 +- 4 files changed, 17 insertions(+), 2 deletions(-) diff --git a/requirements/requirements.txt b/requirements/requirements.txt index 20c7c97df..2913380c6 100644 --- a/requirements/requirements.txt +++ b/requirements/requirements.txt @@ -66,4 +66,9 @@ mailchimp3 requests -paypalrestsdk \ No newline at end of file +paypalrestsdk + +django-haystack<2.8.0 +django-migdal>=0.7.6 + +python-slugify \ No newline at end of file diff --git a/src/wolnelektury/settings/__init__.py b/src/wolnelektury/settings/__init__.py index 54dca9c09..b57ca5545 100644 --- a/src/wolnelektury/settings/__init__.py +++ b/src/wolnelektury/settings/__init__.py @@ -93,6 +93,10 @@ INSTALLED_APPS_CONTRIB = [ 'django_extensions', 'raven.contrib.django.raven_compat', + 'migdal', + 'django_comments', + 'django_comments_xtd', + # allauth stuff 'uni_form', 'allauth', diff --git a/src/wolnelektury/settings/contrib.py b/src/wolnelektury/settings/contrib.py index e7047c006..70eca9e0c 100644 --- a/src/wolnelektury/settings/contrib.py +++ b/src/wolnelektury/settings/contrib.py @@ -2,6 +2,8 @@ # This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later. # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. # +from fnpdjango.utils.text.textilepl import textile_pl + HONEYPOT_FIELD_NAME = 'miut' PAGINATION_INVALID_PAGE_RAISES_404 = True THUMBNAIL_QUALITY = 95 @@ -25,3 +27,7 @@ PAYPAL_CONFIG = { 'client_id': '', 'client_secret': '', } + +MARKUP_FIELD_TYPES = ( + ('textile_pl', textile_pl), +) diff --git a/src/wolnelektury/settings/static.py b/src/wolnelektury/settings/static.py index 8be119efb..3c1997d59 100644 --- a/src/wolnelektury/settings/static.py +++ b/src/wolnelektury/settings/static.py @@ -186,7 +186,7 @@ PIPELINE = { ) } -STATICFILES_STORAGE = 'fnpdjango.utils.pipeline_storage.GzipPipelineCachedStorage' +STATICFILES_STORAGE = 'fnpdjango.pipeline_storage.GzipPipelineCachedStorage' # PIPELINE_PYSCSS_BINARY = '/usr/bin/env pyscss' # PIPELINE_PYSCSS_ARGUMENTS = '' -- 2.20.1