From: Radek Czajka Date: Mon, 5 Sep 2022 23:21:37 +0000 (+0200) Subject: New layout pages. X-Git-Url: https://git.mdrn.pl/wolnelektury.git/commitdiff_plain/fd1a9915dd65789cccf27041a1f01cd4077273fc?ds=sidebyside New layout pages. --- diff --git a/src/catalogue/templates/catalogue/2022/book_box.html b/src/catalogue/templates/catalogue/2022/book_box.html index 83b415744..55f851de9 100644 --- a/src/catalogue/templates/catalogue/2022/book_box.html +++ b/src/catalogue/templates/catalogue/2022/book_box.html @@ -1,12 +1,12 @@ {% load sorl_thumbnail %}
-
+
{% if book.is_picture %} {% if book.image_file %} - {% thumbnail book.image_file "170x170" as im %} - + {% thumbnail book.image_file "170x240" crop="center" as im %} + {% endthumbnail %} {% endif %} {% else %} diff --git a/src/catalogue/templates/catalogue/2022/theme_detail.html b/src/catalogue/templates/catalogue/2022/theme_detail.html new file mode 100644 index 000000000..622b6b521 --- /dev/null +++ b/src/catalogue/templates/catalogue/2022/theme_detail.html @@ -0,0 +1,98 @@ +{% extends '2022/base_real.html' %} +{% load pagination_tags %} + +{% block content %} +
+ +
+ +
+ +
+
+

Motyw: {{ tags.0.name }}

+
+
+ + + + {% autopaginate object_list 10 %} + +
+
+
+
+ + {% for fragment in object_list %} + + {% endfor %} + {% paginate using '2022/paginate.html' %} +
+
+
+
+

Motyw: {{ tags.0.name }}

