From: Radek Czajka Date: Fri, 12 Dec 2025 13:31:37 +0000 (+0100) Subject: Merge branch 'master' into appdev X-Git-Url: https://git.mdrn.pl/wolnelektury.git/commitdiff_plain/d2a9ebf1eae1ee5fa8a09a7dfea76995274f7716?hp=d08ac1730868531e81bf918e0362935e988428ca Merge branch 'master' into appdev --- diff --git a/src/annoy/admin.py b/src/annoy/admin.py index ab7be6aca..ed651b2c5 100644 --- a/src/annoy/admin.py +++ b/src/annoy/admin.py @@ -5,6 +5,9 @@ from modeltranslation.admin import TranslationAdmin from . import models +admin.site.register(models.Campaign) + + class BannerAdmin(TranslationAdmin): list_display = [ 'place', 'text', diff --git a/src/annoy/migrations/0019_campaign_banner_books_alter_banner_place_and_more.py b/src/annoy/migrations/0019_campaign_banner_books_alter_banner_place_and_more.py new file mode 100644 index 000000000..51e33392f --- /dev/null +++ b/src/annoy/migrations/0019_campaign_banner_books_alter_banner_place_and_more.py @@ -0,0 +1,38 @@ +# Generated by Django 4.0.8 on 2025-11-25 15:13 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('catalogue', '0051_book_has_audio'), + ('annoy', '0018_alter_banner_style'), + ] + + operations = [ + migrations.CreateModel( + name='Campaign', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(help_text='Dla zespołu', max_length=255)), + ('image', models.FileField(blank=True, upload_to='annoy/banners/', verbose_name='obraz')), + ], + ), + migrations.AddField( + model_name='banner', + name='books', + field=models.ManyToManyField(blank=True, to='catalogue.book'), + ), + migrations.AlterField( + model_name='banner', + name='place', + field=models.SlugField(choices=[('top', 'U góry wszystkich stron'), ('book-page', 'Strona książki'), ('book-page-center', 'Strona książki, środek'), ('book-text-intermission', 'Przerwa w treści książki'), ('book-fragment-list', 'Obok listy fragmentów książki'), ('blackout', 'Blackout'), ('crisis', 'Kryzysowa')], verbose_name='miejsce'), + ), + migrations.AddField( + model_name='banner', + name='campaign', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='annoy.campaign'), + ), + ] diff --git a/src/annoy/models.py b/src/annoy/models.py index 0d887920d..6af1cc045 100644 --- a/src/annoy/models.py +++ b/src/annoy/models.py @@ -8,8 +8,18 @@ from django.utils.timezone import now from .places import PLACES, PLACE_CHOICES, STYLES +class Campaign(models.Model): + name = models.CharField(max_length=255, help_text='Dla zespołu') + image = models.FileField('obraz', upload_to='annoy/banners/', blank=True) + + def __str__(self): + return self.name + + class Banner(models.Model): place = models.SlugField('miejsce', choices=PLACE_CHOICES) + campaign = models.ForeignKey(Campaign, models.PROTECT, null=True, blank=True) + style = models.CharField( 'styl', max_length=255, blank=True, choices=STYLES, @@ -32,6 +42,8 @@ class Banner(models.Model): help_text='Bannery z wyższym priorytetem mają pierwszeństwo.') since = models.DateTimeField('od', null=True, blank=True) until = models.DateTimeField('do', null=True, blank=True) + books = models.ManyToManyField('catalogue.Book', blank=True) + target = models.IntegerField('cel', null=True, blank=True) progress = models.IntegerField('postęp', null=True, blank=True) show_members = models.BooleanField('widoczny dla członków klubu', default=False) @@ -49,8 +61,20 @@ class Banner(models.Model): def get_text(self): return Template(self.text).render(Context()) + def get_image(self): + if self.campaign and self.campaign.image: + return self.campaign.image + else: + return self.image + + def is_external(self): + return (self.url and + not self.url.startswith('/') and + not self.url.startswith('https://wolnelektury.pl/') + ) + @classmethod - def choice(cls, place, request, exemptions=True): + def choice(cls, place, request, exemptions=True, book=None): Membership = apps.get_model('club', 'Membership') if exemptions and hasattr(request, 'annoy_banner_exempt'): @@ -68,6 +92,12 @@ class Banner(models.Model): until__lt=n ).order_by('-priority', '?') + if book is None: + banners = banners.filter(books=None) + else: + banners = banners.filter(models.Q(books=None) | models.Q(books=book)) + + if not request.user.is_authenticated: banners = banners.filter(only_authenticated=False) diff --git a/src/annoy/places.py b/src/annoy/places.py index 8fa767bba..0de0483b9 100644 --- a/src/annoy/places.py +++ b/src/annoy/places.py @@ -1,6 +1,7 @@ PLACE_DEFINITIONS = [ ('top', 'U góry wszystkich stron', True), ('book-page', 'Strona książki', False), + ('book-page-center', 'Strona książki, środek', False), ('book-text-intermission', 'Przerwa w treści książki', False), ('book-fragment-list', 'Obok listy fragmentów książki', False), ('blackout', 'Blackout', True, ( diff --git a/src/annoy/templates/annoy/banner.html b/src/annoy/templates/annoy/banner.html index 4b2bdcfb8..9a67fcdcb 100644 --- a/src/annoy/templates/annoy/banner.html +++ b/src/annoy/templates/annoy/banner.html @@ -11,7 +11,7 @@ annoy-banner annoy-banner_{{ banner.place }} annoy-banner-style_{{ banner.style }} - {% if banner.image %}with-image{% endif %} + {% if banner.get_image %}with-image{% endif %} {% if banner.smallfont %}banner-smallfont{% endif %} " id="annoy-banner-{{ banner.id }}" @@ -20,19 +20,25 @@ {% if banner.background_color %}background-color: {{ banner.background_color }};{% endif %} "> {% if not banner.action_label %} - + {% endif %}
- {% if banner.image %} - + {% if banner.get_image %} +
+ +
{% endif %}
{{ banner.get_text|safe|linebreaks }}
{% if banner.action_label %} -
+ {{ banner.action_label }} {% endif %} diff --git a/src/annoy/templatetags/annoy.py b/src/annoy/templatetags/annoy.py index 25293f1ca..cdf4dbc5d 100644 --- a/src/annoy/templatetags/annoy.py +++ b/src/annoy/templatetags/annoy.py @@ -7,8 +7,8 @@ register = template.Library() @register.inclusion_tag('annoy/banner.html', takes_context=True) -def annoy_banner(context, place): - banners = Banner.choice(place, request=context['request']) +def annoy_banner(context, place, **kwargs): + banners = Banner.choice(place, request=context['request'], **kwargs) return { 'banner': banners.first(), 'closable': PLACES.get(place, False), diff --git a/src/catalogue/templates/catalogue/book_detail.html b/src/catalogue/templates/catalogue/book_detail.html index 32a10ad11..ccce18bf8 100644 --- a/src/catalogue/templates/catalogue/book_detail.html +++ b/src/catalogue/templates/catalogue/book_detail.html @@ -5,9 +5,14 @@ {% load choose_cites from social_tags %} {% load catalogue_tags %} {% load likes_book from social_tags %} +{% load annoy %} {% block global-content %} +
+ {% annoy_banner 'book-page' %} +
+
{% endwith %} diff --git a/src/club/admin.py b/src/club/admin.py index 442592803..8620e7407 100644 --- a/src/club/admin.py +++ b/src/club/admin.py @@ -11,6 +11,7 @@ from django.utils.safestring import mark_safe from fnpdjango.actions import export_as_csv_action from modeltranslation.admin import TranslationAdmin import annoy.models +from messaging.models import Contact, Level from wolnelektury.utils import YesNoFilter from . import models @@ -127,6 +128,11 @@ class CrisisFilter(admin.SimpleListFilter): ) +class OptOutFilter(YesNoFilter): + title = 'opt out' + parameter_name = 'optout' + q = Q(email__in=Contact.objects.filter(level=Level.OPT_OUT).values_list('email', flat=True)) + class ScheduleAdmin(admin.ModelAdmin): form = ScheduleForm @@ -139,6 +145,7 @@ class ScheduleAdmin(admin.ModelAdmin): search_fields = ['email', 'source'] list_filter = [ 'is_cancelled', 'monthly', 'yearly', 'method', + 'consent', OptOutFilter, PayedFilter, ActiveFilter, ExpiredFilter, SourceFilter, CrisisFilter ] diff --git a/src/club/forms.py b/src/club/forms.py index b75877d49..e0963036a 100644 --- a/src/club/forms.py +++ b/src/club/forms.py @@ -35,6 +35,8 @@ class DonationStep1Form(forms.ModelForm): self.referer = referer super().__init__(*args, **kwargs) club = models.Club.objects.first() + if self.instance.is_custom_amount(): + self.fields['custom_amount'].initial = int(self.instance.amount) if club is not None: self.fields['custom_amount'].widget.attrs['min'] = club.min_amount @@ -50,7 +52,8 @@ class DonationStep1Form(forms.ModelForm): return state def save(self, *args, **kwargs): - self.instance.source = self.referer + if self.referer is not None: + self.instance.source = self.referer return super().save(*args, **kwargs) diff --git a/src/club/models.py b/src/club/models.py index c40428def..77154954b 100644 --- a/src/club/models.py +++ b/src/club/models.py @@ -161,6 +161,15 @@ class Schedule(models.Model): club = Club.objects.first() return club.get_description_for_amount(self.amount, self.monthly) + def is_custom_amount(self): + club = Club.objects.first() + if not self.amount: + return False + if self.monthly: + return not club.monthlyamount_set.filter(amount=self.amount).exists() + else: + return not club.singleamount_set.filter(amount=self.amount).exists() + def initiate_payment(self, request): return self.get_payment_method().initiate(request, self) diff --git a/src/club/templates/club/donation_infobox.html b/src/club/templates/club/donation_infobox.html new file mode 100644 index 000000000..093480b73 --- /dev/null +++ b/src/club/templates/club/donation_infobox.html @@ -0,0 +1,10 @@ +
+
+ Dziękujemy, że decydujesz się wspierać nas co miesiąc.
+ Jeśli to pomyłka, możesz zmienić darowiznę na jednorazową. +
+
+ Wolę wspierać co miesiąc! + Zmień na comiesięczną wpłatę. +
+
diff --git a/src/club/templates/club/donation_step1_form.html b/src/club/templates/club/donation_step1_form.html index 033b0ea3f..49805559b 100644 --- a/src/club/templates/club/donation_step1_form.html +++ b/src/club/templates/club/donation_step1_form.html @@ -1,7 +1,7 @@ {% load static %} {% load i18n %} -
+ {% csrf_token %} {{ form.errors }} @@ -21,14 +21,10 @@ {% with amounts=club.get_amounts %}
{% for amount in amounts.single %} -
+
-

