def placeholdized(form):
for field in form.fields.values():
- field.widget.attrs['placeholder'] = field.label
+ field.widget.attrs['placeholder'] = field.label + ('*' if field.required else '')
return form
def __call__(self, request, *args, **kwargs):
"""A view displaying a form, or JSON if request is AJAX."""
obj = self.get_object(request, *args, **kwargs)
+
+ response = self.validate_object(obj, request)
+ if response:
+ return response
+
form_args, form_kwargs = self.form_args(request, obj)
if self.form_prefix:
form_kwargs['prefix'] = self.form_prefix
context.update(self.extra_context(request, obj))
return render_to_response(template, context, context_instance=RequestContext(request))
+ def validate_object(self, obj, request):
+ return None
+
def redirect_or_refresh(self, request, path, message=None):
"""If the form is AJAX, refresh the page. If not, go to `path`."""
if request.is_ajax():
from django.contrib.sites.models import Site
from django.core.urlresolvers import reverse
+ from django.http.response import HttpResponse
from django.utils.functional import lazy
from django.db import models
from piston.handler import AnonymousBaseHandler, BaseHandler
from piston.utils import rc
from sorl.thumbnail import default
+ from api.models import BookUserData
from catalogue.forms import BookImportForm
from catalogue.models import Book, Tag, BookMedia, Fragment, Collection
from catalogue.models.tag import prefetch_relations
+ from catalogue.utils import is_subscribed
from picture.models import Picture
from picture.forms import PictureImportForm
"""
allowed_methods = ['GET']
fields = ['title', 'parent', 'children'] + Book.formats + [
- 'media', 'url', 'cover', 'cover_thumb', 'simple_thumb', 'simple_cover', 'fragment_data', 'audio_length'] + [
- 'media', 'url', 'cover', 'cover_thumb', 'simple_thumb', 'simple_cover', 'fragment_data', 'preview'] + [
++ 'media', 'url', 'cover', 'cover_thumb', 'simple_thumb', 'simple_cover', 'fragment_data', 'audio_length',
++ 'preview'] + [
category_plural[c] for c in book_tag_categories]
@piwik_track
"""
allowed_methods = ('GET',)
model = Book
- fields = book_tag_categories + ['href', 'title', 'url', 'cover', 'cover_thumb', 'slug', 'simple_thumb']
+ fields = book_tag_categories + ['href', 'title', 'url', 'cover', 'cover_thumb', 'slug', 'simple_thumb', 'has_audio']
@classmethod
def genres(cls, book):
return rc.NOT_FOUND
+ class EpubHandler(BookDetailHandler):
+ def read(self, request, slug):
+ if not is_subscribed(request.user):
+ return rc.FORBIDDEN
+ try:
+ book = Book.objects.get(slug=slug)
+ except Book.DoesNotExist:
+ return rc.NOT_FOUND
+ response = HttpResponse(book.get_media('epub'))
+ return response
+
+
class EBooksHandler(AnonymousBooksHandler):
fields = ('author', 'href', 'title', 'cover') + tuple(Book.ebook_formats) + ('slug',)
class FilterBooksHandler(AnonymousBooksHandler):
fields = book_tag_categories + [
- 'href', 'title', 'url', 'cover', 'cover_thumb', 'simple_thumb', 'slug', 'key']
+ 'href', 'title', 'url', 'cover', 'cover_thumb', 'simple_thumb', 'has_audio', 'slug', 'key']
+ def parse_bool(self, s):
+ if s in ('true', 'false'):
+ return s == 'true'
+ else:
+ return None
+
def read(self, request):
key_sep = '$'
search_string = request.GET.get('search')
- is_lektura = request.GET.get('lektura')
- is_audiobook = request.GET.get('audiobook')
+ is_lektura = self.parse_bool(request.GET.get('lektura'))
+ is_audiobook = self.parse_bool(request.GET.get('audiobook'))
+ preview = self.parse_bool(request.GET.get('preview'))
after = request.GET.get('after')
count = int(request.GET.get('count', 50))
- if is_lektura in ('true', 'false'):
- is_lektura = is_lektura == 'true'
- else:
- is_lektura = None
- if is_audiobook in ('true', 'false'):
- is_audiobook = is_audiobook == 'true'
books = Book.objects.distinct().order_by('slug')
if is_lektura is not None:
books = books.filter(has_audience=is_lektura)
books = books.filter(media__type='mp3')
else:
books = books.exclude(media__type='mp3')
+ if preview is not None:
+ books = books.filter(preview=preview)
for key in request.GET:
if key in category_singular:
category = category_singular[key]
setattr(BookDetails, plural, _tags_getter(singular))
setattr(BookDetails, singular, _tag_getter(singular))
+
add_tag_getters()
# add fields for files in Book
def _file_getter(book_format):
- field = "%s_file" % book_format
- @classmethod
- def get_file(cls, book):
- f = getattr(book, field)
- if f:
- return MEDIA_BASE + f.url
+ @staticmethod
+ def get_file(book):
+ f_url = book.media_url(book_format)
+ if f_url:
+ return MEDIA_BASE + f_url
else:
return ''
return get_file
for book_format in Book.formats:
setattr(BookDetails, book_format, _file_getter(book_format))
++
add_file_getters()
"""
try:
-- tags, ancestors = read_tags(tags, allowed=self.categories)
++ tags, ancestors = read_tags(tags, request, allowed=self.categories)
except ValueError:
return rc.NOT_FOUND
fragments = Fragment.tagged.with_all(tags).select_related('book')
return rc.CREATED
else:
return rc.NOT_FOUND
- ids = BookUserData.objects.filter(user=request.user, complete=state == 'complete').values_list('book_id', flat=True)
+
+
+ class UserDataHandler(BaseHandler):
+ model = BookUserData
+ fields = ('state',)
+ allowed_methods = ('GET', 'POST')
+
+ def read(self, request, slug):
+ 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:
+ return {'state': 'not_started'}
+ return data
+
+ def create(self, request, slug, state):
+ try:
+ book = Book.objects.get(slug=slug)
+ except Book.DoesNotExist:
+ return rc.NOT_FOUND
+ if not request.user.is_authenticated():
+ return rc.FORBIDDEN
+ if state not in ('reading', 'complete'):
+ return rc.NOT_FOUND
+ data, created = BookUserData.objects.get_or_create(book=book, user=request.user)
+ data.state = state
+ data.save()
+ return data
+
+
+ class UserShelfHandler(BookDetailHandler):
+ fields = book_tag_categories + [
+ 'href', 'title', 'url', 'cover', 'cover_thumb', 'simple_thumb', 'slug', 'key']
+
+ def parse_bool(self, s):
+ if s in ('true', 'false'):
+ return s == 'true'
+ else:
+ return None
+
+ def read(self, request, state):
+ if not request.user.is_authenticated():
+ return rc.FORBIDDEN
+ if state not in ('reading', 'complete'):
+ return rc.NOT_FOUND
+ after = request.GET.get('after')
+ count = int(request.GET.get('count', 50))
++ ids = BookUserData.objects.filter(user=request.user, complete=state == 'complete')\
++ .values_list('book_id', flat=True)
+ books = Book.objects.filter(id__in=list(ids)).distinct().order_by('slug')
+ if after:
+ books = books.filter(slug__gt=after)
+ if count:
+ books = books[:count]
+ return books
--- /dev/null
--- /dev/null
++# -*- coding: utf-8 -*-
++from __future__ import unicode_literals
++
++from django.db import migrations, models
++
++
++class Migration(migrations.Migration):
++
++ dependencies = [
++ ('catalogue', '0024_book_audio_length'),
++ ('catalogue', '0024_auto_20180510_1407'),
++ ]
++
++ operations = [
++ ]
# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
#
from collections import OrderedDict
+ from datetime import date, timedelta
from random import randint
import os.path
import re
wiki_link = models.CharField(blank=True, max_length=240)
print_on_demand = models.BooleanField(_('print on demand'), default=False)
recommended = models.BooleanField(_('recommended'), default=False)
+ audio_length = models.CharField(_('audio length'), blank=True, max_length=8)
+ preview = models.BooleanField(_('preview'), default=False)
+ preview_until = models.DateField(_('preview until'), blank=True, null=True)
# files generated during publication
cover = EbookField(
def is_foreign(self):
return self.language_code() != settings.LANGUAGE_CODE
+ def set_audio_length(self):
+ length = self.get_audio_length()
+ if length > 0:
+ self.audio_length = self.format_audio_length(length)
+ self.save()
+
+ @staticmethod
+ def format_audio_length(seconds):
+ if seconds < 60*60:
+ minutes = seconds // 60
+ seconds = seconds % 60
+ return '%d:%02d' % (minutes, seconds)
+ else:
+ hours = seconds // 3600
+ minutes = seconds % 3600 // 60
+ seconds = seconds % 60
+ return '%d:%02d:%02d' % (hours, minutes, seconds)
+
+ def get_audio_length(self):
+ from mutagen.mp3 import MP3
+ total = 0
+ for media in self.get_mp3():
+ audio = MP3(media.file.path)
+ total += audio.info.length
+ return int(total)
+
def has_media(self, type_):
if type_ in Book.formats:
return bool(getattr(self, "%s_file" % type_))
def get_daisy(self):
return self.get_media("daisy")
+ def media_url(self, format_):
+ media = self.get_media(format_)
+ if media:
+ if self.preview:
+ return reverse('embargo_link', kwargs={'slug': self.slug, 'format_': format_})
+ else:
+ return media.url
+ else:
+ return None
+
+ def html_url(self):
+ return self.media_url('html')
+
+ def pdf_url(self):
+ return self.media_url('pdf')
+
+ def epub_url(self):
+ return self.media_url('epub')
+
+ def mobi_url(self):
+ return self.media_url('mobi')
+
+ def txt_url(self):
+ return self.media_url('txt')
+
+ def fb2_url(self):
+ return self.media_url('fb2')
+
+ def xml_url(self):
+ return self.media_url('xml')
+
def has_description(self):
return len(self.description) > 0
has_description.short_description = _('description')
has_description.boolean = True
- # ugly ugly ugly
def has_mp3_file(self):
- return bool(self.has_media("mp3"))
+ return self.has_media("mp3")
has_mp3_file.short_description = 'MP3'
has_mp3_file.boolean = True
def has_ogg_file(self):
- return bool(self.has_media("ogg"))
+ return self.has_media("ogg")
has_ogg_file.short_description = 'OGG'
has_ogg_file.boolean = True
def has_daisy_file(self):
- return bool(self.has_media("daisy"))
+ return self.has_media("daisy")
has_daisy_file.short_description = 'DAISY'
has_daisy_file.boolean = True
format_)
field_name = "%s_file" % format_
- books = Book.objects.filter(parent=None).exclude(**{field_name: ""})
+ books = Book.objects.filter(parent=None).exclude(**{field_name: ""}).exclude(preview=True)
paths = [(pretty_file_name(b), getattr(b, field_name).path) for b in books.iterator()]
return create_zip(paths, app_settings.FORMAT_ZIPS[format_])
index.index.rollback()
raise e
+ # will make problems in conjunction with paid previews
def download_pictures(self, remote_gallery_url):
gallery_path = self.gallery_path()
# delete previous files, so we don't include old files in ebooks
@classmethod
def from_text_and_meta(cls, raw_file, book_info, overwrite=False, dont_build=None, search_index=True,
- search_index_tags=True, remote_gallery_url=None):
+ search_index_tags=True, remote_gallery_url=None, days=0):
if dont_build is None:
dont_build = set()
dont_build = set.union(set(dont_build), set(app_settings.DONT_BUILD))
if created:
book_shelves = []
old_cover = None
+ book.preview = bool(days)
+ if book.preview:
+ book.preview_until = date.today() + timedelta(days)
else:
if not overwrite:
raise Book.AlreadyExists(_('Book %s already exists') % book_slug)
# Save XML file
book.xml_file.save('%s.xml' % book.slug, raw_file, save=False)
+ if book.preview:
+ book.xml_file.set_readable(False)
book.language = book_info.language
book.title = book_info.title
default=''
).contribute_to_class(Book, field_name)
++
add_file_fields()
</div>
{% book_shelf_tags book.pk %}
- <ul class="book-box-tools">
- <li class="book-box-read">
- {% if book.html_file %}
- <a href="{% url 'book_text' book.slug %}" class="downarrow">{% trans "Read online" %}</a>
- {% endif %}
- {% if book.print_on_demand %}
- <a href="{{ book.ridero_link }}" class="downarrow print tlite-tooltip" title="{% trans "Cena książki w druku cyfrowym jest zależna od liczby stron.<br>Przed zakupem upewnij się, że cena druku na żądanie jest dla Ciebie odpowiednia.<br>Wszystkie nasze zasoby w wersji elektronicznej są zawsze dostępne bezpłatnie." %}">{% trans "Print on demand –" %}
- <img src="{% static 'img/ridero.png' %}" style="height: 0.8em;"/></a>
- {% endif %}
- </li>
- <li class="book-box-download">
- <a class="downarrow">{% trans "Download" %}:</a>
- <div class="book-box-formats">
- {% if book.pdf_file %}
- <span><a href="{{ book.pdf_file.url}}">PDF</a></span>
- {% endif %}
- {% if book.epub_file %}
- <span><a href="{{ book.epub_file.url}}">EPUB</a></span>
+ {% if book|status:user != 'closed' %}
+ <ul class="book-box-tools">
+ <li class="book-box-read">
+ {% if book.html_file %}
+ <a href="{% url 'book_text' book.slug %}" class="downarrow">{% trans "Read online" %}</a>
{% endif %}
- {% if book.mobi_file %}
- <span><a href="{{ book.mobi_file.url}}">MOBI</a></span>
+ {% if book.print_on_demand %}
+ <a href="{{ book.ridero_link }}" class="downarrow print tlite-tooltip" title="{% trans "Cena książki w druku cyfrowym jest zależna od liczby stron.<br>Przed zakupem upewnij się, że cena druku na żądanie jest dla Ciebie odpowiednia.<br>Wszystkie nasze zasoby w wersji elektronicznej są zawsze dostępne bezpłatnie." %}">{% trans "Print on demand –" %}
+ <img src="{% static 'img/ridero.png' %}" style="height: 0.8em;"/></a>
{% endif %}
- {% if book.has_audio %}
- <span><a href="{% url 'download_zip_mp3' book.slug %}">MP3</a></span>
- {% endif %}
- <a class="read-more-show hide" href="#">{% trans "more" %}</a>
- <span class="read-more-content">
- {% if book.fb2_file %}
- <span><a href="{{ book.fb2_file.url}}">FB2</a></span>
+ </li>
+ <li class="book-box-download">
+ <a class="downarrow">{% trans "Download" %}:</a>
+ <div class="book-box-formats">
+ {% if book.pdf_file %}
+ <span><a href="{{ book.pdf_url}}">PDF</a></span>
{% endif %}
- {% if book.txt_file %}
- <span><a href="{{ book.txt_file.url}}">TXT</a></span>
+ {% if book.epub_file %}
+ <span><a href="{{ book.epub_url}}">EPUB</a></span>
{% endif %}
- {% download_audio book mp3=False %}
- <br>
- {% custom_pdf_link_li book %}
- <a class="read-more-hide hide" href="#">{% trans "less" %}</a>
- </span>
- </div>
- </li>
- </ul>
+ {% if book.mobi_file %}
+ <span><a href="{{ book.mobi_url}}">MOBI</a></span>
+ {% endif %}
+ {% if book.has_audio %}
+ <span><a href="{% url 'download_zip_mp3' book.slug %}">MP3</a></span>
+ {% endif %}
+ <a class="read-more-show hide" href="#">{% trans "more" %}</a>
+ <span class="read-more-content">
+ {% if book.fb2_file %}
+ <span><a href="{{ book.fb2_url}}">FB2</a></span>
+ {% endif %}
+ {% if book.txt_file %}
+ <span><a href="{{ book.txt_url}}">TXT</a></span>
+ {% endif %}
+ {% download_audio book mp3=False %}
+ <br>
+ {% custom_pdf_link_li book %}
+ <a class="read-more-hide hide" href="#">{% trans "less" %}</a>
+ </span>
+ </div>
+ </li>
+ </ul>
+ {% else %}
+ <p class="book-box-tools">{% trans "For now this work is only available for our subscribers." %}</p>
+ {% endif %}
- <div class="clearboth"></div>
- {% if book.abstract %}
- <div class="abstract more">
- {{ book.abstract|safe }}
- </div>
- {% endif %}
{% block book-box-extra-info %}{% endblock %}
- {% block box-append %}
- {% endblock %}
+ {% block box-append %}{% endblock %}
</div>
+ {% if book.abstract %}
+ <div class="abstract more">
+ {{ book.abstract|safe }}
+ </div>
+ {% endif %}
{% endwith %}
{% block right-column %}
url(r'^uzytkownik/signup/$', views.RegisterFormView(), name='register'),
url(r'^uzytkownik/logout/$', views.logout_then_redirect, name='logout'),
url(r'^uzytkownik/zaloguj-utworz/$', views.LoginRegisterFormView(), name='login_register'),
+ url(r'^uzytkownik/social/signup/$', views.SocialSignupView.as_view(), name='socialaccount_signup'),
# Includes.
url(r'^latests_blog_posts.html$', views.latest_blog_posts, name='latest_blog_posts'),
urlpatterns += [
url(r'^error-test/$', views.exception_test),
+ # url(r'^post-test/$', views.post_test),
]
#
from datetime import date, datetime
import feedparser
+from allauth.socialaccount.views import SignupView
from django.conf import settings
from django.contrib import auth
from django.contrib.auth.decorators import login_required
-from django.contrib.auth.forms import UserCreationForm, AuthenticationForm
+from django.contrib.auth.forms import AuthenticationForm
from django.core.cache import cache
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render
from ssify import ssi_included
from social.utils import get_or_choose_cite
-from wolnelektury.forms import RegistrationForm
+from wolnelektury.forms import RegistrationForm, SocialSignupForm
def main_page(request):
return render(request, 'widget.html')
+class SocialSignupView(SignupView):
+ form_class = SocialSignupForm
+
+
def exception_test(request):
msg = request.GET.get('msg')
if msg:
raise Exception('Exception test: %s' % msg)
else:
- raise Exception('Exception test')
+ raise Exception('Exception test')
+
+
+ def post_test(request):
+ return render(request, 'post_test.html', {'action': '/api/reading/jego-zasady/complete/'})