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.
7 from random import randint
8 from urlparse import urlparse
10 from django.conf import settings
11 from django import template
12 from django.template import Node, Variable, Template, Context
13 from django.core.cache import cache
14 from django.core.urlresolvers import reverse
15 from django.contrib.auth.forms import UserCreationForm, AuthenticationForm
16 from django.utils.translation import ugettext as _
18 from catalogue.utils import related_tag_name as _related_tag_name
19 from catalogue.models import Book, BookMedia, Fragment, Tag, Source
20 from catalogue.constants import LICENSES
22 register = template.Library()
25 class RegistrationForm(UserCreationForm):
27 "Returns this form rendered as HTML <li>s -- excluding the <ul></ul>."
28 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)
31 class LoginForm(AuthenticationForm):
33 "Returns this form rendered as HTML <li>s -- excluding the <ul></ul>."
34 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)
47 return '%s%s' % (text[0].upper(), text[1:])
53 def html_title_from_tags(tags):
55 return title_from_tags(tags)
56 template = Template("{{ category }}: <a href='{{ tag.get_absolute_url }}'>{{ tag.name }}</a>")
57 return capfirst(",<br/>".join(
58 template.render(Context({'tag': tag, 'category': _(tag.category)})) for tag in tags))
61 def simple_title(tags):
64 title.append("%s: %s" % (_(tag.category), tag.name))
65 return capfirst(', '.join(title))
69 def book_title(book, html_links=False):
70 return book.pretty_title(html_links)
74 def book_title_html(book):
75 return book_title(book, html_links=True)
79 def title_from_tags(tags):
83 result[tag.category] = tag
86 # TODO: Remove this after adding flection mechanism
87 return simple_title(tags)
89 class Flection(object):
90 def get_case(self, name, flection):
94 self = split_tags(tags)
98 # Specjalny przypadek oglądania wszystkich lektur na danej półce
99 if len(self) == 1 and 'set' in self:
100 return u'Półka %s' % self['set']
102 # Specjalny przypadek "Twórczość w pozytywizmie", wtedy gdy tylko epoka
103 # jest wybrana przez użytkownika
104 if 'epoch' in self and len(self) == 1:
105 text = u'Twórczość w %s' % flection.get_case(unicode(self['epoch']), u'miejscownik')
106 return capfirst(text)
108 # Specjalny przypadek "Dramat w twórczości Sofoklesa", wtedy gdy podane
109 # są tylko rodzaj literacki i autor
110 if 'kind' in self and 'author' in self and len(self) == 2:
111 text = u'%s w twórczości %s' % (unicode(self['kind']),
112 flection.get_case(unicode(self['author']), u'dopełniacz'))
113 return capfirst(text)
115 # Przypadki ogólniejsze
117 title += u'Motyw %s' % unicode(self['theme'])
121 title += u' w %s' % flection.get_case(unicode(self['genre']), u'miejscownik')
123 title += unicode(self['genre'])
125 if 'kind' in self or 'author' in self or 'epoch' in self:
126 if 'genre' in self or 'theme' in self:
128 title += u' w %s ' % flection.get_case(unicode(self['kind']), u'miejscownik')
130 title += u' w twórczości '
132 title += u'%s ' % unicode(self.get('kind', u'twórczość'))
135 title += flection.get_case(unicode(self['author']), u'dopełniacz')
136 elif 'epoch' in self:
137 title += flection.get_case(unicode(self['epoch']), u'dopełniacz')
139 return capfirst(title)
143 def book_tree(book_list, books_by_parent):
144 text = "".join("<li><a href='%s'>%s</a>%s</li>" % (
145 book.get_absolute_url(), book.title, book_tree(books_by_parent.get(book, ()), books_by_parent)
146 ) for book in book_list)
149 return "<ol>%s</ol>" % text
154 def audiobook_tree(book_list, books_by_parent):
155 text = "".join("<li><a class='open-player' href='%s'>%s</a>%s</li>" % (
156 reverse("book_player", args=[book.slug]), book.title, audiobook_tree(books_by_parent.get(book, ()), books_by_parent)
157 ) for book in book_list)
160 return "<ol>%s</ol>" % text
165 def book_tree_texml(book_list, books_by_parent, depth=1):
167 <cmd name='hspace'><parm>%(depth)dem</parm></cmd>%(title)s
168 <spec cat='align' /><cmd name="note"><parm>%(audiences)s</parm></cmd>
169 <spec cat='align' /><cmd name="note"><parm>%(audiobook)s</parm></cmd>
175 "audiences": ", ".join(book.audiences_pl()),
176 "audiobook": "audiobook" if book.has_media('mp3') else "",
177 "children": book_tree_texml(books_by_parent.get(book.id, ()), books_by_parent, depth + 1)
178 } for book in book_list)
182 def book_tree_csv(author, book_list, books_by_parent, depth=1, max_depth=3, delimeter="\t"):
183 def quote_if_necessary(s):
186 s.replace('"', '\\"')
191 return "".join("""%(author)s%(d)s%(preindent)s%(title)s%(d)s%(postindent)s%(audiences)s%(d)s%(audiobook)s
194 "preindent": delimeter * (depth - 1),
195 "postindent": delimeter * (max_depth - depth),
197 "author": quote_if_necessary(author.name),
198 "title": quote_if_necessary(book.title),
199 "audiences": ", ".join(book.audiences_pl()),
200 "audiobook": "audiobook" if book.has_media('mp3') else "",
201 "children": book_tree_csv(author, books_by_parent.get(book.id, ()), books_by_parent, depth + 1)
202 } for book in book_list)
205 def all_editors(extra_info):
207 if 'editors' in extra_info:
208 editors += extra_info['editors']
209 if 'technical_editors' in extra_info:
210 editors += extra_info['technical_editors']
211 # support for extra_info-s from librarian<1.2
212 if 'editor' in extra_info:
213 editors.append(extra_info['editor'])
214 if 'technical_editor' in extra_info:
215 editors.append(extra_info['technical_editor'])
217 ' '.join(p.strip() for p in person.rsplit(',', 1)[::-1])
218 for person in sorted(set(editors)))
222 def user_creation_form():
223 return RegistrationForm(prefix='registration').as_ul()
227 def authentication_form():
228 return LoginForm(prefix='login').as_ul()
232 def catalogue_url(parser, token):
233 bits = token.split_contents()
239 tags_to_remove.append(bit[1:])
241 tags_to_add.append(bit)
243 return CatalogueURLNode(tags_to_add, tags_to_remove)
246 class CatalogueURLNode(Node):
247 def __init__(self, tags_to_add, tags_to_remove):
248 self.tags_to_add = [Variable(tag) for tag in tags_to_add]
249 self.tags_to_remove = [Variable(tag) for tag in tags_to_remove]
251 def render(self, context):
255 for tag_variable in self.tags_to_add:
256 tag = tag_variable.resolve(context)
257 if isinstance(tag, (list, dict)):
258 tags_to_add += [t for t in tag]
260 tags_to_add.append(tag)
262 for tag_variable in self.tags_to_remove:
263 tag = tag_variable.resolve(context)
265 tags_to_remove += [t for t in tag]
267 tags_to_remove.append(tag)
269 tag_slugs = [tag.url_chunk for tag in tags_to_add]
270 for tag in tags_to_remove:
272 tag_slugs.remove(tag.url_chunk)
276 if len(tag_slugs) > 0:
277 return reverse('tagged_object_list', kwargs={'tags': '/'.join(tag_slugs)})
279 return reverse('main_page')
282 @register.inclusion_tag('catalogue/latest_blog_posts.html')
283 def latest_blog_posts(feed_url, posts_to_show=5):
285 feed = feedparser.parse(str(feed_url))
287 for i in range(posts_to_show):
288 pub_date = feed['entries'][i].published_parsed
289 published = datetime.date(pub_date[0], pub_date[1], pub_date[2])
291 'title': feed['entries'][i].title,
292 'summary': feed['entries'][i].summary,
293 'link': feed['entries'][i].link,
296 return {'posts': posts}
301 @register.inclusion_tag('catalogue/tag_list.html')
302 def tag_list(tags, choices=None):
305 if len(tags) == 1 and tags[0].category not in [t.category for t in choices]:
310 @register.inclusion_tag('catalogue/inline_tag_list.html')
311 def inline_tag_list(tags, choices=None):
312 return tag_list(tags, choices)
315 @register.inclusion_tag('catalogue/collection_list.html')
316 def collection_list(collections):
320 @register.inclusion_tag('catalogue/book_info.html')
325 @register.inclusion_tag('catalogue/book_wide.html', takes_context=True)
326 def book_wide(context, book):
327 book_themes = book.related_themes()
328 extra_info = book.extra_info
329 hide_about = extra_info.get('about', '').startswith('http://wiki.wolnepodreczniki.pl')
330 stage_note, stage_note_url = book.stage_note()
334 'main_link': reverse('book_text', args=[book.slug]) if book.html_file else None,
335 'related': book.related_info(),
336 'extra_info': extra_info,
337 'hide_about': hide_about,
338 'themes': book_themes,
339 'request': context.get('request'),
340 'show_lang': book.language_code() != settings.LANGUAGE_CODE,
341 'stage_note': stage_note,
342 'stage_note_url': stage_note_url,
346 @register.inclusion_tag('catalogue/book_short.html', takes_context=True)
347 def book_short(context, book):
348 stage_note, stage_note_url = book.stage_note()
352 'main_link': book.get_absolute_url(),
353 'related': book.related_info(),
354 'request': context.get('request'),
355 'show_lang': book.language_code() != settings.LANGUAGE_CODE,
356 'stage_note': stage_note,
357 'stage_note_url': stage_note_url,
361 @register.inclusion_tag('catalogue/book_mini_box.html')
362 def book_mini(book, with_link=True):
363 author_str = ", ".join(related_tag_name(tag)
364 for tag in book.related_info()['tags'].get('author', ()))
367 'author_str': author_str,
368 'with_link': with_link,
369 'show_lang': book.language_code() != settings.LANGUAGE_CODE,
373 @register.inclusion_tag('catalogue/work-list.html', takes_context=True)
374 def work_list(context, object_list):
375 request = context.get('request')
379 @register.inclusion_tag('catalogue/fragment_promo.html')
380 def fragment_promo(arg=None):
382 fragments = Fragment.objects.all().order_by('?')
383 fragment = fragments[0] if fragments.exists() else None
384 elif isinstance(arg, Book):
385 fragment = arg.choose_fragment()
387 fragments = Fragment.tagged.with_all(arg).order_by('?')
388 fragment = fragments[0] if fragments.exists() else None
391 'fragment': fragment,
395 @register.inclusion_tag('catalogue/related_books.html')
396 def related_books(book, limit=6, random=1, taken=0):
397 limit = limit - taken
398 cache_key = "catalogue.related_books.%d.%d" % (book.id, limit - random)
399 related = cache.get(cache_key)
401 related = Book.tagged.related_to(book,
402 Book.objects.exclude(common_slug=book.common_slug),
403 ignore_by_tag=book.book_tag())[:limit-random]
404 cache.set(cache_key, related, 1800)
406 random_books = Book.objects.exclude(
407 pk__in=[b.pk for b in related] + [book.pk])
409 count = random_books.count()
411 related.append(random_books[randint(0, count - 1)])
413 related += list(random_books.order_by('?')[:random])
419 @register.inclusion_tag('catalogue/menu.html')
420 def catalogue_menu():
421 return {'categories': [
422 ('author', _('Authors'), 'autorzy'),
423 ('genre', _('Genres'), 'gatunki'),
424 ('kind', _('Kinds'), 'rodzaje'),
425 ('epoch', _('Epochs'), 'epoki'),
426 ('theme', _('Themes'), 'motywy'),
431 def tag_url(category, slug):
432 return Tag.create_url(category, slug)
436 def download_audio(book, daisy=True):
437 related = book.related_info()
439 if related['media'].get('mp3'):
440 links.append("<a href='%s'>%s</a>" %
441 (reverse('download_zip_mp3', args=[book.slug]),
442 BookMedia.formats['mp3'].name))
443 if related['media'].get('ogg'):
444 links.append("<a href='%s'>%s</a>" %
445 (reverse('download_zip_ogg', args=[book.slug]),
446 BookMedia.formats['ogg'].name))
447 if daisy and related['media'].get('daisy'):
448 for dsy in book.get_media('daisy'):
449 links.append("<a href='%s'>%s</a>" %
450 (dsy.file.url, BookMedia.formats['daisy'].name))
451 return ", ".join(links)
454 @register.inclusion_tag("catalogue/snippets/custom_pdf_link_li.html")
455 def custom_pdf_link_li(book):
458 'NO_CUSTOM_PDF': settings.NO_CUSTOM_PDF,
462 @register.inclusion_tag("catalogue/snippets/license_icon.html")
463 def license_icon(license_url):
464 """Creates a license icon, if the license_url is known."""
465 known = LICENSES.get(license_url)
469 "license_url": license_url,
470 "icon": "img/licenses/%s.png" % known['icon'],
471 "license_description": known['description'],
476 def related_tag_name(tag, lang=None):
477 return _related_tag_name(tag, lang)
482 return obj.__class__.__name__
486 def source_name(url):
488 netloc = urlparse(url).netloc
490 netloc = urlparse('http://' + url).netloc
493 source, created = Source.objects.get_or_create(netloc=netloc)
494 return source.name or netloc