{{ amount.amount }} zł

- {% if amount.description %} -

{{ amount.description|safe }}

- {% endif %} - +
{% endfor %} @@ -41,13 +37,9 @@
{% for amount in amounts.monthly %} -
-

{{ amount.amount }} zł {% trans "/mies." context "kwota na miesiąc" %}

+
- {% if amount.description %} -

{{ amount.description|safe }}

- {% endif %} - +
{% endfor %} @@ -58,7 +50,7 @@
- + {{ form.custom_amount }}
diff --git a/src/club/templates/club/donation_step2.html b/src/club/templates/club/donation_step2.html index 3864ad5a5..5d602135d 100644 --- a/src/club/templates/club/donation_step2.html +++ b/src/club/templates/club/donation_step2.html @@ -8,26 +8,27 @@ {% block donation-step-content %} -
+

{{ schedule.amount|floatformat }} zł - {% if schedule.monthly %} - {% trans "/mies." context "kwota na miesiąc" %} - {% endif %} + {% trans "miesięcznie" %} + {% trans "jednorazowo" %}

+ {% if schedule.get_description %}

{{ schedule.get_description }}

+ {% endif %}
+ {% include "club/donation_infobox.html" %} + {% csrf_token %} {{ form.errors }} - {{ form.amount }} - {{ form.monthly }}
diff --git a/src/club/templates/club/donation_step3.html b/src/club/templates/club/donation_step3.html index baeee9f80..999c70872 100644 --- a/src/club/templates/club/donation_step3.html +++ b/src/club/templates/club/donation_step3.html @@ -8,21 +8,33 @@ {% block donation-step-content %} -
+