+ {{ tags.0.description|safe }} + +
+
+
+
+
+ +{% endblock %} diff --git a/src/catalogue/tests/test_tags.py b/src/catalogue/tests/test_tags.py index 92845e228..a7066186c 100644 --- a/src/catalogue/tests/test_tags.py +++ b/src/catalogue/tests/test_tags.py @@ -291,4 +291,4 @@ class BookTagsTests(WLTestCase): self.assertEqual([t.slug for t in book.tags.filter(category='kind')], ['kind']) self.assertEqual([(tag.name, tag.count) for tag in related_themes], - [('ChildTheme', 1), ('ParentTheme', 1), ('Theme', 2)]) + [('Theme', 2), ('ChildTheme', 1), ('ParentTheme', 1)]) diff --git a/src/catalogue/views.py b/src/catalogue/views.py index 6edbbd6a4..c63aa7a9e 100644 --- a/src/catalogue/views.py +++ b/src/catalogue/views.py @@ -144,9 +144,12 @@ def object_list(request, objects, fragments=None, related_tags=None, tags=None, result.update(extra) is_author = len(tags) == 1 and tags[0].category == 'author' + is_theme = len(tags) == 1 and tags[0].category == 'theme' new_layout = request.EXPERIMENTS['layout'] if is_author and new_layout.value: template = 'catalogue/2022/author_detail.html' + elif is_theme and new_layout.value: + template = 'catalogue/2022/theme_detail.html' else: template = 'catalogue/tagged_object_list.html' diff --git a/src/club/admin.py b/src/club/admin.py index d966ad64d..ce74ec61b 100644 --- a/src/club/admin.py +++ b/src/club/admin.py @@ -14,7 +14,20 @@ from wolnelektury.utils import YesNoFilter from . import models -admin.site.register(models.Club) +class SingleAmountInline(admin.TabularInline): + model = models.SingleAmount + + +class MonthlyAmountInline(admin.TabularInline): + model = models.MonthlyAmount + + +@admin.register(models.Club) +class ClubAdmin(admin.ModelAdmin): + inlines = [ + SingleAmountInline, + MonthlyAmountInline + ] class PayUOrderInline(admin.TabularInline): diff --git a/src/club/forms.py b/src/club/forms.py index 3a844dab4..f91985539 100644 --- a/src/club/forms.py +++ b/src/club/forms.py @@ -115,3 +115,84 @@ Twoje dane osobowe nie będą profilowane, ani przesyłane do państw trzecich i class PayUCardTokenForm(CardTokenForm): def get_queryset(self, view): return view.get_schedule().payucardtoken_set + + + +class DonationStep1Form(forms.ModelForm): + switch = forms.CharField() + single_amount = forms.IntegerField(required=False) + monthly_amount = forms.IntegerField(required=False) + single_amount_selected = forms.IntegerField(required=False) + monthly_amount_selected = forms.IntegerField(required=False) + custom_amount = forms.IntegerField(required=False) + + amount = forms.IntegerField(required=False) # hidden + + class Meta: + model = models.Schedule + fields = [ + 'amount', + 'monthly' + ] + + + def clean(self): + state = {} + state['monthly'] = self.cleaned_data['switch'] == 'monthly' + which = 'monthly' if state['monthly'] else 'single' + state['amount'] = \ + self.cleaned_data[f'{which}_amount'] or \ + self.cleaned_data['custom_amount'] or \ + self.cleaned_data[f'{monthly}_amount_selected'] + + return state + + + +class DonationStep2Form(forms.ModelForm, NewsletterForm): + class Meta: + model = models.Schedule + fields = [ + 'first_name', 'last_name', + 'email', 'phone', + 'postal', + 'postal_code', 'postal_town', 'postal_country', + ] + widgets = { + 'amount': forms.HiddenInput, + 'monthly': forms.HiddenInput, + } + + def __init__(self, referer=None, **kwargs): + self.referer = referer + super().__init__(**kwargs) + + self.fields['first_name'].required = True + self.fields['last_name'].required = True + self.fields['phone'].required = True + + self.consent = [] + for c in models.Consent.objects.filter(active=True).order_by('order'): + key = f'consent{c.id}' + self.fields[key] = forms.BooleanField( + label=c.text, + required=c.required + ) + self.consent.append(( + c, key, (lambda k: lambda: self[k])(key) + )) + + + + def save(self, *args, **kwargs): + NewsletterForm.save(self, *args, **kwargs) + self.instance.source = self.referer or '' + instance = super().save(*args, **kwargs) + + consents = [] + for consent, key, consent_field in self.consent: + if self.cleaned_data[key]: + instance.consent.add(consent) + + return instance + diff --git a/src/club/migrations/0040_amount.py b/src/club/migrations/0040_amount.py new file mode 100644 index 000000000..1a1215508 --- /dev/null +++ b/src/club/migrations/0040_amount.py @@ -0,0 +1,38 @@ +# Generated by Django 2.2.27 on 2022-08-26 12:39 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('club', '0039_auto_20220421_0109'), + ] + + operations = [ + migrations.CreateModel( + name='SingleAmount', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('amount', models.IntegerField()), + ('description', models.TextField(blank=True)), + ('club', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='club.Club')), + ], + options={ + 'ordering': ['amount'], + }, + ), + migrations.CreateModel( + name='MonthlyAmount', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('amount', models.IntegerField()), + ('description', models.TextField(blank=True)), + ('club', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='club.Club')), + ], + options={ + 'ordering': ['amount'], + }, + ), + ] diff --git a/src/club/migrations/0041_move_amounts.py b/src/club/migrations/0041_move_amounts.py new file mode 100644 index 000000000..d920fd0d3 --- /dev/null +++ b/src/club/migrations/0041_move_amounts.py @@ -0,0 +1,40 @@ +# Generated by Django 2.2.27 on 2022-08-26 12:39 + +from django.db import migrations + + +def move_amounts(apps, schema_editor): + Club = apps.get_model('club', 'Club') + for club in Club.objects.all(): + for amount in [int(x) for x in club.single_amounts.split(',')]: + club.singleamount_set.create(amount=amount) + for amount in [int(x) for x in club.monthly_amounts.split(',')]: + club.monthlyamount_set.create(amount=amount) + + +def move_amounts_back(apps, schema_editor): + Club = apps.get_model('club', 'Club') + for club in Club.objects.all(): + club.single_amounts = ','.join( + str(x) for x in + club.singleamount_set.order_by('amount') + ) + club.monthly_amounts = ','.join( + str(x) for x in + club.monthlyamount_set.order_by('amount') + ) + club.save() + + +class Migration(migrations.Migration): + + dependencies = [ + ('club', '0040_amount'), + ] + + operations = [ + migrations.RunPython( + move_amounts, + move_amounts_back + ) + ] diff --git a/src/club/migrations/0042_auto_20220826_1458.py b/src/club/migrations/0042_auto_20220826_1458.py new file mode 100644 index 000000000..187e5ebfc --- /dev/null +++ b/src/club/migrations/0042_auto_20220826_1458.py @@ -0,0 +1,21 @@ +# Generated by Django 2.2.27 on 2022-08-26 12:58 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('club', '0041_move_amounts'), + ] + + operations = [ + migrations.RemoveField( + model_name='club', + name='monthly_amounts', + ), + migrations.RemoveField( + model_name='club', + name='single_amounts', + ), + ] diff --git a/src/club/models.py b/src/club/models.py index 4f3a8f632..acf131b12 100644 --- a/src/club/models.py +++ b/src/club/models.py @@ -26,9 +26,7 @@ from . import utils class Club(models.Model): min_amount = models.IntegerField(_('minimum amount')) min_for_year = models.IntegerField(_('minimum amount for year')) - single_amounts = models.CharField(_('proposed amounts for single payment'), max_length=255) default_single_amount = models.IntegerField(_('default single amount')) - monthly_amounts = models.CharField(_('proposed amounts for monthly payments'), max_length=255) default_monthly_amount = models.IntegerField(_('default monthly amount')) class Meta: @@ -37,12 +35,23 @@ class Club(models.Model): def __str__(self): return 'Klub' - - def proposed_single_amounts(self): - return [int(x) for x in self.single_amounts.split(',')] - def proposed_monthly_amounts(self): - return [int(x) for x in self.monthly_amounts.split(',')] + +class SingleAmount(models.Model): + club = models.ForeignKey(Club, models.CASCADE) + amount = models.IntegerField() + description = models.TextField(blank=True) + + class Meta: + ordering = ['amount'] + +class MonthlyAmount(models.Model): + club = models.ForeignKey(Club, models.CASCADE) + amount = models.IntegerField() + description = models.TextField(blank=True) + + class Meta: + ordering = ['amount'] class Consent(models.Model): @@ -116,6 +125,13 @@ class Schedule(models.Model): def get_payment_method(self): return [m for m in methods if m.slug == self.method][0] + def get_payment_methods(self): + for method in methods: + if (self.monthly or self.yearly) and method.is_recurring: + yield method + elif not (self.monthly or self.yearly) and method.is_onetime: + yield method + def is_expired(self): return self.expires_at is not None and self.expires_at <= now() diff --git a/src/club/payment_methods.py b/src/club/payment_methods.py index e74ae83ca..9fddff3ff 100644 --- a/src/club/payment_methods.py +++ b/src/club/payment_methods.py @@ -1,10 +1,14 @@ # This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later. # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. # +from hashlib import sha256 from django.conf import settings from django.urls import reverse from django.utils.timezone import now +from django.utils.translation import get_language from paypal.rest import agreement_approval_url +from django.template.loader import render_to_string +from .payu import POSS class PaymentMethod(object): @@ -28,6 +32,14 @@ class PayU(PaymentMethod): def __init__(self, pos_id): self.pos_id = pos_id + def invite_widget(self, schedule): + return render_to_string( + 'club/payment/payu_invite.html', + { + 'schedule': schedule, + } + ) + def initiate(self, request, schedule): # Create Order at once. from .models import PayUOrder @@ -54,6 +66,36 @@ class PayURe(PaymentMethod): def initiate(self, request, schedule): return reverse('club_payu_rec_payment', args=[schedule.key]) + def invite_widget(self, schedule): + from . import forms + pos = POSS[self.pos_id] + widget_args = { + 'merchant-pos-id': pos.pos_id, + 'shop-name': "SHOW NAME", + 'total-amount': str(int(schedule.amount * 100)), + 'currency-code': pos.currency_code, + 'customer-language': get_language(), # filter to pos.languages + 'customer-email': schedule.email, + 'store-card': 'true', + 'recurring-payment': 'true', + } + widget_sig = sha256( + ( + "".join(v for (k, v) in sorted(widget_args.items())) + + pos.secondary_key + ).encode('utf-8') + ).hexdigest() + + return render_to_string( + 'payu/rec_widget.html', + { + 'form': forms.PayUCardTokenForm(), + 'pos': POSS[self.pos_id], + 'widget_args': widget_args, + 'widget_sig': widget_sig, + } + ) + def pay(self, request, schedule): # Create order, put it and see what happens next. from .models import PayUOrder @@ -104,6 +146,14 @@ class PayPal(PaymentMethod): is_recurring = True is_onetime = False + def invite_widget(self, schedule): + return render_to_string( + 'club/payment/paypal_invite.html', + { + 'schedule': schedule, + } + ) + def initiate(self, request, schedule): app = request.GET.get('app') return agreement_approval_url(schedule.amount, schedule.key, app=app) diff --git a/src/club/static/club/payu/banki.png b/src/club/static/club/payu/banki.png new file mode 100644 index 000000000..528ce8b12 Binary files /dev/null and b/src/club/static/club/payu/banki.png differ diff --git a/src/club/templates/club/2022/donation_step1.html b/src/club/templates/club/2022/donation_step1.html new file mode 100644 index 000000000..057ab4199 --- /dev/null +++ b/src/club/templates/club/2022/donation_step1.html @@ -0,0 +1,139 @@ +{% extends 'club/2022/donation_step_base.html' %} +{% load club %} +{% load static %} + + +{% block donation-step-content %} +
+ {% csrf_token %} + {{ form.errors }} + + +
+ +
+ + + +
+
+ +
+ {% for amount in club.singleamount_set.all %} +
+
+

{{ amount.amount }} zł

+
+ +
+
+ {% if amount.description %} +
+

{{ amount.description|safe }}

+
+ {% endif %} +
+ {% endfor %} + + +
+ + +
+ {% for amount in club.monthlyamount_set.all %} +
+

{{ amount.amount }} zł /mies.

+
+

{{ amount.description|safe }}

+ +
+
+ {% endfor %} + +
+ +
+
+ + + {{ form.custom_amount }} +
+ +
+
+ + Bezpieczne płatności zapewniają +
+
+
+
+

Dane do przelewu tradycyjnego:

+
+
+
nazwa odbiorcy
+

Fundacja Nowoczesna Polska

+ + +
+
+
adres odbiorcy
+

ul. Marszałkowska 84/92 lok. 125, 00-514 Warszawa

+ + +
+
+
numer konta
+

75 1090 2851 0000 0001 4324 3317

+ + +
+
+
tytuł przelewu
+

Darowizna na Wolne Lektury + twoja nazwa użytkownika lub e-mail

+ + +
+
+
wpłaty w EUR
+

PL88 1090 2851 0000 0001 4324 3374

+ + +
+
+
Wpłaty w USD
+

PL82 1090 2851 0000 0001 4324 3385

+ + +
+
+
SWIFT
+

WBKPPLPP

+ + +
+
+
+
+
+
+ + +{% endblock %} diff --git a/src/club/templates/club/2022/donation_step2.html b/src/club/templates/club/2022/donation_step2.html new file mode 100644 index 000000000..cd7a9f4be --- /dev/null +++ b/src/club/templates/club/2022/donation_step2.html @@ -0,0 +1,109 @@ +{% extends 'club/2022/donation_step_base.html' %} + +{% load static %} + + +{% block donation-jumbo-image %}{% static '2022/images/checkout-img-2.jpg' %}{% endblock %} + + +{% block donation-step-content %} + +
+
+
+

+ {{ schedule.amount|floatformat }} zł + {% if schedule.monthly %} + /mies. + {% endif %} +

+ +
+
+
+ +
+ {% csrf_token %} + {{ form.errors }} + {{ form.amount }} + {{ form.monthly }} +
+
+
+ + {{ form.first_name }} + {{ form.first_name.errors }} +
+
+ + {{ form.last_name }} + {{ form.last_name.errors }} +
+
+
+
+ + {{ form.email }} + {{ form.email.errors }} +
+
+ + {{ form.phone }} + {{ form.phone.errors }} +
+
+
+
+ + {{ form.postal }} + {{ form.postal.errors }} +
+
+
+
+ + {{ form.postal_code }} + {{ form.postal_code.errors }} +
+
+ + {{ form.postal_town }} + {{ form.postal_town.errors }} +
+
+
+
+ + {{ form.postal_country }} + {{ form.postal_country.errors }} +
+
+
+ {% for consent, key, field in form.consent %} + {{ field.errors }} +
+ {{ field }} + +
+ {% endfor %} +
+ {{ form.agree_newsletter }} + +
+
+
+ Powrót +
+ +
+
+
+
+
+
+ Bezpieczne płatności zapewniają +{% endblock %} diff --git a/src/club/templates/club/2022/donation_step3.html b/src/club/templates/club/2022/donation_step3.html new file mode 100644 index 000000000..4da3456df --- /dev/null +++ b/src/club/templates/club/2022/donation_step3.html @@ -0,0 +1,103 @@ +{% extends 'club/2022/donation_step_base.html' %} + +{% load static %} +{% load club %} + + +{% block donation-jumbo-image %}{% static '2022/images/checkout-img-4.jpg' %}{% endblock %} + + +{% block donation-step-content %} +
+
+
+

+ {{ schedule.amount|floatformat }} zł + {% if schedule.monthly %} + /mies. + {% endif %}

+ +
+
+
+
+
+ +
+ {% for method in schedule.get_payment_methods %} + {% invite_payment method schedule %} + {% endfor %} +
+
+
+

Możesz też ustawić stały przelew na konto:

+
+
+
nazwa odbiorcy
+

Fundacja Nowoczesna Polska

+ + +
+
+
adres odbiorcy
+

ul. Marszałkowska 84/92 lok. 125, 00-514 Warszawa

+ + +
+
+
numer konta
+

75 1090 2851 0000 0001 4324 3317

+ + +
+
+
tytuł przelewu
+

Darowizna na Wolne Lektury + twoja nazwa użytkownika lub e-mail

+ + +
+
+
wpłaty w EUR
+

PL88 1090 2851 0000 0001 4324 3374

+ + +
+
+
Wpłaty w USD
+

PL82 1090 2851 0000 0001 4324 3385

+ + +
+
+
SWIFT
+

WBKPPLPP

+ + +
+
+
+
+ Powrót + +
+
+
+
+ Bezpieczne płatności zapewniają +{% endblock %} diff --git a/src/club/templates/club/2022/donation_step4.html b/src/club/templates/club/2022/donation_step4.html new file mode 100644 index 000000000..f257111c1 --- /dev/null +++ b/src/club/templates/club/2022/donation_step4.html @@ -0,0 +1,15 @@ +{% extends 'club/2022/donation_step_base.html' %} + + +{% block donation-step-content %} +
+
+

Dziękujemy za wpłatę!

+

+ Zajrzyj teraz do e-maila. Tam znajdziesz link, + który powiąże płatność z Twoim kontem użytkownika Wolnych Lektur. +

+ Wracam do lektur +
+
+{% endblock %} diff --git a/src/club/templates/club/2022/donation_step_base.html b/src/club/templates/club/2022/donation_step_base.html new file mode 100644 index 000000000..976b13c0c --- /dev/null +++ b/src/club/templates/club/2022/donation_step_base.html @@ -0,0 +1,130 @@ +{% extends '2022/base.html' %} +{% load club %} +{% load static %} + + +{% block content %} + + + +
+
+
+ +
+ +
+ +
+
+ Wspieraj Wolne Lektury +
+

Wspieraj Wolne Lektury

+

Dziękujemy, że chcesz razem z nami uwalniać książki!

+

Wspieraj Wolne Lektury stałą wpłatą – nawet niewielka ma wielką moc! Możesz też wesprzeć Wolne Lektury jednorazowo.

+
+
+
+ + {% if view.step > 1 and view.step != 4 %} + + {% endif %} +
+ 1 +

Rodzaj wsparcia

+
+ {% if view.step > 1 and view.step != 4 %} +
+ {% endif %} + + {% if view.step != 2 and schedule and view.step != 4 %} + + {% endif %} +
+ 2 +

Dane

+
+ {% if view.step != 2 and schedule and view.step != 4 %} +
+ {% endif %} + + {% if view.step != 3 and schedule.email and view.step != 4 %} + + {% endif %} +
+ 3 +

Forma płatności

+
+ {% if view.step != 3 and schedule.email and view.step != 4 %} +
+ {% endif %} + +
+ 4 +

Gotowe

+
+
+ + + {% block donation-step-content %}{% endblock %} + +
+ + +
+ + + +{% endblock %} diff --git a/src/club/templates/club/payment/paypal_invite.html b/src/club/templates/club/payment/paypal_invite.html new file mode 100644 index 000000000..6a29097ac --- /dev/null +++ b/src/club/templates/club/payment/paypal_invite.html @@ -0,0 +1,7 @@ +{% load static %} +

Wolisz wpłacić przez PayPal?

+ +
+ PayPal +
+
diff --git a/src/club/templates/club/payment/payu_invite.html b/src/club/templates/club/payment/payu_invite.html new file mode 100644 index 000000000..8c16c51a2 --- /dev/null +++ b/src/club/templates/club/payment/payu_invite.html @@ -0,0 +1,5 @@ +{% load static %} + +

Przejdź do płatności →

+ +
diff --git a/src/club/templates/club/payment_form.html b/src/club/templates/club/payment_form.html index 58d564c12..a5b5c068d 100644 --- a/src/club/templates/club/payment_form.html +++ b/src/club/templates/club/payment_form.html @@ -28,8 +28,8 @@

2. {% trans "Choose the amount" %}