From: Radek Czajka Date: Fri, 25 Nov 2022 14:41:33 +0000 (+0100) Subject: More views, and likes working. X-Git-Url: https://git.mdrn.pl/wolnelektury.git/commitdiff_plain/f9a071b288cec64a55000125a63b62a3b233c3fd?ds=inline;hp=d10c63f27d1494a04d57a6c805d093089526430a More views, and likes working. --- diff --git a/src/catalogue/templates/catalogue/2022/book_box.html b/src/catalogue/templates/catalogue/2022/book_box.html index db6bfb103..dd3b8fd5e 100644 --- a/src/catalogue/templates/catalogue/2022/book_box.html +++ b/src/catalogue/templates/catalogue/2022/book_box.html @@ -1,7 +1,7 @@ {% load sorl_thumbnail %} -
-
+
+
{% if book.is_picture %} {% if book.image_file %} @@ -15,6 +15,17 @@ {% endif %} {% endif %} + +
+
+
+ {% csrf_token %} {# this needs to be copied in with JS #} + + + +
+
+
{% if book.is_book %} @@ -26,7 +37,7 @@ {% if book.is_picture %} {% endif %} - +

{% for author in book.authors %} diff --git a/src/catalogue/templates/catalogue/2022/set_detail.html b/src/catalogue/templates/catalogue/2022/set_detail.html new file mode 100644 index 000000000..e835a86c5 --- /dev/null +++ b/src/catalogue/templates/catalogue/2022/set_detail.html @@ -0,0 +1,41 @@ +{% extends '2022/base.html' %} + +{% load choose_cites from social_tags %} + + +{% block breadcrumbs %} + Półka +{% endblock %} + +{% block main %} +
+
+

{{ tags.0.name }}

+
+
+ +
+
+
+ + +
+
+ Sortuj: +
+ + + +
+
+
+
+ +
+
+ {% for book in object_list %} + {% include "catalogue/2022/book_box.html" %} + {% endfor %} +
+
+{% endblock %} diff --git a/src/catalogue/views.py b/src/catalogue/views.py index b57ba11b5..2955a5ce0 100644 --- a/src/catalogue/views.py +++ b/src/catalogue/views.py @@ -148,10 +148,13 @@ 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_set = len(tags) == 1 and tags[0].category == 'set' 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_set and new_layout.value: + template = 'catalogue/2022/set_detail.html' elif is_theme and new_layout.value: template = 'catalogue/2022/theme_detail.html' else: diff --git a/src/messaging/templates/messaging/contact_detail.html b/src/messaging/templates/messaging/contact_detail.html index 725f2080d..f1e606d3c 100644 --- a/src/messaging/templates/messaging/contact_detail.html +++ b/src/messaging/templates/messaging/contact_detail.html @@ -1,4 +1,4 @@ -{% extends "base/base.html" %} +{% extends request.EXPERIMENTS.layout.value|yesno:"2022/base_simple.html,base/base.html" %} {% block body %} diff --git a/src/messaging/templates/messaging/contact_form.html b/src/messaging/templates/messaging/contact_form.html index 153490abf..789042dba 100644 --- a/src/messaging/templates/messaging/contact_form.html +++ b/src/messaging/templates/messaging/contact_form.html @@ -1,10 +1,12 @@ -{% extends "base/base.html" %} +{% extends request.EXPERIMENTS.layout.value|yesno:"2022/base_simple.html,base/base.html" %} {% block body %} -

Czy na pewno chcesz zrezygnować z otrzymywania e-maili na adres {{ object.email }}?

+

Rezygnacja z e-maili

-
+

Czy na pewno chcesz zrezygnować z otrzymywania e-maili na adres {{ object.email }}?

+ + {% csrf_token %}
diff --git a/src/paypal/templates/paypal/cancel.html b/src/paypal/templates/paypal/cancel.html index 459979975..92360519b 100644 --- a/src/paypal/templates/paypal/cancel.html +++ b/src/paypal/templates/paypal/cancel.html @@ -1,6 +1,7 @@ -{% extends "base/base.html" %} +{% extends request.EXPERIMENTS.layout.value|yesno:"2022/base_simple.html,base/base.html" %} {% load i18n %} {% block body %} +

Płatność nieudana

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

-{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/src/pdcounter/templates/pdcounter/2022/author_detail.html b/src/pdcounter/templates/pdcounter/2022/author_detail.html new file mode 100644 index 000000000..c1920d1fd --- /dev/null +++ b/src/pdcounter/templates/pdcounter/2022/author_detail.html @@ -0,0 +1,54 @@ +{% extends "2022/base.html" %} +{% load i18n %} +{% load time_tags %} + +{% block settings %} + {% load title %} + {% title author.name %} +{% endblock %} + +{% block breadcrumbs %} + Katalog + Autor +{% endblock %} + +{% block main %} +
+
+

{{ author.name }}

+
+
+ +
+ + + {% if author.alive %} +

+ {% trans "This author's works are copyrighted." %} + {% trans "Find out why Internet libraries can't publish this author's works." %} +

+ {% else %} + {% if author.in_pd %} +

{% trans "This author's works are in public domain, but they were not yet published on Internet library of Wolne Lektury." %}

+ {% else %} +
+

+ {% trans "This author's works will become part of public domain and will be allowed to be published without restrictions in" %} +

+
+

+ {% trans "Find out why Internet libraries can't publish this author's works." %} +

+
+ {% endif %} + {% endif %} + + +
+ +
+
+ {% include "catalogue/2022/author_box.html" %} +
+
+{% endblock %} diff --git a/src/pdcounter/templates/pdcounter/2022/book_detail.html b/src/pdcounter/templates/pdcounter/2022/book_detail.html new file mode 100644 index 000000000..9f07b479c --- /dev/null +++ b/src/pdcounter/templates/pdcounter/2022/book_detail.html @@ -0,0 +1,54 @@ +{% extends '2022/base.html' %} +{% load i18n %} +{% load time_tags %} + +{% block global-content %} + + + +
+
+
+ +
+ {% if book.in_pd %} +

{% trans "This work is in public domain and will be published on Internet library of Wolne Lektury soon." %}

+ {% else %} + {% if book.pd %} +

+ {% trans "This work will become part of public domain and will be allowed to be published without restrictions in" %} +

+
+

+ {% trans "Find out why Internet libraries can't publish this work." %} +

+ {% else %} +

+ {% trans "This work is copyrighted." %} + {% trans "Find out why Internet libraries can't publish this work." %} +

+ {% endif %} + {% endif %} +
+
+
+
+ + +{% endblock %} diff --git a/src/pdcounter/views.py b/src/pdcounter/views.py index 5e3f3be7c..c782c0b0f 100644 --- a/src/pdcounter/views.py +++ b/src/pdcounter/views.py @@ -18,7 +18,12 @@ def book_stub_detail(request, slug): form = PublishingSuggestForm(initial={"books": "%s — %s, \n" % (book.author, book.title)}) - return render(request, 'pdcounter/book_stub_detail.html', { + if request.EXPERIMENTS['layout'].value: + template_name = 'pdcounter/2022/book_detail.html' + else: + template_name = 'pdcounter/book_detail.html' + + return render(request, template_name, { 'book': book, 'pd_counter': pd_counter, 'form': form, @@ -35,7 +40,12 @@ def author_detail(request, slug): form = PublishingSuggestForm(initial={"books": author.name + ", \n"}) - return render(request, 'pdcounter/author_detail.html', { + if request.EXPERIMENTS['layout'].value: + template_name = 'pdcounter/2022/author_detail.html' + else: + template_name = 'pdcounter/author_detail.html' + + return render(request, template_name, { 'author': author, 'pd_counter': pd_counter, 'form': form, diff --git a/src/social/forms.py b/src/social/forms.py index 1e2462ac0..8527915a6 100644 --- a/src/social/forms.py +++ b/src/social/forms.py @@ -4,7 +4,7 @@ from django import forms from django.utils.translation import gettext_lazy as _ -from catalogue.models import Tag +from catalogue.models import Book, Tag from catalogue import utils from social.utils import get_set, set_sets @@ -49,3 +49,47 @@ class NewSetForm(forms.Form): new_set.save() return new_set + + +class AddSetForm(forms.Form): + name = forms.CharField(max_length=50) + book = forms.IntegerField() + + def save(self, user): + name = self.cleaned_data['name'].strip() + if not name: return + tag = get_set(user, name) + try: + book = Book.objects.get(id=self.cleaned_data['book']) + except Book.DoesNotExist: + return + + try: + book.tag_relations.create(tag=tag) + except: + pass + + return book, tag + + +class RemoveSetForm(forms.Form): + slug = forms.CharField(max_length=50) + book = forms.IntegerField() + + def save(self, user): + slug = self.cleaned_data['slug'] + try: + tag = Tag.objects.get(user=user, slug=slug) + except Tag.DoesNotExist: + return + try: + book = Book.objects.get(id=self.cleaned_data['book']) + except Book.DoesNotExist: + return + + try: + book.tag_relations.filter(tag=tag).delete() + except: + pass + + return book, tag diff --git a/src/social/templates/social/2022/my_shelf.html b/src/social/templates/social/2022/my_shelf.html new file mode 100644 index 000000000..3dbd80d4d --- /dev/null +++ b/src/social/templates/social/2022/my_shelf.html @@ -0,0 +1,41 @@ +{% extends '2022/base.html' %} + + +{% block settings %} + {% load title %} + {% title 'Półka' %} +{% endblock %} + +{% block main %} +
+
+

Półka

+
+
+ +
+
+
+ + +
+
+ Sortuj: +
+ + + +
+
+
+
+ +
+
+ {% for book in books %} + {% include "catalogue/2022/book_box.html" %} + {% endfor %} +
+
+ +{% endblock %} diff --git a/src/social/urls.py b/src/social/urls.py index e092ff0bf..dc34fb62a 100644 --- a/src/social/urls.py +++ b/src/social/urls.py @@ -8,7 +8,11 @@ from . import views urlpatterns = [ path('lektura//lubie/', views.like_book, name='social_like_book'), + path('dodaj-tag/', views.AddSetView.as_view(), name='social_add_set_tag'), + path('usun-tag/', views.RemoveSetView.as_view(), name='social_remove_set_tag'), + path('moje-tagi/', views.my_tags, name='social_my_tags'), path('lektura//nie_lubie/', views.unlike_book, name='social_unlike_book'), path('lektura//polki/', never_cache(views.ObjectSetsFormView()), name='social_book_sets'), path('polka/', views.my_shelf, name='social_my_shelf'), + path('ulubione/', views.my_liked), ] diff --git a/src/social/views.py b/src/social/views.py index 89661590e..d9627eb9e 100644 --- a/src/social/views.py +++ b/src/social/views.py @@ -4,11 +4,14 @@ from django.shortcuts import render, get_object_or_404, redirect from django.http import HttpResponseForbidden, JsonResponse from django.contrib.auth.decorators import login_required +from django.views.decorators.cache import never_cache from django.views.decorators.http import require_POST +from django.views.generic.edit import FormView from ajaxable.utils import AjaxableFormView -from catalogue.models import Book +from catalogue.models import Book, Tag +import catalogue.models.tag from social import forms from wolnelektury.utils import is_ajax @@ -32,6 +35,23 @@ def like_book(request, slug): return redirect(book) +class AddSetView(FormView): + form_class = forms.AddSetForm + template_name = 'forms/form_detail.html' + + def form_valid(self, form): + book, tag = form.save(self.request.user) + + if is_ajax(self.request): + return JsonResponse(get_sets_for_book_ids([book.id], self.request.user)) + else: + return redirect(book) + + +class RemoveSetView(AddSetView): + form_class = forms.RemoveSetForm + + @require_POST def unlike_book(request, slug): if not request.user.is_authenticated: @@ -48,11 +68,64 @@ def unlike_book(request, slug): @login_required def my_shelf(request): - return render(request, 'social/my_shelf.html', { + if request.EXPERIMENTS['layout'].value: + template_name = 'social/2022/my_shelf.html' + else: + template_name = 'social/my_shelf.html' + return render(request, template_name, { 'books': Book.tagged.with_any(request.user.tag_set.all()) }) +def get_sets_for_book_ids(book_ids, user): + data = {} + tagged = catalogue.models.tag.TagRelation.objects.filter( + tag__user=user, + #content_type= # for books, + object_id__in=book_ids + ).order_by('tag__sort_key') + for t in tagged: + # related? + item = data.setdefault(t.object_id, []) + if t.tag.name: + item.append({ + "slug": t.tag.slug, + "url": t.tag.get_absolute_url(), + "name": t.tag.name, + }) + for b in book_ids: + if b not in data: + data[b] = None + return data + + + + +@never_cache +def my_liked(request): + if not request.user.is_authenticated: + return JsonResponse({}) + try: + ids = [int(x) for x in request.GET.get('ids', '').split(',')] + except: + return JsonResponse({}) + return JsonResponse(get_sets_for_book_ids(ids, request.user)) + + +@never_cache +@login_required +def my_tags(request): + term = request.GET.get('term', '') + tags = Tag.objects.filter(user=request.user).order_by('sort_key') + if term: + tags = tags.filter(name__icontains=term) + return JsonResponse( + [ + t.name for t in tags + ], safe=False + ) + + class ObjectSetsFormView(AjaxableFormView): form_class = forms.ObjectSetsForm placeholdize = True diff --git a/src/wolnelektury/settings/static.py b/src/wolnelektury/settings/static.py index 9e07efb27..81a935a58 100644 --- a/src/wolnelektury/settings/static.py +++ b/src/wolnelektury/settings/static.py @@ -25,6 +25,7 @@ PIPELINE = { '2022': { 'source_filenames': [ 'contrib/jquery-ui-1.13.1.custom/jquery-ui.css', + 'css/jquery.countdown.css', '2022/styles/main.scss', '2022/more.scss', 'chunks/edit.scss', @@ -138,11 +139,14 @@ PIPELINE = { }, '2022': { 'source_filenames': [ + '2022/scripts/vendor.js', 'contrib/jquery-ui-1.13.1.custom/jquery-ui.js', 'js/search.js', 'js/2022.js', '2022/book/filter.js', 'chunks/edit.js', + '2022/scripts/modernizr.js', + '2021/scripts/main.js', 'js/contrib/jquery.countdown.js', 'js/contrib/jquery.countdown-pl.js', 'js/contrib/jquery.countdown-de.js', 'js/contrib/jquery.countdown-uk.js', diff --git a/src/wolnelektury/static/2021/scripts/main.js b/src/wolnelektury/static/2021/scripts/main.js index 3dd799a25..a37d25691 100644 --- a/src/wolnelektury/static/2021/scripts/main.js +++ b/src/wolnelektury/static/2021/scripts/main.js @@ -335,3 +335,156 @@ document.execCommand('copy'); }); })(); + + + +// Likes +(function() { + + ids = new Set(); + $(".icon-like").each( + (i, e)=>{ + ids.add($(e).attr('data-book')); + } + ); + ids = [...ids].join(','); + + state = { + liked: [], + }; + + $(".icon-like").on('click', function(e) { + e.preventDefault(); + let liked = $(this).hasClass('icon-liked'); + $btn = $(this); + if (liked) { + $.post({ + url: '/ludzie/lektura/' + $(this).attr('data-book-slug') + '/nie_lubie/', + data: {'csrfmiddlewaretoken': $('[name=csrfmiddlewaretoken]').val()}, + success: function() { + delete state.liked[$btn.attr('data-book')]; + updateLiked($btn); + } + }) + } else { + $.post({ + url: '/ludzie/lektura/' + $(this).attr('data-book-slug') + '/lubie/', + data: {'csrfmiddlewaretoken': $('[name=csrfmiddlewaretoken]').val()}, + success: function() { + state.liked[$btn.attr('data-book')] = []; + updateLiked($btn); + }, + error: function(e) { + if (e.status == 403) { + $('#login-link').click(); + } + }, + }); + } + }) + + $(".add-set-tag input[name=name]").autocomplete({ + source: '/ludzie/moje-tagi/', + }).on('autocompleteopen', function() { + $(this).closest('article').addClass('ac-hover'); + }).on('autocompleteclose', function() { + $(this).closest('article').removeClass('ac-hover'); + }); + $(".add-set-tag").on("submit", function(e) { + e.preventDefault(); + let $form = $(this); + $.post({ + url: $form.attr("action"), + data: $form.serialize(), + success: (data) => { + updateFromData(data); + updateLikedAll(); + $('input[name=name]', $form).val(''); + } + }); + }) + + $(document).on("click", ".sets .close", function() { + let bookId = $(this).closest("[data-book]").attr('data-book'); + $.post({ + url: '/ludzie/usun-tag/', + data: { + csrfmiddlewaretoken: $("[name=csrfmiddlewaretoken]").val(), + book: bookId, + slug: $(this).parent().attr('data-set'), + }, + success: (data) => { + updateFromData(data); + updateLikedAll(); + } + }); + }) + + + function refreshAll(ids) { + $.ajax('/ludzie/ulubione/?ids=' + ids, { + success: function(result) { + updateFromData(result); + updateLikedAll(); + }, + }); + } + refreshAll(ids); + + function updateFromData(data) { + for (pk in data) { + if (data[pk] === null) { + delete state.liked[pk]; + } else { + state.liked[pk] = data[pk]; + } + } + } + + function updateLikedAll() { + $(".icon-like").each( + (i, e) => { + updateLiked(e); + } + ) + } + + function updateLiked(e) { + let bookId = $(e).attr('data-book'); + let liked = bookId in state.liked; + $(e).toggleClass('icon-liked', liked); + let $bookContainer = $('.book-container-' + bookId); + $bookContainer.toggleClass('book-liked', liked); + let $sets = $(".sets", $bookContainer); + $sets.empty(); + $.each(state.liked[bookId], (i,e) => { + let $set = $(""); + $set.attr("data-set", e.slug); + let $setA = $("").appendTo($set); + $setA.attr("href", e.url); + $setA.text(e.name); + let $setX = $("").appendTo($set); + $sets.append($set); + }); + } + +})(); + + + +// Toggle a class on long press. +(function() { + const TIME = 250; + let timer; + + $("[data-longpress]").on("touchstart", (e) => { + $e = $(e.currentTarget); + timer = setTimeout(() => { + $e.toggleClass($e.attr('data-longpress')); + }, TIME); + }); + + $("[data-longpress]").on("touchend", () => { + clearTimeout(timer); + }); +})(); diff --git a/src/wolnelektury/static/2022/styles/local.scss b/src/wolnelektury/static/2022/styles/local.scss index 726b1b3f3..5f6327550 100644 --- a/src/wolnelektury/static/2022/styles/local.scss +++ b/src/wolnelektury/static/2022/styles/local.scss @@ -96,3 +96,86 @@ $teal: #007880; } } } + + + + +.book-container { + position: relative; + + .set-tools { + display: none; + font-size: 12px; + position: absolute; + bottom: 0px; + right: 0; + left: 0; + background: black; + background: linear-gradient(0deg, rgba(0,0,0,1) 0%, rgba(255,255,255,0) 100%); + color: white; + padding: 30px 10px 10px; + + .sets { + span { + padding: 3px 18px 3px 3px; + background: white; + color: black; + margin-right: 5px; + margin-bottom: 5px; + display: inline-block; + border-radius: 3px; + position: relative; + + .close { + margin-left: 5px; + position: absolute; + top: 0; + right: 0; + font-family: wl; + font-size: 10px; + color: #FF4C54; + padding: 4px; + + &:hover { + text-decoration: none; + color: black; + cursor: pointer; + } + } + } + } + .add-set-tag { + input { + width: 100%; + } + button { + font-family: wl; + position: absolute; + right: 12px; + color: #FF4C54; + bottom: 10px; + font-size: 12px; + border: 0; + background: none; + padding: 3px; + } + } + } +} + + +.book-container-activator { + &:hover, &.hover, &.ac-hover { + .book-container { + &.book-liked { + .icon-liked { + font-size: 2em; + } + + .set-tools { + display: block; + } + } + } + } +} diff --git a/src/wolnelektury/templates/2022/base.html b/src/wolnelektury/templates/2022/base.html index 36b1452fb..363d99f0e 100644 --- a/src/wolnelektury/templates/2022/base.html +++ b/src/wolnelektury/templates/2022/base.html @@ -17,7 +17,6 @@ {% if title %}{{ title }} | {% endif %}WolneLektury.pl {% stylesheet '2022' %} - @@ -67,9 +66,6 @@ {% include '2022/footer.html' %} - - - @@ -77,5 +73,6 @@ {% javascript '2022_player' %} {% block extrabody %}{% endblock %} {% include '2022/hotjar.html' %} + {% csrf_token %} diff --git a/src/wolnelektury/templates/2022/header.html b/src/wolnelektury/templates/2022/header.html index 6c7ff1185..a99a8ab4a 100644 --- a/src/wolnelektury/templates/2022/header.html +++ b/src/wolnelektury/templates/2022/header.html @@ -44,7 +44,7 @@ {% else %} diff --git a/src/wolnelektury/templates/registration/login.html b/src/wolnelektury/templates/registration/login.html index a962aec26..150988a28 100644 --- a/src/wolnelektury/templates/registration/login.html +++ b/src/wolnelektury/templates/registration/login.html @@ -24,6 +24,9 @@ {% trans "Forgot Password?" %} +

+ Nie masz jeszcze konta? + {% if USE_OPENID %}

{% trans "Sign in using:" %}