1 # -*- coding: utf-8 -*-
2 # This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
3 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
8 from django.conf import settings
9 from django import template
10 from django.template import Node, Variable, Template, Context
11 from django.core.cache import cache
12 from django.core.urlresolvers import reverse
13 from django.contrib.auth.forms import UserCreationForm, AuthenticationForm
14 from django.utils.translation import ugettext as _
16 from catalogue.utils import split_tags
17 from catalogue.models import Book, BookMedia, Fragment, Tag
19 register = template.Library()
22 class RegistrationForm(UserCreationForm):
24 "Returns this form rendered as HTML <li>s -- excluding the <ul></ul>."
25 return self._html_output(u'<li>%(errors)s%(label)s %(field)s<span class="help-text">%(help_text)s</span></li>', u'<li>%s</li>', '</li>', u' %s', False)
28 class LoginForm(AuthenticationForm):
30 "Returns this form rendered as HTML <li>s -- excluding the <ul></ul>."
31 return self._html_output(u'<li>%(errors)s%(label)s %(field)s<span class="help-text">%(help_text)s</span></li>', u'<li>%s</li>', '</li>', u' %s', False)
44 return '%s%s' % (text[0].upper(), text[1:])
50 def html_title_from_tags(tags):
52 return title_from_tags(tags)
53 template = Template("{{ category }}: <a href='{{ tag.get_absolute_url }}'>{{ tag.name }}</a>")
54 return capfirst(",<br/>".join(
55 template.render(Context({'tag': tag, 'category': _(tag.category)})) for tag in tags))
58 def simple_title(tags):
61 title.append("%s: %s" % (_(tag.category), tag.name))
62 return capfirst(', '.join(title))
66 def book_title(book, html_links=False):
67 return book.pretty_title(html_links)
71 def book_title_html(book):
72 return book_title(book, html_links=True)
76 def title_from_tags(tags):
80 result[tag.category] = tag
83 # TODO: Remove this after adding flection mechanism
84 return simple_title(tags)
86 class Flection(object):
87 def get_case(self, name, flection):
91 self = split_tags(tags)
95 # Specjalny przypadek oglądania wszystkich lektur na danej półce
96 if len(self) == 1 and 'set' in self:
97 return u'Półka %s' % self['set']
99 # Specjalny przypadek "Twórczość w pozytywizmie", wtedy gdy tylko epoka
100 # jest wybrana przez użytkownika
101 if 'epoch' in self and len(self) == 1:
102 text = u'Twórczość w %s' % flection.get_case(unicode(self['epoch']), u'miejscownik')
103 return capfirst(text)
105 # Specjalny przypadek "Dramat w twórczości Sofoklesa", wtedy gdy podane
106 # są tylko rodzaj literacki i autor
107 if 'kind' in self and 'author' in self and len(self) == 2:
108 text = u'%s w twórczości %s' % (unicode(self['kind']),
109 flection.get_case(unicode(self['author']), u'dopełniacz'))
110 return capfirst(text)
112 # Przypadki ogólniejsze
114 title += u'Motyw %s' % unicode(self['theme'])
118 title += u' w %s' % flection.get_case(unicode(self['genre']), u'miejscownik')
120 title += unicode(self['genre'])
122 if 'kind' in self or 'author' in self or 'epoch' in self:
123 if 'genre' in self or 'theme' in self:
125 title += u' w %s ' % flection.get_case(unicode(self['kind']), u'miejscownik')
127 title += u' w twórczości '
129 title += u'%s ' % unicode(self.get('kind', u'twórczość'))
132 title += flection.get_case(unicode(self['author']), u'dopełniacz')
133 elif 'epoch' in self:
134 title += flection.get_case(unicode(self['epoch']), u'dopełniacz')
136 return capfirst(title)
140 def book_tree(book_list, books_by_parent):
141 text = "".join("<li><a href='%s'>%s</a>%s</li>" % (
142 book.get_absolute_url(), book.title, book_tree(books_by_parent.get(book, ()), books_by_parent)
143 ) for book in book_list)
146 return "<ol>%s</ol>" % text
151 def audiobook_tree(book_list, books_by_parent):
152 text = "".join("<li><a class='open-player' href='%s'>%s</a>%s</li>" % (
153 reverse("book_player", args=[book.slug]), book.title, audiobook_tree(books_by_parent.get(book, ()), books_by_parent)
154 ) for book in book_list)
157 return "<ol>%s</ol>" % text
162 def book_tree_texml(book_list, books_by_parent, depth=1):
164 <cmd name='hspace'><parm>%(depth)dem</parm></cmd>%(title)s
165 <spec cat='align' /><cmd name="note"><parm>%(audiences)s</parm></cmd>
166 <spec cat='align' /><cmd name="note"><parm>%(audiobook)s</parm></cmd>
172 "audiences": ", ".join(book.audiences_pl()),
173 "audiobook": "audiobook" if book.has_media('mp3') else "",
174 "children": book_tree_texml(books_by_parent.get(book.id, ()), books_by_parent, depth + 1)
175 } for book in book_list)
179 def book_tree_csv(author, book_list, books_by_parent, depth=1, max_depth=3, delimeter="\t"):
180 def quote_if_necessary(s):
183 s.replace('"', '\\"')
188 return "".join("""%(author)s%(d)s%(preindent)s%(title)s%(d)s%(postindent)s%(audiences)s%(d)s%(audiobook)s
191 "preindent": delimeter * (depth - 1),
192 "postindent": delimeter * (max_depth - depth),
194 "author": quote_if_necessary(author.name),
195 "title": quote_if_necessary(book.title),
196 "audiences": ", ".join(book.audiences_pl()),
197 "audiobook": "audiobook" if book.has_media('mp3') else "",
198 "children": book_tree_csv(author, books_by_parent.get(book.id, ()), books_by_parent, depth + 1)
199 } for book in book_list)
202 def all_editors(extra_info):
204 if 'editors' in extra_info:
205 editors += extra_info['editors']
206 if 'technical_editors' in extra_info:
207 editors += extra_info['technical_editors']
208 # support for extra_info-s from librarian<1.2
209 if 'editor' in extra_info:
210 editors.append(extra_info['editor'])
211 if 'technical_editor' in extra_info:
212 editors.append(extra_info['technical_editor'])
214 ' '.join(p.strip() for p in person.rsplit(',', 1)[::-1])
215 for person in sorted(set(editors)))
219 def user_creation_form():
220 return RegistrationForm(prefix='registration').as_ul()
224 def authentication_form():
225 return LoginForm(prefix='login').as_ul()
229 def catalogue_url(parser, token):
230 bits = token.split_contents()
236 tags_to_remove.append(bit[1:])
238 tags_to_add.append(bit)
240 return CatalogueURLNode(tags_to_add, tags_to_remove)
243 class CatalogueURLNode(Node):
244 def __init__(self, tags_to_add, tags_to_remove):
245 self.tags_to_add = [Variable(tag) for tag in tags_to_add]
246 self.tags_to_remove = [Variable(tag) for tag in tags_to_remove]
248 def render(self, context):
252 for tag_variable in self.tags_to_add:
253 tag = tag_variable.resolve(context)
254 if isinstance(tag, (list, dict)):
255 tags_to_add += [t for t in tag]
257 tags_to_add.append(tag)
259 for tag_variable in self.tags_to_remove:
260 tag = tag_variable.resolve(context)
262 tags_to_remove += [t for t in tag]
264 tags_to_remove.append(tag)
266 tag_slugs = [tag.url_chunk for tag in tags_to_add]
267 for tag in tags_to_remove:
269 tag_slugs.remove(tag.url_chunk)
273 if len(tag_slugs) > 0:
274 return reverse('tagged_object_list', kwargs={'tags': '/'.join(tag_slugs)})
276 return reverse('main_page')
279 @register.inclusion_tag('catalogue/latest_blog_posts.html')
280 def latest_blog_posts(feed_url, posts_to_show=5):
282 feed = feedparser.parse(str(feed_url))
284 for i in range(posts_to_show):
285 pub_date = feed['entries'][i].updated_parsed
286 published = datetime.date(pub_date[0], pub_date[1], pub_date[2] )
288 'title': feed['entries'][i].title,
289 'summary': feed['entries'][i].summary,
290 'link': feed['entries'][i].link,
293 return {'posts': posts}
298 @register.inclusion_tag('catalogue/tag_list.html')
299 def tag_list(tags, choices=None):
302 if len(tags) == 1 and tags[0].category not in [t.category for t in choices]:
307 @register.inclusion_tag('catalogue/inline_tag_list.html')
308 def inline_tag_list(tags, choices=None):
309 return tag_list(tags, choices)
312 @register.inclusion_tag('catalogue/book_info.html')
317 @register.inclusion_tag('catalogue/book_wide.html', takes_context=True)
318 def book_wide(context, book):
319 book_themes = book.related_themes()
320 extra_info = book.extra_info
321 hide_about = extra_info.get('about', '').startswith('http://wiki.wolnepodreczniki.pl')
325 'main_link': reverse('book_text', args=[book.slug]) if book.html_file else None,
326 'related': book.related_info(),
327 'extra_info': extra_info,
328 'hide_about': hide_about,
329 'themes': book_themes,
330 'request': context.get('request'),
334 @register.inclusion_tag('catalogue/book_short.html', takes_context=True)
335 def book_short(context, book):
338 'main_link': book.get_absolute_url(),
339 'related': book.related_info(),
340 'request': context.get('request'),
344 @register.inclusion_tag('catalogue/book_mini_box.html')
348 'related': book.related_info(),
352 @register.inclusion_tag('catalogue/work-list.html', takes_context=True)
353 def work_list(context, object_list):
354 request = context.get('request')
356 object_type = type(object_list[0]).__name__
360 @register.inclusion_tag('catalogue/fragment_promo.html')
361 def fragment_promo(arg=None):
363 fragments = Fragment.objects.all().order_by('?')
364 fragment = fragments[0] if fragments.exists() else None
365 elif isinstance(arg, Book):
366 fragment = arg.choose_fragment()
368 fragments = Fragment.tagged.with_all(arg).order_by('?')
369 fragment = fragments[0] if fragments.exists() else None
372 'fragment': fragment,
376 @register.inclusion_tag('catalogue/related_books.html')
377 def related_books(book, limit=6, random=1):
378 cache_key = "catalogue.related_books.%d.%d" % (book.id, limit - random)
379 related = cache.get(cache_key)
381 related = list(Book.objects.filter(
382 common_slug=book.common_slug).exclude(pk=book.pk)[:limit])
383 limit -= len(related)
385 related += Book.tagged.related_to(book,
386 Book.objects.exclude(common_slug=book.common_slug),
387 ignore_by_tag=book.book_tag())[:limit-random]
388 cache.set(cache_key, related, 1800)
390 related += list(Book.objects.exclude(
391 pk__in=[b.pk for b in related] + [book.pk]
392 ).order_by('?')[:random])
398 @register.inclusion_tag('catalogue/menu.html')
399 def catalogue_menu():
400 tags = Tag.objects.filter(
401 category__in=('author', 'epoch', 'genre', 'kind', 'theme')
402 ).exclude(book_count=0)
403 return split_tags(tags)
408 def tag_url(category, slug):
409 return reverse('catalogue.views.tagged_object_list', args=[
410 '/'.join((Tag.categories_dict[category], slug))
415 def download_audio(book, daisy=True):
416 related = book.related_info()
418 if related['media'].get('mp3'):
419 links.append("<a href='%s'>%s</a>" %
420 (reverse('download_zip_mp3', args=[book.slug]),
421 BookMedia.formats['mp3'].name))
422 if related['media'].get('ogg'):
423 links.append("<a href='%s'>%s</a>" %
424 (reverse('download_zip_ogg', args=[book.slug]),
425 BookMedia.formats['ogg'].name))
426 if daisy and related['media'].get('daisy'):
427 for dsy in book.get_media('daisy'):
428 links.append("<a href='%s'>%s</a>" %
429 (dsy.file.url, BookMedia.formats['daisy'].name))
430 return ", ".join(links)
433 @register.inclusion_tag("catalogue/snippets/custom_pdf_link_li.html")
434 def custom_pdf_link_li(book):
437 'NO_CUSTOM_PDF': settings.NO_CUSTOM_PDF,