{% load i18n %}
+
<h1>{{ title }}</h1>
<form action="{{ request.get_full_path }}" method="post" accept-charset="utf-8"
class="cuteform{% if placeholdize %} hidelabels{% endif %}">
+{% csrf_token %}
+{% if honeypot %}
+ {% load honeypot %}
+ {% render_honeypot_field %}
+{% endif %}
<ol>
<div id="id_{% if form_prefix %}{{ form_prefix }}-{% endif %}__all__"></div>
{{ form.as_ul }}
from django.utils import simplejson
from django.utils.translation import ugettext_lazy as _
from django.views.decorators.vary import vary_on_headers
+from honeypot.decorators import verify_honeypot_value
class LazyEncoder(simplejson.JSONEncoder):
formname = "form"
form_prefix = None
full_template = "ajaxable/form_on_page.html"
+ honeypot = False
@method_decorator(vary_on_headers('X-Requested-With'))
def __call__(self, request, *args, **kwargs):
form_kwargs['prefix'] = self.form_prefix
if request.method == "POST":
+ if self.honeypot:
+ response = verify_honeypot_value(request, None)
+ if response:
+ return response
+
# do I need to be logged in?
if self.POST_login and not request.user.is_authenticated():
return require_login(request)
form = self.form_class(*form_args, **form_kwargs)
if form.is_valid():
add_args = self.success(form, request)
- redirect = request.GET.get('next')
- if not request.is_ajax() and redirect:
- return HttpResponseRedirect(urlquote_plus(
- redirect, safe='/?=&'))
- response_data = {'success': True,
- 'message': self.success_message, 'redirect': redirect}
+ response_data = {
+ 'success': True,
+ 'message': self.success_message,
+ 'redirect': request.GET.get('next')
+ }
if add_args:
response_data.update(add_args)
+ if not request.is_ajax() and response_data['redirect']:
+ return HttpResponseRedirect(urlquote_plus(
+ response_data['redirect'], safe='/?=&'))
elif request.is_ajax():
# Form was sent with errors. Send them back.
if self.form_prefix:
context = {
self.formname: form,
"title": title,
+ "honeypot": self.honeypot,
"placeholdize": self.placeholdize,
"submit": self.submit,
"response_data": response_data,
from django.utils.translation import ugettext_lazy as _
from catalogue.models import Book
+from waiter.models import WaitedFile
+from django.core.exceptions import ValidationError
+from catalogue.utils import get_customized_pdf_path
+from catalogue.tasks import build_custom_pdf
class BookImportForm(forms.Form):
super(DownloadFormatsForm, self).__init__(*args, **kwargs)
-PDF_PAGE_SIZES = (
- ('a4paper', _('A4')),
- ('a5paper', _('A5')),
-)
-
-
-PDF_LEADINGS = (
- ('', _('Normal leading')),
- ('onehalfleading', _('One and a half leading')),
- ('doubleleading', _('Double leading')),
+CUSTOMIZATION_FLAGS = (
+ ('nofootnotes', _("Don't show footnotes")),
+ ('nothemes', _("Don't disply themes")),
+ ('nowlfont', _("Don't use our custom font")),
)
-
-PDF_FONT_SIZES = (
- ('11pt', _('Default')),
- ('13pt', _('Big'))
+CUSTOMIZATION_OPTIONS = (
+ ('leading', _("Leading"), (
+ ('defaultleading', _('Normal leading')),
+ ('onehalfleading', _('One and a half leading')),
+ ('doubleleading', _('Double leading')),
+ )),
+ ('fontsize', _("Font size"), (
+ ('11pt', _('Default')),
+ ('13pt', _('Big'))
+ )),
+# ('pagesize', _("Paper size"), (
+# ('a4paper', _('A4')),
+# ('a5paper', _('A5')),
+# )),
)
class CustomPDFForm(forms.Form):
- nofootnotes = forms.BooleanField(required=False, label=_("Don't show footnotes"))
- nothemes = forms.BooleanField(required=False, label=_("Don't disply themes"))
- nowlfont = forms.BooleanField(required=False, label=_("Don't use our custom font"))
- ## pagesize = forms.ChoiceField(PDF_PAGE_SIZES, required=True, label=_("Paper size"))
- leading = forms.ChoiceField(PDF_LEADINGS, required=False, label=_("Leading"))
- fontsize = forms.ChoiceField(PDF_FONT_SIZES, required=True, label=_("Font size"))
+ def __init__(self, book, *args, **kwargs):
+ super(CustomPDFForm, self).__init__(*args, **kwargs)
+ self.book = book
+ for name, label in CUSTOMIZATION_FLAGS:
+ self.fields[name] = forms.BooleanField(required=False, label=label)
+ for name, label, choices in CUSTOMIZATION_OPTIONS:
+ self.fields[name] = forms.ChoiceField(choices, label=label)
+
+ def clean(self):
+ self.cleaned_data['cust'] = self.customizations
+ self.cleaned_data['path'] = get_customized_pdf_path(self.book,
+ self.cleaned_data['cust'])
+ if not WaitedFile.can_order(self.cleaned_data['path']):
+ raise ValidationError(_('Queue is full. Please try again later.'))
+ return self.cleaned_data
@property
def customizations(self):
c = []
- if self.cleaned_data['nofootnotes']:
- c.append('nofootnotes')
-
- if self.cleaned_data['nothemes']:
- c.append('nothemes')
-
- if self.cleaned_data['nowlfont']:
- c.append('nowlfont')
-
- ## c.append(self.cleaned_data['pagesize'])
- c.append(self.cleaned_data['fontsize'])
-
- if self.cleaned_data['leading']:
- c.append(self.cleaned_data['leading'])
-
+ for name, label in CUSTOMIZATION_FLAGS:
+ if self.cleaned_data.get(name):
+ c.append(name)
+ for name, label, choices in CUSTOMIZATION_OPTIONS:
+ c.append(self.cleaned_data[name])
c.sort()
-
return c
+ def save(self, *args, **kwargs):
+ url = WaitedFile.order(self.cleaned_data['path'],
+ lambda p: build_custom_pdf.delay(self.book.id,
+ self.cleaned_data['cust'], p),
+ self.book.pretty_title()
+ )
+ #return redirect(url)
+ return {"redirect": url}
from collections import namedtuple
from django.db import models
-from django.db.models import permalink, Q
+from django.db.models import permalink
import django.dispatch
from django.core.cache import get_cache
-from django.core.files.storage import DefaultStorage
from django.utils.translation import ugettext_lazy as _
from django.contrib.auth.models import User
from django.template.loader import render_to_string
from django.utils.safestring import mark_safe
from django.utils.translation import get_language
from django.core.urlresolvers import reverse
-from django.db.models.signals import post_save, m2m_changed, pre_delete, post_delete
+from django.db.models.signals import post_save, pre_delete, post_delete
import jsonfield
from django.conf import settings
from newtagging import managers
from catalogue.fields import JSONField, OverwritingFileField
from catalogue.utils import create_zip, split_tags, truncate_html_words
-from catalogue.tasks import touch_tag, index_book
-from shutil import copy
-from glob import glob
+from catalogue import tasks
import re
-from os import path
import search
return lambda *args: get_dynamic_path(*args, ext=ext, maxlen=maxlen)
-def customizations_hash(customizations):
- customizations.sort()
- return hash(tuple(customizations))
-
-
-def get_customized_pdf_path(book, customizations):
- """
- Returns a MEDIA_ROOT relative path for a customized pdf. The name will contain a hash of customization options.
- """
- h = customizations_hash(customizations)
- pdf_name = '%s-custom-%s' % (book.slug, h)
- pdf_file = get_dynamic_path(None, pdf_name, ext='pdf')
-
- return pdf_file
-
-
-def get_existing_customized_pdf(book):
- """
- Returns a list of paths to generated customized pdf of a book
- """
- pdf_glob = '%s-custom-' % (book.slug,)
- pdf_glob = get_dynamic_path(None, pdf_glob, ext='pdf')
- pdf_glob = re.sub(r"[.]([a-z0-9]+)$", "*.\\1", pdf_glob)
- return glob(path.join(settings.MEDIA_ROOT, pdf_glob))
-
-
class BookMedia(models.Model):
FileFormat = namedtuple("FileFormat", "name ext")
formats = SortedDict([
try:
old = BookMedia.objects.get(pk=self.pk)
- except BookMedia.DoesNotExist, e:
+ except BookMedia.DoesNotExist:
old = None
else:
# if name changed, change the file name, too
book_tag.save()
return book_tag
- def has_media(self, type):
- if type in Book.formats:
- return bool(getattr(self, "%s_file" % type))
+ def has_media(self, type_):
+ if type_ in Book.formats:
+ return bool(getattr(self, "%s_file" % type_))
else:
- return self.media.filter(type=type).exists()
+ return self.media.filter(type=type_).exists()
- def get_media(self, type):
- if self.has_media(type):
- if type in Book.formats:
- return getattr(self, "%s_file" % type)
+ def get_media(self, type_):
+ if self.has_media(type_):
+ if type_ in Book.formats:
+ return getattr(self, "%s_file" % type_)
else:
- return self.media.filter(type=type)
+ return self.media.filter(type=type_)
else:
return None
cover.save(imgstr, 'png')
self.cover.save(None, ContentFile(imgstr.getvalue()))
- def build_pdf(self, customizations=None, file_name=None):
- """ (Re)builds the pdf file.
- customizations - customizations which are passed to LaTeX class file.
- file_name - save the pdf file under a different name and DO NOT save it in db.
- """
- from os import unlink
- from django.core.files import File
- from catalogue.utils import remove_zip
-
- pdf = self.wldocument().as_pdf(customizations=customizations)
-
- if file_name is None:
- # we'd like to be sure not to overwrite changes happening while
- # (timely) pdf generation is taking place (async celery scenario)
- current_self = Book.objects.get(id=self.id)
- current_self.pdf_file.save('%s.pdf' % self.slug,
- File(open(pdf.get_filename())))
- self.pdf_file = current_self.pdf_file
-
- # remove cached downloadables
- remove_zip(settings.ALL_PDF_ZIP)
-
- for customized_pdf in get_existing_customized_pdf(self):
- unlink(customized_pdf)
- else:
- print "saving %s" % file_name
- print "to: %s" % DefaultStorage().path(file_name)
- DefaultStorage().save(file_name, File(open(pdf.get_filename())))
-
- def build_mobi(self):
- """ (Re)builds the MOBI file.
-
- """
- from django.core.files import File
- from catalogue.utils import remove_zip
-
- mobi = self.wldocument().as_mobi()
-
- self.mobi_file.save('%s.mobi' % self.slug, File(open(mobi.get_filename())))
-
- # remove zip with all mobi files
- remove_zip(settings.ALL_MOBI_ZIP)
-
- def build_epub(self):
- """(Re)builds the epub file."""
- from django.core.files import File
- from catalogue.utils import remove_zip
-
- epub = self.wldocument().as_epub()
-
- self.epub_file.save('%s.epub' % self.slug,
- File(open(epub.get_filename())))
-
- # remove zip package with all epub files
- remove_zip(settings.ALL_EPUB_ZIP)
-
- def build_txt(self):
- from django.core.files.base import ContentFile
-
- text = self.wldocument().as_text()
- self.txt_file.save('%s.txt' % self.slug, ContentFile(text.get_string()))
-
-
def build_html(self):
from django.core.files.base import ContentFile
from slughifi import slughifi
return True
return False
+ # Thin wrappers for builder tasks
+ def build_pdf(self, *args, **kwargs):
+ return tasks.build_pdf.delay(self.pk, *args, **kwargs)
+ def build_epub(self, *args, **kwargs):
+ return tasks.build_epub.delay(self.pk, *args, **kwargs)
+ def build_mobi(self, *args, **kwargs):
+ return tasks.build_mobi.delay(self.pk, *args, **kwargs)
+ def build_txt(self, *args, **kwargs):
+ return tasks.build_txt.delay(self.pk, *args, **kwargs)
+
@staticmethod
def zip_format(format_):
def pretty_file_name(book):
books = Book.objects.filter(parent=None).exclude(**{field_name: ""})
paths = [(pretty_file_name(b), getattr(b, field_name).path)
for b in books]
- result = create_zip.delay(paths,
+ return create_zip(paths,
getattr(settings, "ALL_%s_ZIP" % format_.upper()))
- return result.wait()
def zip_audiobooks(self, format_):
bm = BookMedia.objects.filter(book=self, type=format_)
paths = map(lambda bm: (None, bm.file.path), bm)
- result = create_zip.delay(paths, "%s_%s" % (self.slug, format_))
- return result.wait()
+ return create_zip(paths, "%s_%s" % (self.slug, format_))
def search_index(self, book_info=None, reuse_index=False, index_tags=True):
if reuse_index:
def from_text_and_meta(cls, raw_file, book_info, overwrite=False,
build_epub=True, build_txt=True, build_pdf=True, build_mobi=True,
search_index=True, search_index_tags=True, search_index_reuse=False):
- import re
- from sortify import sortify
# check for parts before we do anything
children = []
for part_url in book_info.parts:
try:
children.append(Book.objects.get(slug=part_url.slug))
- except Book.DoesNotExist, e:
+ except Book.DoesNotExist:
raise Book.DoesNotExist(_('Book "%s" does not exist.') %
part_url.slug)
book_descendants += list(child_book.children.all())
for tag in descendants_tags:
- touch_tag(tag)
+ tasks.touch_tag(tag)
book.save()
type(self).objects.filter(pk=self.pk).update(_related_info=rel)
return rel
+ def related_themes(self):
+ theme_counter = self.theme_counter
+ book_themes = Tag.objects.filter(pk__in=theme_counter.keys())
+ for tag in book_themes:
+ tag.count = theme_counter[tag.pk]
+ return book_themes
+
def reset_tag_counter(self):
if self.id is None:
return
# reset tag global counter
# we want Tag.changed_at updated for API to know the tag was touched
for tag in affected_tags:
- touch_tag(tag)
+ tasks.touch_tag(tag)
# if book tags changed, reset book tag counter
if isinstance(sender, Book) and \
# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
#
from datetime import datetime
-from celery.task import task
-import catalogue.models
from traceback import print_exc
+from celery.task import task
+from django.conf import settings
-@task
+
+# TODO: move to model?
def touch_tag(tag):
update_dict = {
'book_count': tag.get_count(),
@task
def index_book(book_id, book_info=None):
+ from catalogue.models import Book
try:
- return catalogue.models.Book.objects.get(id=book_id).search_index(book_info)
+ return Book.objects.get(id=book_id).search_index(book_info)
except Exception, e:
print "Exception during index: %s" % e
print_exc()
raise e
+
+
+@task(ignore_result=True)
+def build_txt(book_id):
+ """(Re)builds the TXT file for a book."""
+ from django.core.files.base import ContentFile
+ from catalogue.models import Book
+
+ text = Book.objects.get(pk=book_id).wldocument().as_text()
+
+ # Save the file in new instance. Building TXT takes time and we don't want
+ # to overwrite any interim changes.
+ book = Book.objects.get(id=book_id)
+ book.txt_file.save('%s.txt' % book.slug, ContentFile(text.get_string()))
+
+
+@task(ignore_result=True, rate_limit=settings.CATALOGUE_PDF_RATE_LIMIT)
+def build_pdf(book_id):
+ """(Re)builds the pdf file for a book."""
+ from django.core.files import File
+ from catalogue.models import Book
+ from catalogue.utils import remove_zip
+ from waiter.utils import clear_cache
+
+ pdf = Book.objects.get(pk=book_id).wldocument().as_pdf(
+ morefloats=settings.LIBRARIAN_PDF_MOREFLOATS)
+
+ # Save the file in new instance. Building PDF takes time and we don't want
+ # to overwrite any interim changes.
+ book = Book.objects.get(id=book_id)
+ book.pdf_file.save('%s.pdf' % book.slug,
+ File(open(pdf.get_filename())))
+
+ # Remove cached downloadables
+ remove_zip(settings.ALL_PDF_ZIP)
+ clear_cache(book.slug)
+
+
+@task(ignore_result=True, rate_limit=settings.CATALOGUE_EPUB_RATE_LIMIT)
+def build_epub(book_id):
+ """(Re)builds the EPUB file for a book."""
+ from django.core.files import File
+ from catalogue.models import Book
+ from catalogue.utils import remove_zip
+
+ epub = Book.objects.get(pk=book_id).wldocument().as_epub()
+ # Save the file in new instance. Building MOBI takes time and we don't want
+ # to overwrite any interim changes.
+ book = Book.objects.get(id=book_id)
+ book.epub_file.save('%s.epub' % book.slug,
+ File(open(epub.get_filename())))
+
+ # remove zip with all epub files
+ remove_zip(settings.ALL_EPUB_ZIP)
+
+
+@task(ignore_result=True, rate_limit=settings.CATALOGUE_MOBI_RATE_LIMIT)
+def build_mobi(book_id):
+ """(Re)builds the MOBI file for a book."""
+ from django.core.files import File
+ from catalogue.models import Book
+ from catalogue.utils import remove_zip
+
+ mobi = Book.objects.get(pk=book_id).wldocument().as_mobi()
+ # Save the file in new instance. Building MOBI takes time and we don't want
+ # to overwrite any interim changes.
+ book = Book.objects.get(id=book_id)
+ book.mobi_file.save('%s.mobi' % book.slug,
+ File(open(mobi.get_filename())))
+
+ # remove zip with all mobi files
+ remove_zip(settings.ALL_MOBI_ZIP)
+
+
+@task(rate_limit=settings.CATALOGUE_CUSTOMPDF_RATE_LIMIT)
+def build_custom_pdf(book_id, customizations, file_name):
+ """Builds a custom PDF file."""
+ from django.core.files import File
+ from django.core.files.storage import DefaultStorage
+ from catalogue.models import Book
+
+ print "will gen %s" % DefaultStorage().path(file_name)
+ if not DefaultStorage().exists(file_name):
+ pdf = Book.objects.get(pk=book_id).wldocument().as_pdf(
+ customizations=customizations,
+ morefloats=settings.LIBRARIAN_PDF_MOREFLOATS)
+ DefaultStorage().save(file_name, File(open(pdf.get_filename())))
</div>
<div class="if-unlike">
<form id="social-like-book-{{ book.slug }}" data-callback='social-like-book' method='post' class='ajax-form' action='{% url social_like_book book.slug %}'>
+ {% csrf_token %}
<button type='submit'>☆</button>
</form>
</div>
</ul>
</div>
<div class="other-download">
- {% if related.media.mp3 or related.media.ogg %}
<h2 class="mono">{% trans "Download" %}</h2>
<ul class="plain">
<li>
{% if related.media.ogg %}<a href="{% url download_zip_ogg book.slug %}">OGG</a>{% endif %}.
{% endif %}
</li>
- {% comment %}
<li>
- <a href="{% url custom_pdf_form %}?slug={{book.slug}}" id="custom-pdf" class="ajaxable">{% trans "Download a custom PDF" %}</a>
+ <a href="{% url custom_pdf_form book.slug %}" id="custom-pdf" class="ajaxable">{% trans "Download a custom PDF" %}</a>
</li>
- {% endcomment %}
</ul>
- {% endif %}
</div>
</div>
{% endblock %}
@register.inclusion_tag('catalogue/book_wide.html', takes_context=True)
def book_wide(context, book):
- theme_counter = book.theme_counter
- book_themes = Tag.objects.filter(pk__in=theme_counter.keys())
- for tag in book_themes:
- tag.count = theme_counter[tag.pk]
+ book_themes = book.related_themes()
extra_info = book.get_extra_info_value()
hide_about = extra_info.get('about', '').startswith('http://wiki.wolnepodreczniki.pl')
'extra_info': book.get_extra_info_value(),
'hide_about': hide_about,
'themes': book_themes,
- 'custom_pdf_form': forms.CustomPDFForm(),
'request': context.get('request'),
}
from librarian import WLURI
from nose.tools import raises
-import tempfile
-from os import unlink, path, makedirs
+from os import path, makedirs
class BookImportLogicTests(WLTestCase):
</opowiadanie></utwor>
"""
child = models.Book.from_text_and_meta(ContentFile(CHILD_TEXT), self.child_info, overwrite=True)
-
- themes = self.client.get(parent.get_absolute_url()).context['book_themes']
-
+ themes = parent.related_themes()
self.assertEqual(['Kot'], [tag.name for tag in themes],
'wrong related theme list')
class BookImportGenerateTest(WLTestCase):
def setUp(self):
WLTestCase.setUp(self)
- input = path.join(path.dirname(__file__), 'files/fraszka-do-anusie.xml')
- self.book = models.Book.from_xml_file(input)
+ xml = path.join(path.dirname(__file__), 'files/fraszka-do-anusie.xml')
+ self.book = models.Book.from_xml_file(xml)
def test_gen_pdf(self):
self.book.build_pdf()
- self.assertTrue(path.exists(self.book.pdf_file.path))
+ book = models.Book.objects.get(pk=self.book.pk)
+ self.assertTrue(path.exists(book.pdf_file.path))
def test_gen_pdf_parent(self):
"""This book contains a child."""
- input = path.join(path.dirname(__file__), "files/fraszki.xml")
- parent = models.Book.from_xml_file(input)
+ xml = path.join(path.dirname(__file__), "files/fraszki.xml")
+ parent = models.Book.from_xml_file(xml)
parent.build_pdf()
+ parent = models.Book.objects.get(pk=parent.pk)
self.assertTrue(path.exists(parent.pdf_file.path))
def test_custom_pdf(self):
+ from catalogue.tasks import build_custom_pdf
out = models.get_dynamic_path(None, 'test-custom', ext='pdf')
absoulute_path = path.join(settings.MEDIA_ROOT, out)
if not path.exists(path.dirname(absoulute_path)):
makedirs(path.dirname(absoulute_path))
- self.book.build_pdf(customizations=['nofootnotes', '13pt', 'a4paper'], file_name=out)
+ build_custom_pdf(self.book.id,
+ customizations=['nofootnotes', '13pt', 'a4paper'], file_name=out)
self.assertTrue(path.exists(absoulute_path))
# -*- coding: utf-8 -*-
+from django.core.files.base import ContentFile
+from django.test import Client
from catalogue import models
from catalogue.test_utils import *
-from django.core.files.base import ContentFile
+
class BooksByTagTests(WLTestCase):
""" tests the /katalog/category/tag page for found books """
['Child'])
-from django.test import Client
class TagRelatedTagsTests(WLTestCase):
""" tests the /katalog/category/tag/ page for related tags """
<end id="m01" />
</akap></opowiadanie></utwor>
"""
- book = models.Book.from_text_and_meta(ContentFile(book_text), book_info)
+ self.book = models.Book.from_text_and_meta(ContentFile(book_text), book_info)
def test_delete_objects(self):
""" there should be no related tags left after deleting some objects """
""" there should be no tag relations left after deleting tags """
models.Tag.objects.all().delete()
- cats = self.client.get('/katalog/lektura/book/').context['categories']
- self.assertEqual(cats, {})
+ self.assertEqual(len(self.book.related_info()['tags']), 0)
+ self.assertEqual(len(self.book.related_themes()), 0)
self.assertEqual(models.Tag.intermediary_table_model.objects.all().count(), 0,
"orphaned TagRelation objects left")
def test_book_tags(self):
""" there should be all related tags in relevant categories """
- models.Book.from_text_and_meta(ContentFile(self.book_text), self.book_info)
+ book = models.Book.from_text_and_meta(ContentFile(self.book_text), self.book_info)
- context = self.client.get('/katalog/lektura/tag/').context
+ related_info = book.related_info()
+ related_themes = book.related_themes()
for category in 'author', 'kind', 'genre', 'epoch':
- self.assertTrue('tag' in [tag.slug for tag in context['categories'][category]],
+ self.assertTrue('tag' in [tag[1] for tag in related_info['tags'][category]],
'missing related tag for %s' % category)
- self.assertTrue('tag' in [tag.slug for tag in context['book_themes']])
+ self.assertTrue('tag' in [tag.slug for tag in related_themes])
def test_qualified_url(self):
models.Book.from_text_and_meta(ContentFile(self.book_text), self.book_info)
<end id="m01" />
</akap></opowiadanie></utwor>
""" % info.title.encode('utf-8')
- book = models.Book.from_text_and_meta(ContentFile(book_text), info)
+ models.Book.from_text_and_meta(ContentFile(book_text), info)
def test_book_tags(self):
""" book should have own tags and whole tree's themes """
- context = self.client.get('/katalog/lektura/parent/').context
+ book = models.Book.objects.get(slug='parent')
+ related_info = book.related_info()
+ related_themes = book.related_themes()
- self.assertEqual([tag.name for tag in context['categories']['author']],
- ['Common Man'])
- self.assertEqual([tag.name for tag in context['categories']['kind']],
- ['Kind'])
- self.assertEqual([(tag.name, tag.count) for tag in context['book_themes']],
+ self.assertEqual(related_info['tags']['author'],
+ [('Common Man', 'common-man')])
+ self.assertEqual(related_info['tags']['kind'],
+ [('Kind', 'kind')])
+ self.assertEqual([(tag.name, tag.count) for tag in related_themes],
[('ChildTheme', 1), ('ParentTheme', 1), ('Theme', 2)])
def test_main_page_tags(self):
""" test main page tags and counts """
-
- context = self.client.get('/katalog/').context
-
- self.assertEqual([(tag.name, tag.count) for tag in context['categories']['author']],
+ from catalogue.templatetags.catalogue_tags import catalogue_menu
+ menu = catalogue_menu()
+ self.assertEqual([(tag.name, tag.book_count) for tag in menu['author']],
[('Jim Lazy', 1), ('Common Man', 1)])
- self.assertEqual([(tag.name, tag.count) for tag in context['fragment_tags']],
+ self.assertEqual([(tag.name, tag.book_count) for tag in menu['theme']],
[('ChildTheme', 1), ('ParentTheme', 1), ('Theme', 2)])
url(r'^jtags/$', 'json_tags_starting_with', name='jhint'),
#url(r'^szukaj/$', 'search', name='old_search'),
+ url(r'^custompdf/(?P<slug>%s)/$' % SLUG, CustomPDFFormView(), name='custom_pdf_form'),
+
+ url(r'^audiobooki/(?P<type>mp3|ogg|daisy|all).xml$', AudiobookFeed(), name='audiobook_feed'),
+
+
# zip
url(r'^zip/pdf\.zip$', 'download_zip', {'format': 'pdf', 'slug': None}, 'download_zip_pdf'),
url(r'^zip/epub\.zip$', 'download_zip', {'format': 'epub', 'slug': None}, 'download_zip_epub'),
url(r'^lektura/(?P<slug>%s)/motyw/(?P<theme_slug>[a-zA-Z0-9-]+)/$' % SLUG,
'book_fragments', name='book_fragments'),
+ # This should be the last pattern.
url(r'^(?P<tags>[a-zA-Z0-9-/]*)/$', 'tagged_object_list', name='tagged_object_list'),
-
- url(r'^audiobooki/(?P<type>mp3|ogg|daisy|all).xml$', AudiobookFeed(), name='audiobook_feed'),
-
- url(r'^custompdf$', CustomPDFFormView(), name='custom_pdf_form'),
- url(r'^custompdf/(?P<slug>%s).pdf' % SLUG, 'download_custom_pdf'),
-
)
import time
from base64 import urlsafe_b64encode
-from django.http import HttpResponse, HttpResponseRedirect, Http404, HttpResponsePermanentRedirect
+from django.http import HttpResponse
from django.core.files.uploadedfile import UploadedFile
-from django.core.files.base import File
from django.core.files.storage import DefaultStorage
from django.utils.encoding import force_unicode
from django.utils.hashcompat import sha_constructor
from django.conf import settings
-from celery.task import task
from os import mkdir, path, unlink
from errno import EEXIST, ENOENT
from fcntl import flock, LOCK_EX
from zipfile import ZipFile
-from traceback import print_exc
from reporting.utils import read_chunks
-from celery.task import task
-import catalogue.models
# Use the system (hardware-based) random number generator if it exists.
if hasattr(random, 'SystemRandom'):
self.lock.close()
-@task
+#@task
def create_zip(paths, zip_slug):
"""
Creates a zip in MEDIA_ROOT/zip directory containing files from path.
for chunk in read_chunks(f):
self.write(chunk)
-@task
-def async_build_pdf(book_id, customizations, file_name):
- """
- A celery task to generate pdf files.
- Accepts the same args as Book.build_pdf, but with book id as first parameter
- instead of Book instance
- """
- try:
- book = catalogue.models.Book.objects.get(id=book_id)
- print "will gen %s" % DefaultStorage().path(file_name)
- if not DefaultStorage().exists(file_name):
- book.build_pdf(customizations=customizations, file_name=file_name)
- print "done."
- except Exception, e:
- print "Error during pdf creation: %s" % e
- print_exc
- raise e
-
-
class MultiQuerySet(object):
def __init__(self, *args, **kwargs):
self.querysets = args
out += '</%s>' % tag
# Return string
return out
+
+
+def customizations_hash(customizations):
+ customizations.sort()
+ return hash(tuple(customizations))
+
+
+def get_customized_pdf_path(book, customizations):
+ """
+ Returns a MEDIA_ROOT relative path for a customized pdf. The name will contain a hash of customization options.
+ """
+ h = customizations_hash(customizations)
+ return 'book/%s/%s-custom-%s.pdf' % (book.slug, book.slug, h)
+
+
+def clear_custom_pdf(book):
+ """
+ Returns a list of paths to generated customized pdf of a book
+ """
+ from waiter.utils import clear_cache
+ clear_cache('book/%s' % book.slug)
from django.conf import settings
from django.template import RequestContext
-from django.shortcuts import render_to_response, get_object_or_404
+from django.shortcuts import render_to_response, get_object_or_404, redirect
from django.http import HttpResponse, HttpResponseRedirect, Http404, HttpResponsePermanentRedirect
from django.core.urlresolvers import reverse
from django.db.models import Q
from catalogue import models
from catalogue import forms
-from catalogue.utils import (split_tags, AttachmentHttpResponse,
- async_build_pdf, MultiQuerySet)
+from catalogue.utils import split_tags, MultiQuerySet
from pdcounter import models as pdcounter_models
from pdcounter import views as pdcounter_views
from suggest.forms import PublishingSuggestForm
from picture.models import Picture
-from os import path
-
staff_required = user_passes_test(lambda user: user.is_staff)
return HttpResponseRedirect(urlquote_plus(settings.MEDIA_URL + url, safe='/?='))
-def download_custom_pdf(request, slug, method='GET'):
- book = get_object_or_404(models.Book, slug=slug)
-
- if request.method == method:
- form = forms.CustomPDFForm(method == 'GET' and request.GET or request.POST)
- if form.is_valid():
- cust = form.customizations
- pdf_file = models.get_customized_pdf_path(book, cust)
-
- if not path.exists(pdf_file):
- result = async_build_pdf.delay(book.id, cust, pdf_file)
- result.wait()
- return AttachmentHttpResponse(file_name=("%s.pdf" % book.slug), file_path=pdf_file, mimetype="application/pdf")
- else:
- raise Http404(_('Incorrect customization options for PDF'))
- else:
- raise Http404(_('Bad method'))
-
-
class CustomPDFFormView(AjaxableFormView):
form_class = forms.CustomPDFForm
title = ugettext_lazy('Download custom PDF')
submit = ugettext_lazy('Download')
+ honeypot = True
- def __call__(self, request):
- from copy import copy
- if request.method == 'POST':
- request.GET = copy(request.GET)
- request.GET['next'] = "%s?%s" % (reverse('catalogue.views.download_custom_pdf', args=[request.GET.get('slug')]),
- request.POST.urlencode())
- return super(CustomPDFFormView, self).__call__(request)
+ def form_args(self, request, obj):
+ """Override to parse view args and give additional args to the form."""
+ return (obj,), {}
- def get_object(self, request):
- return get_object_or_404(models.Book, slug=request.GET.get('slug'))
+ def get_object(self, request, slug, *args, **kwargs):
+ return get_object_or_404(models.Book, slug=slug)
def context_description(self, request, obj):
return obj.pretty_title()
-
- def success(self, *args):
- pass
# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
#
from django.db import models
-
+from celery.task import task
from sortify import sortify
from catalogue.models import Book
ordering = ['sort_key']
-def notes_from_book(sender, **kwargs):
- from librarian import html
-
- Note.objects.filter(book=sender).delete()
- if sender.html_file:
- for anchor, text_str, html_str in html.extract_annotations(sender.html_file.path):
- Note.objects.create(book=sender, anchor=anchor,
+@task(ignore_result=True)
+def build_notes(book_id):
+ book = Book.objects.get(pk=book_id)
+ Note.objects.filter(book=book).delete()
+ if book.html_file:
+ from librarian import html
+ for anchor, text_str, html_str in html.extract_annotations(book.html_file.path):
+ Note.objects.create(book=book, anchor=anchor,
html=html_str,
sort_key=sortify(text_str).strip()[:128])
-
-# always re-extract notes after making a HTML in a Book
-Book.html_built.connect(notes_from_book)
+
+@Book.html_built.connect
+def notes_from_book(sender, **kwargs):
+ build_notes.delat(sender)
# -*- coding: utf-8 -*-
from __future__ import with_statement
-from django.core.files.base import ContentFile, File
-from catalogue.test_utils import *
-from catalogue import models
-from librarian import WLURI
+from os import path
+from django.test import TestCase
from picture.models import Picture
-from nose.tools import raises
-import tempfile
-from os import unlink, path, makedirs
-
class PictureTest(TestCase):
<form action="{% url social_unlike_book view_kwargs.slug %}" method="post" accept-charset="utf-8"
class="cuteform{% if placeholdize %} hidelabels{% endif %}">
+{% csrf_token %}
<input type="submit" value="{% trans "Remove from my shelf" %}"/>
</form>
<form action="{{ request.get_full_path }}" method="post" accept-charset="utf-8"
class="cuteform{% if placeholdize %} hidelabels{% endif %}">
+{% csrf_token %}
<ol>
<div id="id___all__"></div>
{{ form.as_ul }}
{% load i18n %}
+{% load honeypot %}
+
<h1>{% trans "Didn't find a book? Make a suggestion." %}</h1>
<form id='suggest-publishing-form' action="{% url suggest_publishing %}" method="post" accept-charset="utf-8" class="cuteform">
{% csrf_token %}
+{% render_honeypot_field %}
<ol>
<li><span class="error">{{ form.contact.errors }}</span><label for="id_contact">{{ form.contact.label }}</label> {{ form.contact }}</li>
from ajaxable.utils import AjaxableFormView
from suggest import forms
-from suggest.models import Suggestion, PublishingSuggestion
class PublishingSuggestionFormView(AjaxableFormView):
title = _('Report a bug or suggestion')
template = "publishing_suggest.html"
success_message = _('Report was sent successfully.')
+ honeypot = True
class SuggestionFormView(AjaxableFormView):
title = _('Report a bug or suggestion')
submit = _('Send report')
success_message = _('Report was sent successfully.')
+ honeypot = True
--- /dev/null
+"""
+Celery waiter.
+
+Takes orders for files generated by async Celery tasks.
+Serves the file when ready. Kindly asks the user to wait if not.
+
+Author: Radek Czajka <radoslaw.czajka@nowoczesnapolska.org.pl>
+"""
\ No newline at end of file
--- /dev/null
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2012-03-22 15:54+0100\n"
+"PO-Revision-Date: 2012-03-22 15:54+0100\n"
+"Last-Translator: Radek Czajka <radoslaw.czajka@nowoczesnapolska.org.pl>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n"
+
+#: templates/waiter/wait.html:7
+#: templates/waiter/wait.html.py:33
+msgid "The file is ready for download!"
+msgstr "Plik jest gotowy do pobrania!"
+
+#: templates/waiter/wait.html:10
+#: templates/waiter/wait.html.py:42
+msgid "Your file is being prepared, please wait."
+msgstr "Plik jest generowany, proszę czekać."
+
+#: templates/waiter/wait.html:12
+#: templates/waiter/wait.html.py:51
+msgid "Something went wrong."
+msgstr "Coś poszło nie tak."
+
+#: templates/waiter/wait.html:36
+#, python-format
+msgid ""
+"Your file is ready!\n"
+" If the download doesn't start in a few seconds,\n"
+" feel free to use this <a href=\"%(file_url)s\">direct link</a>."
+msgstr ""
+"Twój plik jest gotowy!\n"
+"Jeśli pobieranie nie zacznie się w ciągu kilku sekund,\n"
+"skorzystaj z tego <a href=\"%(file_url)s\">bezpośredniego linku</a>."
+
+#: templates/waiter/wait.html:45
+#, python-format
+msgid "The file you requested was: <em>%(d)s</em>."
+msgstr "Zamówiony plik to: <em>%(d)s</em>."
+
+#: templates/waiter/wait.html:47
+msgid ""
+"<strong>Be aware:</strong> Generating the file can take a while.\n"
+" Please be patient, or bookmark this page and come back later.</p>"
+msgstr ""
+"<strong>Uwaga:</strong> Generowanie pliku może trwać dłuższą chwilę.\n"
+"Poczekaj cierpliwie, albo dodaj tę stronę do zakładek i wróć później.</p>"
+
+#: templates/waiter/wait.html:55
+#, python-format
+msgid ""
+"Something seems to have gone wrong while generating your file.\n"
+" Please order it again or <a id='suggest' class='ajaxable' href=\"%(s)s\">complain to us</a> about it."
+msgstr ""
+"Wygląda na to, że coś poszło źle podczas generowania Twojego pliku.\n"
+"Spróbuj zamówić go jeszcze raz albo <a id='suggest' class='ajaxable' href=\"%(s)s\">napisz do nas</a>."
+
--- /dev/null
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+
+ # Adding model 'WaitedFile'
+ db.create_table('waiter_waitedfile', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('path', self.gf('django.db.models.fields.CharField')(unique=True, max_length=255, db_index=True)),
+ ('task_id', self.gf('django.db.models.fields.CharField')(db_index=True, max_length=128, null=True, blank=True)),
+ ('task', self.gf('picklefield.fields.PickledObjectField')(null=True)),
+ ('description', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
+ ))
+ db.send_create_signal('waiter', ['WaitedFile'])
+
+
+ def backwards(self, orm):
+
+ # Deleting model 'WaitedFile'
+ db.delete_table('waiter_waitedfile')
+
+
+ models = {
+ 'waiter.waitedfile': {
+ 'Meta': {'object_name': 'WaitedFile'},
+ 'description': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'path': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'}),
+ 'task': ('picklefield.fields.PickledObjectField', [], {'null': 'True'}),
+ 'task_id': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '128', 'null': 'True', 'blank': 'True'})
+ }
+ }
+
+ complete_apps = ['waiter']
--- /dev/null
+from os.path import join, isfile
+from django.core.urlresolvers import reverse
+from django.db import models
+from waiter.settings import WAITER_URL, WAITER_MAX_QUEUE
+from waiter.utils import check_abspath
+from picklefield import PickledObjectField
+
+
+class WaitedFile(models.Model):
+ path = models.CharField(max_length=255, unique=True, db_index=True)
+ task_id = models.CharField(max_length=128, db_index=True, null=True, blank=True)
+ task = PickledObjectField(null=True, editable=False)
+ description = models.CharField(max_length=255, null=True, blank=True)
+
+ @classmethod
+ def exists(cls, path):
+ """Returns opened file or None.
+
+ `path` is relative to WAITER_ROOT.
+ Won't open a path leading outside of WAITER_ROOT.
+ """
+ abs_path = check_abspath(path)
+ # Pre-fetch objects for deletion to avoid minor race condition
+ relevant = [o.id for o in cls.objects.filter(path=path)]
+ if isfile(abs_path):
+ cls.objects.filter(id__in=relevant).delete()
+ return True
+ else:
+ return False
+
+ @classmethod
+ def can_order(cls, path):
+ return (cls.objects.filter(path=path).exists() or
+ cls.exists(path) or
+ cls.objects.count() < WAITER_MAX_QUEUE
+ )
+
+ def is_stale(self):
+ if self.task is None:
+ # Race; just let the other task roll.
+ return False
+ if self.task.status not in (u'PENDING', u'STARTED', u'SUCCESS', u'RETRY'):
+ return True
+ return False
+
+ @classmethod
+ def order(cls, path, task_creator, description=None):
+ """
+ Returns an URL for the user to follow.
+ If the file is ready, returns download URL.
+ If not, starts preparing it and returns waiting URL.
+
+ task_creator: function taking a path and generating the file;
+ description: a string or string proxy with a description for user;
+ """
+ already = cls.exists(path)
+ if not already:
+ waited, created = cls.objects.get_or_create(path=path)
+ if created or waited.is_stale():
+ waited.task = task_creator(check_abspath(path))
+ waited.task_id = waited.task.task_id
+ waited.description = description
+ waited.save()
+ return reverse("waiter", args=[path])
+ return join(WAITER_URL, path)
--- /dev/null
+from os.path import join
+from django.conf import settings
+
+try:
+ WAITER_ROOT = settings.WAITER_ROOT
+except AttributeError:
+ WAITER_ROOT = join(settings.MEDIA_ROOT, 'waiter')
+
+try:
+ WAITER_URL = settings.WAITER_URL
+except AttributeError:
+ WAITER_URL = join(settings.MEDIA_URL, 'waiter')
+
+try:
+ WAITER_MAX_QUEUE = settings.WAITER_MAX_QUEUE
+except AttributeError:
+ WAITER_MAX_QUEUE = 20
+
--- /dev/null
+from celery.signals import task_postrun
+from waiter.models import WaitedFile
+
+
+def task_delete_after(task_id=None, **kwargs):
+ WaitedFile.objects.filter(task_id=task_id).delete()
+task_postrun.connect(task_delete_after)
--- /dev/null
+{% extends "base.html" %}
+{% load i18n %}
+{% load url from future %}
+
+{% block titleextra %}
+{% if file_url %}
+ {% trans "The file is ready for download!" %}
+{% else %}
+ {% if waiting %}
+ {% trans "Your file is being prepared, please wait." %}
+ {% else %}
+ {% trans "Something went wrong." %}
+ {% endif %}
+{% endif %}
+{% endblock %}
+
+
+{% block extrahead %}
+{% if file_url %}
+ <meta http-equiv="refresh" content="3; url={{ file_url }}" />
+{% else %}
+ {% if waiting %}
+ <noscript>
+ <meta http-equiv="refresh" content="10" />
+ </noscript>
+ {% endif %}
+{% endif %}
+{% endblock %}
+
+
+{% block body %}
+{% if file_url %}
+ <h1>{% trans "The file is ready for download!" %}</h1>
+
+ <div class="normal-text white-box">
+ <p>{% blocktrans %}Your file is ready!
+ If the download doesn't start in a few seconds,
+ feel free to use this <a href="{{ file_url }}">direct link</a>.{% endblocktrans %}</p>
+ </div>
+{% else %}
+ {% if waiting %}
+ <h1><img src="{{ STATIC_URL }}img/indicator.gif" alt="{% trans 'Please wait' %}"/>
+ {% trans "Your file is being prepared, please wait." %}</h1>
+
+ <div class="normal-text">
+ <p>{% blocktrans with d=waiting.description %}The file you requested was: <em>{{d}}</em>.{% endblocktrans %}</p>
+
+ <p>{% blocktrans %}<strong>Be aware:</strong> Generating the file can take a while.
+ Please be patient, or bookmark this page and come back later.</p>{% endblocktrans %}
+ </div>
+ {% else %}
+ <h1>{% trans "Something went wrong." %}</h1>
+
+ <div class="normal-text">
+ {% url 'suggest' as s %}
+ <p>{% blocktrans %}Something seems to have gone wrong while generating your file.
+ Please order it again or <a id='suggest' class='ajaxable' href="{{s}}">complain to us</a> about it.{% endblocktrans %}</p>
+ </div>
+ {% endif %}
+{% endif %}
+
+{% endblock %}
+
+{% block extrabody %}
+{% if waiting %}
+<script language="JavaScript">
+<!--
+(function($) {
+ $(function(){
+
+function wait() {
+ $.ajax({
+ href: '',
+ success: function(data) {
+ if (data) {
+ location.reload();
+ }
+ else
+ setTimeout(wait, 10*1000);
+ },
+ error: function(xhr) {
+ location.reload();
+ }
+ });
+}
+setTimeout(wait, 10*1000);
+
+ });
+})(jQuery);
+//-->
+</script>
+{% endif %}
+{% endblock %}
--- /dev/null
+from django.conf.urls.defaults import *
+
+urlpatterns = patterns('waiter.views',
+ url(r'^(?P<path>.*)$', 'wait', name='waiter'),
+)
--- /dev/null
+from os.path import abspath, join, exists
+from shutil import rmtree
+from waiter.settings import WAITER_ROOT
+
+
+def check_abspath(path):
+ abs_path = abspath(join(WAITER_ROOT, path))
+ if not abs_path.startswith(WAITER_ROOT):
+ raise ValueError('Path not inside WAITER_ROOT.')
+ return abs_path
+
+
+def clear_cache(path):
+ abs_path = check_abspath(path)
+ if exists(abs_path):
+ rmtree(abs_path)
+
--- /dev/null
+from os.path import join
+from waiter.models import WaitedFile
+from waiter.settings import WAITER_URL
+from django.shortcuts import render, get_object_or_404
+from django.http import HttpResponse
+
+def wait(request, path):
+ if WaitedFile.exists(path):
+ file_url = join(WAITER_URL, path)
+ else:
+ file_url = ""
+ waiting = get_object_or_404(WaitedFile, path=path)
+ if waiting.is_stale():
+ waiting = None
+
+ if request.is_ajax():
+ return HttpResponse(file_url)
+ else:
+ return render(request, "waiter/wait.html", locals())
{% block branding %}
<h1 id="site-name">{% trans "Site administration" %} - WolneLektury.pl</h1>
-<p style="float: right; font-size: 11px; padding-right: 10px;"><a href="/rosetta/">{% trans "Translations" %}</a></p>
+<p style="font-size: 11px;margin-left:10px;">
+ <!--a href="/rosetta/">{% trans "Translations" %}</a-->
+ <a href="/admin/chunks/chunk/promo/">promobox</a> |
+ <a href="/admin/social/cite">cytaty</a> |
+ <a href="/admin/infopages/infopage/">info</a> |
+ <a href="/admin/catalogue/book/">książki</a> |
+ <a href="/admin/catalogue/tag/">tagi</a> |
+ <a href="/admin/catalogue/collection/">kolekcje</a> |
+ <a href="/admin/pdcounter/">licznik domeny</a> |
+ <a href="/admin/sponsors/">sponsorzy</a> |
+ <a href="/admin/suggest/suggestion/">sugestie</a>
+</p>
{% endblock %}
{% block nav-global %}{% endblock %}
\ No newline at end of file
{% block content %}
<form action="{% url import_book %}" method="post" enctype="multipart/form-data">
+ {% csrf_token %}
<p><input type="file" id="id_book_xml_file" name="book_xml_file" /> <input type="submit" value="{% trans "Import book" %}"/></p>
</form>
{{ block.super }}
<form action="{{ request.get_full_path }}" method="post" accept-charset="utf-8"
class="cuteform{% if placeholdize %} hidelabels{% endif %}">
+{% csrf_token %}
<ol>
<div id="id_{% if form_prefix %}{{ form_prefix }}-{% endif %}__all__"></div>
{{ form.as_ul }}
{% extends "auth/login.html" %}
{% load i18n %}
+{% load honeypot %}
{% block extra %}
<form action="{% url register %}" method="post" accept-charset="utf-8"
class="cuteform hidelabels">
+{% csrf_token %}
+{% honeypot_render_field %}
<ol>
<div id="id_register-__all__"></div>
{{ register_form.as_ul }}
<p>{% blocktrans %}Confirm to authorize access to Wolne Lektury as user <strong>{{ user}}</strong>.{% endblocktrans %}</p>
<form action="{% url piston.authentication.oauth_user_auth %}" method="POST">
+ {% csrf_token %}
{{ form.as_p }}
<button type="submit">Confirm</button>
</form>
<div id="lang-menu-items">
{% for lang in LANGUAGES %}
<form action="{% url django.views.i18n.set_language %}" method="post">
+ {% csrf_token %}
<input type="hidden" name="language" value="{{ lang.0 }}" />
<button type="submit"
class="{% ifequal lang.0 LANGUAGE_CODE %}active{% endifequal %} mono"
submit = _('Register')
ajax_redirect = True
form_prefix = 'register'
+ honeypot = True
def __call__(self, request):
if request.user.is_authenticated():
-Subproject commit 9e13b0c994e9d481008bef7006a74609adfd16f8
+Subproject commit b8e34e0e6730ef76a353a15ff653faa9e8c88a77
django-maintenancemode>=0.9
django-piston
django-jsonfield
+django-picklefield
django-allauth
+django-honeypot
python-memcached
piwik
from settings.auth import *
from settings.cache import *
from settings.celery import *
+from settings.contrib import *
from settings.custom import *
from settings.locale import *
from settings.static import *
'django.middleware.cache.UpdateCacheMiddleware',
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
+ 'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.middleware.doc.XViewMiddleware',
'pagination.middleware.PaginationMiddleware',
ROOT_URLCONF = 'wolnelektury.urls'
-INSTALLED_APPS = [
+# These are the ones we should test.
+INSTALLED_APPS_OUR = [
'wolnelektury_core',
+ # our
+ 'ajaxable',
+ 'api',
+ 'catalogue',
+ 'chunks',
+ 'dictionary',
+ 'infopages',
+ 'lesmianator',
+ #'lessons',
+ 'newtagging',
+ 'opds',
+ 'pdcounter',
+ 'reporting',
+ 'sponsors',
+ 'stats',
+ 'suggest',
+ 'picture',
+ 'search',
+ 'social',
+ 'waiter',
+ ]
+
+INSTALLED_APPS_CONTRIB = [
# external
'django.contrib.auth',
'django.contrib.contenttypes',
'sorl.thumbnail',
'djcelery',
'djkombu',
+ 'honeypot',
# 'django_nose',
#allauth stuff
# included
'compress',
'modeltranslation',
+ ]
- # our
- 'ajaxable',
- 'api',
- 'catalogue',
- 'chunks',
- 'dictionary',
- 'infopages',
- 'lesmianator',
- #'lessons',
- 'newtagging',
- 'opds',
- 'pdcounter',
- 'reporting',
- 'sponsors',
- 'stats',
- 'suggest',
- 'picture',
- 'search',
- 'social',
-]
+INSTALLED_APPS = INSTALLED_APPS_OUR + INSTALLED_APPS_CONTRIB
# Load localsettings, if they exist
try:
BROKER_VHOST = "/"
CELERY_EAGER_PROPAGATES_EXCEPTIONS = True
+CELERY_SEND_TASK_ERROR_EMAILS = True
--- /dev/null
+HONEYPOT_FIELD_NAME = 'miut'
CATALOGUE_DEFAULT_LANGUAGE = 'pol'
PUBLISH_PLAN_FEED = 'http://redakcja.wolnelektury.pl/documents/track/editor-proofreading/?published=false'
+
+# limit rate for ebooks creation
+CATALOGUE_PDF_RATE_LIMIT = '1/m'
+CATALOGUE_EPUB_RATE_LIMIT = '6/m'
+CATALOGUE_MOBI_RATE_LIMIT = '5/m'
+CATALOGUE_CUSTOMPDF_RATE_LIMIT = '1/m'
+
+# set to 'new' or 'old' to skip time-consuming test
+# for TeX morefloats library version
+LIBRARIAN_PDF_MOREFLOATS = None
url(r'^info/', include('infopages.urls')),
url(r'^ludzie/', include('social.urls')),
url(r'^uzytkownik/', include('allauth.urls')),
+ url(r'^czekaj/', include('waiter.urls')),
# Admin panel
url(r'^admin/catalogue/book/import$', 'catalogue.views.import_book', name='import_book'),