{% load sorl_thumbnail %}
-<article class="l-books__item" data-pop="-{{ book.popularity.count }}">
- <figure class="l-books__item__img" style="height:240px">
+<article class="l-books__item book-container-activator" data-pop="-{{ book.popularity.count }}" data-longpress="hover">
+ <figure class="l-books__item__img book-container book-container-{{ book.pk }}" style="height:240px" data-book="{{ book.pk }}">
<a href="{{ book.get_absolute_url }}">
{% if book.is_picture %}
{% if book.image_file %}
{% endif %}
{% endif %}
</a>
+
+ <div class="set-tools">
+ <div class="sets"></div>
+ <form method="post" action="{% url 'social_add_set_tag' %}" class="add-set-tag">
+ {% csrf_token %} {# this needs to be copied in with JS #}
+ <input type="hidden" name="book" value="{{ book.id }}">
+ <input name="name" placeholder="nazwa półki" />
+ <button type="submit"></button>
+ </form>
+ </div>
+
</figure>
<div class="l-books__item__actions">
{% if book.is_book %}
{% if book.is_picture %}
<span class="icon icon-picture" title="obraz"></span>
{% endif %}
- <!-- a href="#" class="icon icon-like"></a -->
+ <a href="{{ book.get_absolute_url }}" class="icon icon-like" data-book="{{ book.pk }}" data-book-slug="{{ book.slug }}"></a>
</div>
<h3>
{% for author in book.authors %}
--- /dev/null
+{% extends '2022/base.html' %}
+
+{% load choose_cites from social_tags %}
+
+
+{% block breadcrumbs %}
+ <a href="/ludzie/polka/"><span>Półka</span></a>
+{% endblock %}
+
+{% block main %}
+ <div class="l-section">
+ <div class="l-author__header">
+ <h1>{{ tags.0.name }}</h1>
+ </div>
+ </div>
+
+ <div class="l-section">
+ <div class="l-books__header">
+ <div class="l-books__input">
+ <i class="icon icon-filter"></i>
+ <input type="text" placeholder="filtry, tytuł" class="quick-filter" data-for="book-list">
+ </div>
+ <div class="l-books__sorting">
+ <span>Sortuj:</span>
+ <div>
+ <button data-order="data-pop">najpopularniejsze</button>
+ <button class="is-active">alfabetycznie</button>
+ <!--button>chronologicznie</button-->
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <div class="l-section l-section--col">
+ <div class="l-books__grid" id="book-list">
+ {% for book in object_list %}
+ {% include "catalogue/2022/book_box.html" %}
+ {% endfor %}
+ </div>
+ </div>
+{% endblock %}
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:
-{% extends "base/base.html" %}
+{% extends request.EXPERIMENTS.layout.value|yesno:"2022/base_simple.html,base/base.html" %}
{% block body %}
-{% extends "base/base.html" %}
+{% extends request.EXPERIMENTS.layout.value|yesno:"2022/base_simple.html,base/base.html" %}
{% block body %}
-<h1>Czy na pewno chcesz zrezygnować z otrzymywania e-maili na adres {{ object.email }}?</h1>
+ <h1>Rezygnacja z e-maili</h1>
-<form method="POST" action="">
+ <p>Czy na pewno chcesz zrezygnować z otrzymywania e-maili na adres {{ object.email }}?</p>
+
+ <form method="POST" action="">
{% csrf_token %}
<button type="submit">Tak, wypisz mnie</button>
</form>
-{% extends "base/base.html" %}
+{% extends request.EXPERIMENTS.layout.value|yesno:"2022/base_simple.html,base/base.html" %}
{% load i18n %}
{% block body %}
+ <h1>Płatność nieudana</h1>
<p>{% trans "Zrezygnowano z płatności :(" %}</p>
-{% endblock %}
\ No newline at end of file
+{% endblock %}
--- /dev/null
+{% extends "2022/base.html" %}
+{% load i18n %}
+{% load time_tags %}
+
+{% block settings %}
+ {% load title %}
+ {% title author.name %}
+{% endblock %}
+
+{% block breadcrumbs %}
+ <a href="/katalog/"><span>Katalog</span></a>
+ <a href="/katalog/autor/"><span>Autor</span></a>
+{% endblock %}
+
+{% block main %}
+ <div class="l-section">
+ <div class="l-author__header">
+ <h1>{{ author.name }}</h1>
+ </div>
+ </div>
+
+ <div class="l-section">
+
+
+ {% if author.alive %}
+ <p>
+ {% trans "This author's works are copyrighted." %}
+ {% trans "<a href='http://domenapubliczna.org/co-to-jest-domena-publiczna/'>Find out</a> why Internet libraries can't publish this author's works." %}
+ </p>
+ {% else %}
+ {% if author.in_pd %}
+ <p>{% trans "This author's works are in public domain, but they were not yet published on Internet library of Wolne Lektury." %}</p>
+ {% else %}
+ <div>
+ <p>
+ {% trans "This author's works will become part of public domain and will be allowed to be published without restrictions in" %}
+ </p>
+ <div class='countdown' data-until='{{ pd_counter|date_to_utc|utc_for_js }}'></div>
+ <p>
+ {% trans "<a href='http://domenapubliczna.org/co-to-jest-domena-publiczna/'>Find out</a> why Internet libraries can't publish this author's works." %}
+ </p>
+ </div>
+ {% endif %}
+ {% endif %}
+
+
+ </div>
+
+ <section class="l-section">
+ <div class="l-author">
+ {% include "catalogue/2022/author_box.html" %}
+ </div>
+ </section>
+{% endblock %}
--- /dev/null
+{% extends '2022/base.html' %}
+{% load i18n %}
+{% load time_tags %}
+
+{% block global-content %}
+ <div class="l-container">
+ <div class="l-breadcrumb">
+ <a href="/"><span>Strona główna</span></a>
+ <a href="/katalog/lektury/"><span>Literatura</span></a>
+ </div>
+ </div>
+
+
+ <main class="l-main">
+ <section class="l-section">
+ <div class="l-content">
+ <header class="l-header">
+ <div class="l-header__content">
+ <p>{{ book.author }}</p>
+ <h1>{{ book.title }}</h1>
+ {% if book.translator %}
+ <p class="l-header__translators">
+ tłum.
+ {{ book.translator }}
+ </p>
+ {% endif %}
+ </div>
+ </header>
+ <article class="l-article">
+ {% if book.in_pd %}
+ <p>{% trans "This work is in public domain and will be published on Internet library of Wolne Lektury soon." %}</p>
+ {% else %}
+ {% if book.pd %}
+ <p>
+ {% trans "This work will become part of public domain and will be allowed to be published without restrictions in" %}
+ </p>
+ <div class='countdown' data-until='{{ pd_counter|date_to_utc|utc_for_js }}'></div>
+ <p>
+ {% trans "<a href='http://domenapubliczna.org/co-to-jest-domena-publiczna/'>Find out</a> why Internet libraries can't publish this work." %}
+ </p>
+ {% else %}
+ <p>
+ {% trans "This work is copyrighted." %}
+ {% trans "<a href='http://domenapubliczna.org/co-to-jest-domena-publiczna/'>Find out</a> why Internet libraries can't publish this work." %}
+ </p>
+ {% endif %}
+ {% endif %}
+ </article>
+ </div>
+ </section>
+ </main>
+
+
+{% endblock %}
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,
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,
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
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
--- /dev/null
+{% extends '2022/base.html' %}
+
+
+{% block settings %}
+ {% load title %}
+ {% title 'Półka' %}
+{% endblock %}
+
+{% block main %}
+ <div class="l-section">
+ <div class="l-author__header">
+ <h1>Półka</h1>
+ </div>
+ </div>
+
+ <div class="l-section">
+ <div class="l-books__header">
+ <div class="l-books__input">
+ <i class="icon icon-filter"></i>
+ <input type="text" placeholder="filtry, tytuł" class="quick-filter" data-for="book-list">
+ </div>
+ <div class="l-books__sorting">
+ <span>Sortuj:</span>
+ <div>
+ <button data-order="data-pop">najpopularniejsze</button>
+ <button class="is-active">alfabetycznie</button>
+ <!--button>chronologicznie</button-->
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <div class="l-section l-section--col">
+ <div class="l-books__grid" id="book-list">
+ {% for book in books %}
+ {% include "catalogue/2022/book_box.html" %}
+ {% endfor %}
+ </div>
+ </div>
+
+{% endblock %}
urlpatterns = [
path('lektura/<slug:slug>/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/<slug:slug>/nie_lubie/', views.unlike_book, name='social_unlike_book'),
path('lektura/<slug:slug>/polki/', never_cache(views.ObjectSetsFormView()), name='social_book_sets'),
path('polka/', views.my_shelf, name='social_my_shelf'),
+ path('ulubione/', views.my_liked),
]
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
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:
@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
'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',
},
'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',
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 = $("<span>");
+ $set.attr("data-set", e.slug);
+ let $setA = $("<a>").appendTo($set);
+ $setA.attr("href", e.url);
+ $setA.text(e.name);
+ let $setX = $("<a class='close'></a>").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);
+ });
+})();
}
}
}
+
+
+
+
+.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;
+ }
+ }
+ }
+ }
+}
<title>{% if title %}{{ title }} | {% endif %}WolneLektury.pl</title>
<link rel="apple-touch-icon" href="apple-touch-icon.png">
{% stylesheet '2022' %}
- <script src="scripts/modernizr.js"></script>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
{% include '2022/footer.html' %}
- <script src="{% static '2022/scripts/vendor.js' %}"></script>
- <script src="{% static '2021/scripts/main.js' %}"></script>
-
<script type="text/javascript">
var LANGUAGE_CODE = "{{ LANGUAGE_CODE }}";
</script>
{% javascript '2022_player' %}
{% block extrabody %}{% endblock %}
{% include '2022/hotjar.html' %}
+ {% csrf_token %}
</body>
</html>
</a>
{% else %}
<div class="l-navigation__login">
- <a href='{% url 'login' %}?next={{ request.path }}'>Zaloguj się</a>
+ <a id="login-link" href='{% url 'login' %}?next={{ request.path }}'>Zaloguj się</a>
/
<a href='{% url 'register' %}?next={{ request.path }}'>Załóż konto</a>
</div>
<a href="{% url 'account_reset_password' %}">{% trans "Forgot Password?" %}</a>
+ <br><br>
+ <a href="{% url 'register' %}?next={{ request.GET.next }}">Nie masz jeszcze konta?</a>
+
{% if USE_OPENID %}
<h3>{% trans "Sign in using:" %}</h3>