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():
"""
allowed_methods = ['GET']
fields = ['title', 'parent', 'children'] + Book.formats + [
- 'media', 'url', 'cover', 'cover_thumb', 'simple_thumb', 'simple_cover', 'fragment_data'] + [
+ 'media', 'url', 'cover', 'cover_thumb', 'simple_thumb', 'simple_cover', 'fragment_data', 'preview'] + [
category_plural[c] for c in book_tag_categories]
@piwik_track
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
EBOOK_FORMATS = EBOOK_FORMATS_WITHOUT_CHILDREN + EBOOK_FORMATS_WITH_CHILDREN
+EBOOK_CONTENT_TYPES = {
+ 'html': 'text/html',
+ 'pdf': 'application/pdf',
+ 'txt': 'text/plain',
+ 'epub': 'application/epub+zip',
+ 'mobi': 'application/x-mobipocket-ebook',
+ 'fb2': 'text/xml',
+ 'xml': 'text/xml',
+}
+
LANGUAGES_3TO2 = {
'deu': 'de',
'ger': 'de',
"""Builds the ebook in a delayed task."""
return self.field.builder.delay(self.instance, self.field.attname)
+ def get_url(self):
+ return self.instance.media_url(self.field.attname.split('_')[0])
+
+ def set_readable(self, readable):
+ import os
+ permissions = 0o644 if readable else 0o600
+ os.chmod(self.path, permissions)
+
class EbookField(models.FileField):
"""Represents an ebook file field, attachable to a model."""
obj.flush_includes()
return ret
+ def set_file_permissions(self, fieldfile):
+ if fieldfile.instance.preview:
+ fieldfile.set_readable(False)
+
def build(self, fieldfile):
book = fieldfile.instance
out = self.transform(book.wldocument(), fieldfile)
fieldfile.save(None, File(open(out.get_filename())), save=False)
+ self.set_file_permissions(fieldfile)
if book.pk is not None:
type(book).objects.filter(pk=book.pk).update(**{
fieldfile.field.attname: fieldfile
lang = None
fieldfile.save(None, ContentFile(html_output.get_string()), save=False)
+ self.set_file_permissions(fieldfile)
type(book).objects.filter(pk=book.pk).update(**{
fieldfile.field.attname: fieldfile
})
return wldoc.as_html(options={'gallery': "'%s'" % gallery})
+class BuildCover(BuildEbook):
+ def set_file_permissions(self, fieldfile):
+ pass
+
+
@BuildEbook.register('cover_thumb')
@task(ignore_result=True)
-class BuildCoverThumb(BuildEbook):
+class BuildCoverThumb(BuildCover):
@classmethod
def transform(cls, wldoc, fieldfile):
from librarian.cover import WLCover
@BuildEbook.register('cover_api_thumb')
@task(ignore_result=True)
-class BuildCoverApiThumb(BuildEbook):
+class BuildCoverApiThumb(BuildCover):
@classmethod
def transform(cls, wldoc, fieldfile):
from librarian.cover import WLNoBoxCover
@BuildEbook.register('simple_cover')
@task(ignore_result=True)
-class BuildSimpleCover(BuildEbook):
+class BuildSimpleCover(BuildCover):
@classmethod
def transform(cls, wldoc, fieldfile):
from librarian.cover import WLNoBoxCover
book_xml_file = forms.FileField(required=False)
book_xml = forms.CharField(required=False)
gallery_url = forms.CharField(required=False)
+ days = forms.IntegerField(required=False)
def clean(self):
from django.core.files.base import ContentFile
def save(self, **kwargs):
return Book.from_xml_file(self.cleaned_data['book_xml_file'], overwrite=True,
- remote_gallery_url=self.cleaned_data['gallery_url'], **kwargs)
+ remote_gallery_url=self.cleaned_data['gallery_url'],
+ days=self.cleaned_data['days'], **kwargs)
FORMATS = [(f, f.upper()) for f in Book.ebook_formats]
def save(self, *args, **kwargs):
if not self.cleaned_data['cust'] and self.book.pdf_file:
# Don't build with default options, just redirect to the standard file.
- return {"redirect": self.book.pdf_file.url}
+ return {"redirect": self.book.pdf_url()}
url = WaitedFile.order(
self.cleaned_data['path'],
lambda p, waiter_id: build_custom_pdf.delay(self.book.id, self.cleaned_data['cust'], p, waiter_id),
--- /dev/null
+# -*- coding: utf-8 -*-
+# This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+#
+from datetime import date
+from django.core.management.base import BaseCommand
+
+from catalogue.models import Book
+
+
+class Command(BaseCommand):
+ def handle(self, *args, **options):
+ for book in Book.objects.filter(preview=True, preview_until__lt=date.today()):
+ book.preview = False
+ book.save()
+ for format_ in Book.formats:
+ media_file = book.get_media(format_)
+ if media_file:
+ media_file.set_readable(True)
--- /dev/null
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('catalogue', '0023_book_abstract'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='book',
+ name='preview',
+ field=models.BooleanField(default=False, verbose_name='preview'),
+ ),
+ migrations.AddField(
+ model_name='book',
+ name='preview_until',
+ field=models.DateField(null=True, verbose_name='preview until', blank=True),
+ ),
+ ]
# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
#
from collections import OrderedDict
+from datetime import date
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)
+ preview = models.BooleanField(_('preview'), default=False)
+ preview_until = models.DateField(_('preview until'), blank=True, null=True)
# files generated during publication
cover = EbookField(
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')
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()
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
{% block bodyid %}book-detail{% endblock %}
{% block body %}
- {% cache 86400 book_wide book.pk %} {# book|status:user #}
+ {% cache 86400 book_wide book.pk book|status:user %}
{% include 'catalogue/book_wide.html' %}
{% endcache %}
{% trans "in" %} {% source_name extra_info.source_url %}
</div>
{% endif %}
- <div class="white-box"><a href="{{ book.xml_file.url }}">{% trans "Source XML file" %}</a></div>
+ {% if book|status:user != 'closed' %}
+ <div class="white-box"><a href="{{ book.xml_url }}">{% trans "Source XML file" %}</a></div>
+ {% endif %}
{% if extra_info.about and not hide_about %}
<div class="white-box">
{% trans "Book on" %} <a href="{{ extra_info.about }}">{% trans "Editor's Platform" %}</a>
</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">
{% block big-pane %}
<article id="main-text">
- <!--#include file='{{ book.html_file.url }}'-->
+ <!--#include file='{{ book.html_url }}'-->
</article>
<article id="other-text">
{% for other_version in book.other_versions %}
<li>
<a class="display-other"
- data-other="{{ other_version.html_file.url }}"
+ data-other="{{ other_version.html_url }}"
href="{% url 'book_text' other_version.slug %}">
{% cache 86400 book_mini_box other_version.pk %}
{% include 'catalogue/book_mini_box.html' with book=other_version no_link=True %}
</div>
<div class="box" id="book-short">
- {% cache 86400 catalogue_book_short book.pk %}
+ {% cache 86400 catalogue_book_short book.pk book|status:user %}
{% include 'catalogue/book_short.html' %}
{% endcache %}
</div>
from catalogue.helpers import get_audiobook_tags
from catalogue.models import Book, BookMedia, Fragment, Tag, Source
from catalogue.constants import LICENSES
+from catalogue.utils import is_subscribed
from picture.models import Picture
register = template.Library()
# docelowo może być warto zainstalować BeautifulSoup do takich rzeczy
import re
return re.sub(r"<.?%s\b[^>]*>" % tag_name, "", html)
+
+
+@register.filter
+def status(book, user):
+ if not book.preview:
+ return 'open'
+ elif is_subscribed(user):
+ return 'preview'
+ else:
+ return 'closed'
url(r'^audiobooki/(?P<type>mp3|ogg|daisy|all).xml$', AudiobookFeed(), name='audiobook_feed'),
+ url(r'^pobierz/(?P<slug>%s).(?P<format_>[a-z0-9]*)$' % SLUG, 'embargo_link', name='embargo_link'),
# zip
url(r'^zip/pdf\.zip$', 'download_zip', {'format': 'pdf', 'slug': None}, 'download_zip_pdf'),
def gallery_url(slug):
return '%s%s%s/' % (settings.MEDIA_URL, settings.IMAGE_DIR, slug)
+
+
+def is_subscribed(user):
+ return user.is_authenticated() # TEMPORARY
from catalogue import forms
from catalogue.helpers import get_top_level_related_tags
from catalogue.models import Book, Collection, Tag, Fragment
-from catalogue.utils import split_tags
+from catalogue.utils import split_tags, is_subscribed
from catalogue.models.tag import prefetch_relations
from wolnelektury.utils import is_crawler
def book_text(request, slug):
book = get_object_or_404(Book, slug=slug)
+ if book.preview and not is_subscribed(request.user):
+ return HttpResponseRedirect(book.get_absolute_url())
+
if not book.has_html_file():
raise Http404
return render_to_response('catalogue/book_text.html', {'book': book}, context_instance=RequestContext(request))
return HttpResponse(tag.description)
+def embargo_link(request, format_, slug):
+ book = get_object_or_404(Book, slug=slug)
+ if format_ not in Book.formats:
+ raise Http404
+ media_file = book.get_media(format_)
+ if not book.preview:
+ return HttpResponseRedirect(media_file.url)
+ if not is_subscribed(request.user):
+ return HttpResponseRedirect(book.get_absolute_url())
+ return HttpResponse(media_file, content_type=constants.EBOOK_CONTENT_TYPES[format_])
+
+
def download_zip(request, format, slug=None):
if format in Book.ebook_formats:
url = Book.zip_format(format)
"""Override to parse view args and give additional args to the form."""
return (obj,), {}
+ def validate_object(self, obj, request):
+ book = obj
+ if book.preview and not is_subscribed(request.user):
+ return HttpResponseRedirect(book.get_absolute_url())
+ return super(CustomPDFFormView, self).validate_object(obj, request)
+
def get_object(self, request, slug, *args, **kwargs):
- return get_object_or_404(Book, slug=slug)
+ book = get_object_or_404(Book, slug=slug)
+ return book
def context_description(self, request, obj):
return obj.pretty_title()