{{ schedule.amount|floatformat }} zł {% if schedule.monthly %} - {% trans "/mies." context "kwota na miesiąc" %} + {% trans "miesięcznie" %} + {% else %} + {% trans "jednorazowo" %} {% endif %}

+ {% include "club/donation_infobox.html" %} + + {% if schedule.monthly %} +

Darowizna będzie pobierana automatycznie co miesiąc.

+

Możesz z niej zrezygnować w dowolnej chwili, korzystając z linku który dostaniesz mailem.

+ {% endif %} +
-
+ + +
+ {% for method in schedule.get_payment_methods %} {% invite_payment method schedule %} {% endfor %} diff --git a/src/club/templates/club/donation_step_base.html b/src/club/templates/club/donation_step_base.html index cede1b4ca..804ace3f8 100644 --- a/src/club/templates/club/donation_step_base.html +++ b/src/club/templates/club/donation_step_base.html @@ -31,9 +31,7 @@
Wspieraj Wolne Lektury
-

{% trans "Wspieraj Wolne Lektury" %}

-

{% trans "Dziękujemy, że chcesz razem z nami uwalniać książki!" %}

-

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

+ {% chunk "donate-top" %}
@@ -43,7 +41,7 @@ {% endif %}
1 -

{% trans "Rodzaj wsparcia" %}

+

{% trans "Kwota wsparcia" %}

{% if view.step > 1 and view.step != 4 %} @@ -84,6 +82,8 @@