From 76fc2ed0abc7ff4bb7ddee798a115f11bf51ee1c Mon Sep 17 00:00:00 2001 From: Radek Czajka Date: Tue, 27 Dec 2011 13:46:32 +0100 Subject: [PATCH 01/16] fix chaining querysets for pagination --- apps/catalogue/utils.py | 30 ++++++++++++++++++++++++++++++ apps/catalogue/views.py | 20 +++++++++----------- 2 files changed, 39 insertions(+), 11 deletions(-) diff --git a/apps/catalogue/utils.py b/apps/catalogue/utils.py index acbd778cd..145511e06 100644 --- a/apps/catalogue/utils.py +++ b/apps/catalogue/utils.py @@ -164,3 +164,33 @@ def async_build_pdf(book_id, customizations, file_name): if not DefaultStorage().exists(file_name): book.build_pdf(customizations=customizations, file_name=file_name) print "done." + + +class MultiQuerySet(object): + def __init__(self, *args, **kwargs): + self.querysets = args + self._count = None + + def count(self): + if not self._count: + self._count = sum(len(qs) for qs in self.querysets) + return self._count + + def __len__(self): + return self.count() + + def __getitem__(self, item): + indices = (offset, stop, step) = item.indices(self.count()) + items = [] + total_len = stop - offset + for qs in self.querysets: + if len(qs) < offset: + offset -= len(qs) + else: + items += list(qs[offset:stop]) + if len(items) >= total_len: + return items + else: + offset = 0 + stop = total_len - len(items) + continue \ No newline at end of file diff --git a/apps/catalogue/views.py b/apps/catalogue/views.py index f1eeab739..c34cf29c3 100644 --- a/apps/catalogue/views.py +++ b/apps/catalogue/views.py @@ -26,14 +26,14 @@ from django.views.generic.list_detail import object_list from ajaxable.utils import LazyEncoder, JSONResponse from catalogue import models from catalogue import forms -from catalogue.utils import split_tags, AttachmentHttpResponse, async_build_pdf +from catalogue.utils import (split_tags, AttachmentHttpResponse, + async_build_pdf, MultiQuerySet) from catalogue.tasks import touch_tag 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 itertools import chain from os import path staff_required = user_passes_test(lambda user: user.is_staff) @@ -169,22 +169,20 @@ def tagged_object_list(request, tags=''): only_author = len(tags) == 1 and tags[0].category == 'author' objects = models.Book.objects.none() - # Add pictures - objects = Picture.tagged.with_all(tags)|objects + # Add pictures + objects = MultiQuerySet(Picture.tagged.with_all(tags), objects) - return object_list( - request, - objects, - template_name='catalogue/tagged_object_list.html', - extra_context={ + return render_to_response('catalogue/tagged_object_list.html', + { + 'object_list': objects, 'categories': categories, 'only_shelf': only_shelf, 'only_author': only_author, 'only_my_shelf': only_my_shelf, 'formats_form': forms.DownloadFormatsForm(), 'tags': tags, - } - ) + }, + context_instance=RequestContext(request)) def book_fragments(request, book, theme_slug): -- 2.20.1 From 77baf8640ab29684909b9f57cd9d4999dad2071f Mon Sep 17 00:00:00 2001 From: Marcin Koziej Date: Wed, 28 Dec 2011 16:55:55 +0100 Subject: [PATCH 02/16] some css tweaks to download popup and logo/tagline shifted right a bit --- apps/catalogue/models.py | 8 ++----- wolnelektury/static/css/base.css | 1 + wolnelektury/static/css/book_box.css | 24 +++++++++++++++---- wolnelektury/static/css/header.css | 6 ++--- .../templates/catalogue/book_short.html | 19 +++++++++++---- 5 files changed, 41 insertions(+), 17 deletions(-) diff --git a/apps/catalogue/models.py b/apps/catalogue/models.py index 2effb3bc7..e4cd8c40e 100644 --- a/apps/catalogue/models.py +++ b/apps/catalogue/models.py @@ -510,16 +510,12 @@ class Book(models.Model): tags = self.tags.filter(category__in=('author', 'kind', 'genre', 'epoch')) tags = split_tags(tags) - formats = [] + formats = {} # files generated during publication for ebook_format in self.ebook_formats: if self.has_media(ebook_format): - formats.append(u'%s' % ( - self.get_media(ebook_format).url, - ebook_format.upper() - )) + formats[ebook_format] = self.get_media(ebook_format) - formats = [mark_safe(format) for format in formats] short_html = unicode(render_to_string('catalogue/book_short.html', {'book': self, 'tags': tags, 'formats': formats})) diff --git a/wolnelektury/static/css/base.css b/wolnelektury/static/css/base.css index f55a39e01..dd686c792 100755 --- a/wolnelektury/static/css/base.css +++ b/wolnelektury/static/css/base.css @@ -90,6 +90,7 @@ h2 { -moz-box-shadow: 2px 2px 2px #ddd; -webkit-box-shadow: 2px 2px 2px #ddd; box-shadow: 2px 2px 2px #ddd; + z-index: 2; } #themes-list ul { list-style: none; diff --git a/wolnelektury/static/css/book_box.css b/wolnelektury/static/css/book_box.css index ca2e72696..b51750287 100755 --- a/wolnelektury/static/css/book_box.css +++ b/wolnelektury/static/css/book_box.css @@ -87,21 +87,37 @@ .book-box-download { position: relative; } + +.book-box-download a { + position: relative; + z-index: 1; +} + .book-box-formats { display: none; position: absolute; + width: 16.363em; border: 1px solid #ddd; - padding: .8em 1em; + padding: 3.454em 1.727em .818em 1.727em; background: #fff; -moz-box-shadow: 2px 2px 2px #ddd; -webkit-box-shadow: 2px 2px 2px #ddd; box-shadow: 2px 2px 2px #ddd; - + + z-index: 0; + top: -1.454em; + left: -1.727em; } -.book-box-formats a { +.book-box-formats span { display: block; } + + +.book-box-download:hover .book-box-formats:first { + margin-top: 1.454em; +} + .book-box-download:hover .book-box-formats { display: block; } @@ -109,7 +125,7 @@ .book-box-tools { font-size: 1.1em; } -.book-box-tools a:before { +.book-box-tools a.downarrow:before { content: "⇩"; font-family: WL-Nav; font-size: 2em; diff --git a/wolnelektury/static/css/header.css b/wolnelektury/static/css/header.css index 79e8815d6..8615d8b59 100755 --- a/wolnelektury/static/css/header.css +++ b/wolnelektury/static/css/header.css @@ -37,8 +37,8 @@ #logo { position: absolute; - top: 1.9em; - margin-left: 1.6em; + top: 5.5em; + margin-left: 1.5em; } #logo a { @@ -49,7 +49,7 @@ #tagline { display: inline-block; - margin-left: 24em; + margin-left: 25.5em; } #tagline span { font-size: 1.1em; diff --git a/wolnelektury/templates/catalogue/book_short.html b/wolnelektury/templates/catalogue/book_short.html index c80efff91..8ac23a3a6 100644 --- a/wolnelektury/templates/catalogue/book_short.html +++ b/wolnelektury/templates/catalogue/book_short.html @@ -49,18 +49,29 @@ -- 2.20.1 From 6c071c6cbb739209e9d8488ff7080f8ad7c7d2d0 Mon Sep 17 00:00:00 2001 From: Radek Czajka Date: Wed, 28 Dec 2011 17:29:37 +0100 Subject: [PATCH 03/16] publishing plan, plus some layout fixes --- wolnelektury/static/css/base.css | 1 + wolnelektury/static/css/book_box.css | 26 ++++++++++++---- wolnelektury/static/css/header.css | 7 +++-- wolnelektury/static/fonts/WL-Nav.ttf | Bin 9492 -> 2244 bytes wolnelektury/static/fonts/WL.ttf | Bin 5120 -> 5248 bytes .../templates/catalogue/book_short.html | 2 +- wolnelektury/templates/main_page.html | 1 + wolnelektury/templates/publish_plan.html | 16 ++++++++++ wolnelektury/urls.py | 1 + wolnelektury/views.py | 29 ++++++++++++++++-- 10 files changed, 71 insertions(+), 12 deletions(-) create mode 100755 wolnelektury/templates/publish_plan.html diff --git a/wolnelektury/static/css/base.css b/wolnelektury/static/css/base.css index f55a39e01..d7be8e395 100755 --- a/wolnelektury/static/css/base.css +++ b/wolnelektury/static/css/base.css @@ -90,6 +90,7 @@ h2 { -moz-box-shadow: 2px 2px 2px #ddd; -webkit-box-shadow: 2px 2px 2px #ddd; box-shadow: 2px 2px 2px #ddd; + z-index: 500; } #themes-list ul { list-style: none; diff --git a/wolnelektury/static/css/book_box.css b/wolnelektury/static/css/book_box.css index ca2e72696..272f558b3 100755 --- a/wolnelektury/static/css/book_box.css +++ b/wolnelektury/static/css/book_box.css @@ -109,19 +109,33 @@ .book-box-tools { font-size: 1.1em; } -.book-box-tools a:before { +.book-box-read a:before { + content: "\2609"; + font-family: WL-Nav; + font-size: 2.25em; + margin-right: .15em; + vertical-align: middle; +} +.book-box-download a:before { content: "⇩"; font-family: WL-Nav; - font-size: 2em; - margin-right: .25em; + font-size: 2.25em; + margin-right: .15em; + vertical-align: middle; +} +.book-box-audiobook a:before { + content: "\266B"; + font-family: WL-Nav; + font-size: 2.25em; + margin-right: .15em; vertical-align: middle; } .book-box-read { - width: 11em; + width: 11.5em; } .book-box-download { - width: 8em; + width: 8.5em; } .book-box-audiobook { - width: 8em; + width: 7em; } diff --git a/wolnelektury/static/css/header.css b/wolnelektury/static/css/header.css index 79e8815d6..71dd09c95 100755 --- a/wolnelektury/static/css/header.css +++ b/wolnelektury/static/css/header.css @@ -66,8 +66,8 @@ display: inline-block; width: 63.1em; padding-left: .5em; - padding-right: .5em; - padding-top: 0; + padding-right: 0; + padding-top: 0.5em; padding-bottom: 0; } @@ -75,7 +75,7 @@ padding: 0; height: 3.3em; border: none; - width: 62.6em; + width: 63.1em; font-size: 1em; padding-left: .5em; -webkit-border-radius: .5em; @@ -92,6 +92,7 @@ padding: 0; margin: 0; width: 9.4em; + float: right; } #search-button button { font-size: 1em; diff --git a/wolnelektury/static/fonts/WL-Nav.ttf b/wolnelektury/static/fonts/WL-Nav.ttf index 05282b86652d3496d73782b935d96ef2f8712a6f..e73dedf6f3a029c62f1c84cc9904d39ea961eeb4 100644 GIT binary patch literal 2244 zcmds2?N3uz7=O;aZEs6?X?ZEdp`5nbBE!C15adOI0a46whN*GHL@%^J1=^+*5tA)u ze%M_6VCE7NOdMOBjItLg`<>egnl1hTJE!NK z-}5`?{Lb?{=iFOjL{va$NTvf_T?c!Pee?N#BJ(WR%Gcirs9$8>%Mh_6u#XRnX^HMv z_uYkk53O(DWQvnZnj(^e7&i=Q?5=qC0BU<>cPd*G_ z>;YQCFbMN4128Lvy?J;nb;^F9T(G}{T{#*b&{#WL#QJ*JwlVEgg7(w97~cV#$F#A? z*!l185E-vReBMLFh zmdJ!Wt(SKG3?CTW9j05Zi{26Vu!H*GTLNRxd%%gYQIRLmh(vfP??vlI+o$UYZP6-o z>kh&a3YBCak-I!STFG_M^{dz79l@#5I)0dV_{zf5h08DONzAnFgot1*TY!3b-ot0y z|F75YJJsm5jly)F1(6Ay1*?&!k!6MaHyH(5=M@7zR&Sb&4_fDC36O5ef;I|zUNNz! zt@B9ZW3|rivis`n9v8DL-o7nOuMD~*yl%**xS4>sL#*7w8kupUZ>4{w&nLYl9of2~ zNgw%E&WTfA{XyP~TOyN{>_l5!K9|HDHWT;Sj7oWZF%#8CLTqJ1yD0f54Le;dJdgu`LR-xr_du5ZkO8*|Ic(;Mn`RCQX+ZO+Ecbxs?1 zJGaQ=4~$G6|NFzZKbfSZH|zCPj7Q`Z_C@N$D?{=p3YoL zr&(Y6iR=h$++(q{mj_CUjrP2%{2FUWRyxW;CB@a&0(WtP)fC8cTS`qPtJPh+-D=%c z;<0XR5n`oK@~C(RXmvr}hd8a|qY$;|QI?g8Ft^t?Fs01v$;JcAx4m)A+{w0x_%N-$ zp?nRoYjf%J+zn*1<+q}8cQDxLm)VtKhmJCfHmB2Ouc%na^)g?YyS}`9eQqi9N7HAD z8y;0iW@*+pYZl&B%oU-k_uq`9@XaW8$4!wl#2B!}@qVcD;ec=NsN zXpT3Kgy;3=^k*n!>XeOc=}va0i`+bplwU+Fa!{Nl~mB#&{RSDF!V1Bn@E| zCl%k-Ao^;|NTDXEnF6STxw)dx&h^tcSU(gZn75UvBN@?BkudL{;=NipGQvAfYbQpu zS`JRej;Z!Qo90&}89Tcp8e9#|@q@Z+2cT2cAss|SW6-z}^ zqmeLRkIqGDg2rG(!qExzFN>ua@%&qoBQgg<$@A!56bFUc=}&O z0m(>W5|f#M87*2e8J`TtCu7P)EUJcfH|c#tu&HotVmvyajmF|Qul~`j7Q?@j*ntW2 S+jDWmC;r1PKg#yAJO2T`uXD2i literal 9492 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 diff --git a/wolnelektury/static/fonts/WL.ttf b/wolnelektury/static/fonts/WL.ttf index 7feb6b403f97a1a4d6b3d1e4997aebb2c855d19b..6a8934a1b9a766c61896e2bb75b8afa87b177b78 100644 GIT binary patch delta 648 zcmX9)Ur19?9RAL|V;jrqottLvy4`ExpTVqM$|*_zKp(_NFv`l%+ng?Ndi_`PA)Mty zgh6o$Y8GrzK1BF33L%KF=tZ!HXb^}*MYP8tB|<#?oCkjA$M=2be82CUWL>g;8VrCF zNr>odYdd?ocj51z0!XzDQg*F5Kg<3ICoCovEol8Rt#_JA@sY7F}|d@ti3$uT3i0 zNaisn3sTtZVZkb;aEP|ff!2P!g`(y~g*V|M*#yi5cG64>&QrU?cK)MKa={0fB}D#^ zVL^;~IV57NP|$(%h++sM7{er{)V!$(<&|olB7Wyfe357PV?NIx@rV4udJUL)U#Q4l z5dzF&{RU*`#7`EXSO_4kcu)5VvCE=t66V!NoI z2&FV1O!F~MF{^H1M-Df-ZF|GDB}^?-+0(J*(b44}%f@EVJO9KxbC*?a{}dI2DB{N! zH@8G_|pfbxY{b@JR$zMQHi zF1tdI6O>PcBb91nVXHO0M*2N!;@XxupWJx!lC)4S*tWG|X1CP)WR@agukCpC%oudS z>xB1LYh_dazNeoIs)RMith=blFGwE_@uKNi&fO1hfy7@xT61g59!oPp0ai&r+#QYv z%!*?ZrTFM??BL&s$Gb#(cKc1YXDKn%RxAtFL_U{XqI+uL=sY^m(BPUF#--54f5whjw1jXGP`kyz4MJo}SjHT^DI|!Isqz#q!a@}_ zT*Wms!Z`ogD_A7f3t915tc%y;iC7hn#Ut@hJOK0Gv;N?wG{}Nid{dT2w4fFFC`6A&lTSCNPOJIExFEat1}en9_Noec~U7v4#5p diff --git a/wolnelektury/templates/catalogue/book_short.html b/wolnelektury/templates/catalogue/book_short.html index c80efff91..524617425 100644 --- a/wolnelektury/templates/catalogue/book_short.html +++ b/wolnelektury/templates/catalogue/book_short.html @@ -60,7 +60,7 @@
  • {% if book.has_mp3_file %} - {% trans "Audiobook" %} + {% trans "Listen" %} {% endif %}
  • diff --git a/wolnelektury/templates/main_page.html b/wolnelektury/templates/main_page.html index 2a63ed57c..002d45bc0 100755 --- a/wolnelektury/templates/main_page.html +++ b/wolnelektury/templates/main_page.html @@ -60,6 +60,7 @@
  • {% trans "Widget" %}
  • {% trans "Public domain counter" %}
  • {% trans "Missing a book?" %}
  • +
  • {% trans "Publishing plan" %}
  • diff --git a/wolnelektury/templates/publish_plan.html b/wolnelektury/templates/publish_plan.html new file mode 100755 index 000000000..2a6d90a8d --- /dev/null +++ b/wolnelektury/templates/publish_plan.html @@ -0,0 +1,16 @@ +{% extends "base.html" %} +{% load i18n %} + +{% block titleextra %}{% trans "Publishing plan" %}{% endblock titleextra %} + + +{% block body %} +

    {% trans "Publishing plan" %}

    + + + +{% endblock %} diff --git a/wolnelektury/urls.py b/wolnelektury/urls.py index 4ec1b3713..63a49eca6 100644 --- a/wolnelektury/urls.py +++ b/wolnelektury/urls.py @@ -11,6 +11,7 @@ admin.autodiscover() urlpatterns = patterns('wolnelektury.views', url(r'^$', 'main_page', name='main_page'), + url(r'^planowane/$', 'publish_plan', name='publish_plan'), url(r'^zegar/$', 'clock', name='clock'), diff --git a/wolnelektury/views.py b/wolnelektury/views.py index 0af07f454..3fa4337af 100755 --- a/wolnelektury/views.py +++ b/wolnelektury/views.py @@ -1,13 +1,15 @@ from datetime import datetime +import feedparser from django.contrib import auth from django.contrib.auth.forms import UserCreationForm, AuthenticationForm +from django.core.cache import cache from django.http import HttpResponse, HttpResponseRedirect from django.shortcuts import render_to_response from django.template import RequestContext from django.utils.http import urlquote_plus from django.utils.translation import ugettext_lazy as _ -from django.views.decorators import cache +from django.views.decorators.cache import never_cache from ajaxable.utils import AjaxableFormView from catalogue.models import Book @@ -55,7 +57,7 @@ class RegisterFormView(AjaxableFormView): auth.login(request, user) -@cache.never_cache +@never_cache def logout_then_redirect(request): auth.logout(request) return HttpResponseRedirect(urlquote_plus(request.GET.get('next', '/'), safe='/?=')) @@ -66,3 +68,26 @@ def clock(request): in a format suitable for Date.parse() """ return HttpResponse(datetime.now().strftime('%Y/%m/%d %H:%M:%S')) + + +def publish_plan(request): + cache_key = "publish_plan" + plan = cache.get(cache_key) + + if plan is None: + plan = [] + try: + feed = feedparser.parse('http://localhost:8000/documents/track/editor-proofreading/') + except: + pass + else: + for i in range(len(feed['entries'])): + print i + plan.append({ + 'title': feed['entries'][i].title, + 'link': feed['entries'][i].link, + }) + cache.set(cache_key, plan, 1800) + + return render_to_response("publish_plan.html", {'plan': plan}, + context_instance=RequestContext(request)) -- 2.20.1 From 6e5535d4d36c0cfd763fb16538ac054d01097fe2 Mon Sep 17 00:00:00 2001 From: Radek Czajka Date: Thu, 29 Dec 2011 10:41:09 +0100 Subject: [PATCH 04/16] publish plan fix --- wolnelektury/settings.py | 1 + wolnelektury/views.py | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/wolnelektury/settings.py b/wolnelektury/settings.py index 18e8d7573..a248059dc 100644 --- a/wolnelektury/settings.py +++ b/wolnelektury/settings.py @@ -257,6 +257,7 @@ ALL_PDF_ZIP = 'wolnelektury_pl_pdf' ALL_MOBI_ZIP = 'wolnelektury_pl_mobi' CATALOGUE_DEFAULT_LANGUAGE = 'pol' +PUBLISH_PLAN_FEED = 'http://redakcja.wolnelektury.pl/documents/track/editor-proofreading/' PAGINATION_INVALID_PAGE_RAISES_404 = True diff --git a/wolnelektury/views.py b/wolnelektury/views.py index 3fa4337af..f2d9abd70 100755 --- a/wolnelektury/views.py +++ b/wolnelektury/views.py @@ -11,6 +11,7 @@ from django.utils.http import urlquote_plus from django.utils.translation import ugettext_lazy as _ from django.views.decorators.cache import never_cache +from django.conf import settings from ajaxable.utils import AjaxableFormView from catalogue.models import Book @@ -77,7 +78,7 @@ def publish_plan(request): if plan is None: plan = [] try: - feed = feedparser.parse('http://localhost:8000/documents/track/editor-proofreading/') + feed = feedparser.parse(settings.PUBLISH_PLAN_FEED) except: pass else: -- 2.20.1 From 4b6fc6dc545a10955094bfaa9448ba070cfb33bf Mon Sep 17 00:00:00 2001 From: Radek Czajka Date: Thu, 29 Dec 2011 12:06:12 +0100 Subject: [PATCH 05/16] jplayer --- apps/catalogue/models.py | 6 +- apps/catalogue/urls.py | 10 +- apps/catalogue/views.py | 41 +- wolnelektury/settings.py | 14 + wolnelektury/static/css/header.css | 2 +- wolnelektury/static/jplayer/Jplayer.swf | Bin 0 -> 8452 bytes .../static/jplayer/jplayer.blue.monday.css | 623 ++++++++++++++++++ .../static/jplayer/jplayer.blue.monday.jpg | Bin 0 -> 23189 bytes .../jplayer/jplayer.blue.monday.seeking.gif | Bin 0 -> 3284 bytes .../jplayer.blue.monday.video.play.png | Bin 0 -> 17692 bytes .../static/jplayer/jplayer.playlist.min.js | 30 + .../static/jplayer/jquery.jplayer.min.js | 97 +++ wolnelektury/static/js/base.js | 9 + wolnelektury/static/js/player.js | 33 + .../templates/catalogue/book_short.html | 2 +- wolnelektury/templates/catalogue/player.html | 149 +++++ 16 files changed, 1001 insertions(+), 15 deletions(-) create mode 100644 wolnelektury/static/jplayer/Jplayer.swf create mode 100644 wolnelektury/static/jplayer/jplayer.blue.monday.css create mode 100644 wolnelektury/static/jplayer/jplayer.blue.monday.jpg create mode 100644 wolnelektury/static/jplayer/jplayer.blue.monday.seeking.gif create mode 100644 wolnelektury/static/jplayer/jplayer.blue.monday.video.play.png create mode 100644 wolnelektury/static/jplayer/jplayer.playlist.min.js create mode 100644 wolnelektury/static/jplayer/jquery.jplayer.min.js create mode 100755 wolnelektury/static/js/player.js create mode 100755 wolnelektury/templates/catalogue/player.html diff --git a/apps/catalogue/models.py b/apps/catalogue/models.py index f92ebd38b..112ec1b99 100644 --- a/apps/catalogue/models.py +++ b/apps/catalogue/models.py @@ -723,10 +723,10 @@ class Book(models.Model): getattr(settings, "ALL_%s_ZIP" % format_.upper())) return result.wait() - def zip_audiobooks(self): - bm = BookMedia.objects.filter(book=self, type='mp3') + 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, self.fileid()) + result = create_zip.delay(paths, "%s_%s" % (self.fileid(), format_)) return result.wait() @classmethod diff --git a/apps/catalogue/urls.py b/apps/catalogue/urls.py index 0b2a1bb41..241afca3a 100644 --- a/apps/catalogue/urls.py +++ b/apps/catalogue/urls.py @@ -29,13 +29,15 @@ urlpatterns = patterns('picture.views', url(r'^szukaj/$', 'search', name='search'), # 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'^zip/mobi\.zip$', 'download_zip', {'format': 'mobi', 'slug': None}, 'download_zip_mobi'), - #url(r'^zip/audiobook/(?P%s)\.zip' % Book.FILEID_RE, 'download_zip', {'format': 'audiobook'}, 'download_zip_audiobook'), + 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'^zip/mobi\.zip$', 'download_zip', {'format': 'mobi', 'slug': None}, 'download_zip_mobi'), + url(r'^zip/mp3/(?P%s)\.zip' % Book.FILEID_RE, 'download_zip', {'format': 'mp3'}, 'download_zip_mp3'), + url(r'^zip/ogg/(?P%s)\.zip' % Book.FILEID_RE, 'download_zip', {'format': 'ogg'}, 'download_zip_ogg'), # Public interface. Do not change this URLs. url(r'^lektura/(?P%s)\.html$' % Book.FILEID_RE, 'book_text', name='book_text'), + url(r'^lektura/(?P%s)/audiobook/$' % Book.URLID_RE, 'player', name='book_player'), url(r'^lektura/(?P%s)/$' % Book.URLID_RE, 'book_detail', name='book_detail'), url(r'^lektura/(?P%s)/motyw/(?P[a-zA-Z0-9-]+)/$' % Book.URLID_RE, 'book_fragments', name='book_fragments'), diff --git a/apps/catalogue/views.py b/apps/catalogue/views.py index c34cf29c3..90fe22fd0 100644 --- a/apps/catalogue/views.py +++ b/apps/catalogue/views.py @@ -228,20 +228,49 @@ def book_detail(request, book): extra_info = book.get_extra_info_value() hide_about = extra_info.get('about', '').startswith('http://wiki.wolnepodreczniki.pl') + custom_pdf_form = forms.CustomPDFForm() + return render_to_response('catalogue/book_detail.html', locals(), + context_instance=RequestContext(request)) + + +def player(request, book): + kwargs = models.Book.split_urlid(book) + if kwargs is None: + raise Http404 + book = get_object_or_404(models.Book, **kwargs) + if not book.has_media('mp3'): + raise Http404 + + ogg_files = {} + for m in book.media.filter(type='ogg').order_by(): + ogg_files[m.name] = m + + audiobooks = [] + have_oggs = True projects = set() - for m in book.media.filter(type='mp3'): + for mp3 in book.media.filter(type='mp3'): # ogg files are always from the same project - meta = m.get_extra_info_value() + meta = mp3.get_extra_info_value() project = meta.get('project') if not project: # temporary fallback project = u'CzytamySłuchając' projects.add((project, meta.get('funded_by', ''))) + + media = {'mp3': mp3} + + ogg = ogg_files.get(mp3.name) + if ogg: + media['ogg'] = ogg + else: + have_oggs = False + audiobooks.append(media) + print audiobooks + projects = sorted(projects) - custom_pdf_form = forms.CustomPDFForm() - return render_to_response('catalogue/book_detail.html', locals(), + return render_to_response('catalogue/player.html', locals(), context_instance=RequestContext(request)) @@ -688,9 +717,9 @@ def download_zip(request, format, book=None): url = None if format in models.Book.ebook_formats: url = models.Book.zip_format(format) - elif format == 'audiobook' and kwargs is not None: + elif format in ('mp3', 'ogg') and kwargs is not None: book = get_object_or_404(models.Book, **kwargs) - url = book.zip_audiobooks() + url = book.zip_audiobooks(format) else: raise Http404('No format specified for zip package') return HttpResponseRedirect(urlquote_plus(settings.MEDIA_URL + url, safe='/?=')) diff --git a/wolnelektury/settings.py b/wolnelektury/settings.py index a248059dc..bc63aa3fa 100644 --- a/wolnelektury/settings.py +++ b/wolnelektury/settings.py @@ -181,6 +181,12 @@ COMPRESS_CSS = { 'source_filenames': ('css/master.book.css',), 'output_filename': 'css/book.min?.css', }, + 'player': { + 'source_filenames': [ + 'jplayer/jplayer.blue.monday.css', + ], + 'output_filename': 'css/player.min?.css', + }, 'simple': { 'source_filenames': ('css/simple.css',), 'output_filename': 'css/simple.min?.css', @@ -209,6 +215,14 @@ COMPRESS_JS = { ), 'output_filename': 'js/base?.min.js', }, + 'player': { + 'source_filenames': [ + 'jplayer/jquery.jplayer.min.js', + 'jplayer/jplayer.playlist.min.js', + 'js/player.js', + ], + 'output_filename': 'js/player.min?.js', + }, #~ 'book': { #~ 'source_filenames': ('js/jquery.eventdelegation.js', 'js/jquery.scrollto.js', 'js/jquery.highlightfade.js', 'js/book.js',), #~ 'source_filenames': [], diff --git a/wolnelektury/static/css/header.css b/wolnelektury/static/css/header.css index 71dd09c95..e2867aeeb 100755 --- a/wolnelektury/static/css/header.css +++ b/wolnelektury/static/css/header.css @@ -75,7 +75,7 @@ padding: 0; height: 3.3em; border: none; - width: 63.1em; + width: 62.6em; font-size: 1em; padding-left: .5em; -webkit-border-radius: .5em; diff --git a/wolnelektury/static/jplayer/Jplayer.swf b/wolnelektury/static/jplayer/Jplayer.swf new file mode 100644 index 0000000000000000000000000000000000000000..4d50c86ae1faf32a43ff61906129cb15f80a6b49 GIT binary patch literal 8452 zcmV+fA^YA#S5phOKmY)E+O>NJcpOEtaM$eY^zJIH*0yZRfdypAVJ$fkENp|2RtZTf z6aX6(M!PebwO6}hgKTrakxb6PWH2Ti>3}11i90TF0a%&CT;h&PJ}##hF1$Ovs-Bq* zvi;uo-ur)jR8LiRb#--BcXieJZWoh(!dUrZj4c4xQn`>Z_VGEd17n+#5xudmrPaS{ zA{I|IBDJ~BNT(+o8ya@*+*!YKeSIQ1-mrG##*GbY)-|kKw;DNCr*_BF>aNxC)bVv& zgn$+;6;4Jc)6qoSPqaFg$fP&d)mc&_;hfOPOfn`!M#2qROq9lOEx{f!mZOEHYFgW}X6@>JZF2RxHEY+JYEkE= zhFKQ2deCWGSc~@~$0pV+&CJYf^El9}3WDtH?PuaI&BvClLodJS=`^>@UY~p87RIoP z=}C1$Tg$wxo;jqM4?e@0!<_V&kDK2QPyLg` zG&QdDr<2k6xYCoE7}JuKhHbNY%PXbU>SB@-8r!ag(`8m)e_BmvQcf{cX08*7rf5v* zpG-#6nx{LFNoiu-+nk7}wO#3MEuJwaoT2R=OQ^|+ph`PJ!DKRFE>qs4rA^s_^!01u z42s*G=irtG%JwreT5|V-6!w`rXichNvUE#yJep3)nJCH*CnhEmaZ_T+#O{uGG%XBK zV(OepM`J0c*)y@bdvd+GbKCCNA-Xrr?%v>xrf`r)Q)fjZTEdy$)s-0cm@;Xr?g4CC zqgpKDjA&z-aXE?$m#4IJX0i*9nHJZQsgjt$1`@PN9^X)*$3~E|DHDk%MkXfL7t%q#3?JdN)iYuVTy*$I?ld}LiSY_y#O~C%HCnO5IzSe^_^`9vnD-!`*xjGV#3Po= zC&)@p(c_-j-E64wxE3oFv_(+2d18H{y9>>!C9N(z4+BXxo}y4zE{1a?&g;|8&uFQ% z+gvKHr9FdvT~=pB8jsoFBt-{t;L0YGiE%VmN>HUKP1}LDQ4lUvSy-K;qg$gf%`F^E zSgA%g!P>WrJRO?U;-!Q%CnhFiT3VZ%5}UCo+hq1?rUt1TZ9CDXh3`x9x~3}gxGgK+ zV_r#1_j2oNp9G6EB@&^TH(n-fWpa>H98DLY?Q|^ZT#IAr%LwtN70yy)Xu#5OElsZI zqZg(GiIz-KJT~QsH)oPbluXw1HI1c?IqH!y*Hp~w;iS6}aio%A7lMdF z3L^bl+8s^NJlr2*{pL6C%_=ecWJ#31zrsMe_PmzVi z0jW&HX9YQaR$Ifo*>*?retFT9Rs>wLIr_vtp3N^L#4>a`+*sp?kQpB0?+C05|p*GaL-)pnj z0VKCd|6qS_u%{)rN-C4WP!-8(*mQcRZy={3CZEZ4Zf>x}0-fvr0RiftXGi&asmY2o;!8B}uP%}qUo<;KHmoLH)6jSsZ(+>l@asS#x5;SSA`n=guukh#Sp>xTd(X#r&FCHRTq#o|ZhO zn5hF(4w2MOX(r>M|3b*(!gRk;ggzQq(_yT79LwvDm!c6*-3mMMI?zILI+@X&cz4FN z@_uUj!)i?HPDC_UPiUmSxv4AYPT5@^!O;|pj>laCq27_MVC#TWB~meCm{*OM5!X&} zogzNDu)Eoa#v(2&=Z=;X6a-!aqy6b*w~lxRx*}CkFi0IPOJ9HW8&CMr${R(Ylg!58i7wmTKj7HLiVrbEEBduWQK|g!O z)bNh+BqdK3d9*nZOC;SB8FXw6T_lqfJ{3(x$6}gtMsVxMV6T(zMW0i|M;X_hEVo>- z3*8r2W0gS*5&qQGu;x~$)M!ja5hY|DJae?5jHZswNoo^`DXqwmm9t8MM*qCSdg7T_ zta4VVFq@Bh3ixs*M2@eZhxkhJbw$0CE<=5pICp-!_5RKNauGgrWJ#{RX!OG}mw5#n z{R-t!xP@1A|ywu4y!>L{i(^#PJehZDOz>2 z><_0mJN%K&^N(DeG%hYGPjEgRAsIn(dRr)V9$J)iAp#2n(PV;q*t5eZ&%HR{2 z*w3*dLE#S*%E(wujqjLe4doLa3KZa3GOKcF^lSCl)r|KG7vvT?Zy+%~j?bANYahRT z+5J46PR6iwcBPD{o<^j!ncQQt)mO~A@nuE9q4Ka$Qg!4Aeo{j!G zEcW$;97aDv-O0HWwVqCAeLqHrMt?!^vV4VqwZFK4!LH4Ixm!&e4s|TGq$u~Tx6u0j zOgM~6%{@#$#-KSzShPCdN11MYM|=uncZAkkau_?|J&ANH`D5OZ(&zf}qWrnulh2nG zyePPl3N@Wp!$wZDQ-l(3O_M3HP|&IvAY_Uja}GxD!yP4DD&=xHmnyjAam1DTW&y{1javWEV=dNX3spU!?SC(^S1y@#bWffNfTv^SPdag8Z zWer!>a%CM?)^lY8S5Dx{iCj5}D<^YhBUc)^atc>Aab+`Cws7TC?rGvB&D_(%OM=|f z%1hd~r=6E{a8D;M?c$zp?&;y45Qkn4XL9J{o__8b;GRM5Ig5MF=ALu7XDjz?55{FT(u@4rS>&mepb;dO*BB76zqD+pgh_&UNj5#B<02VolFUEq}m@%tTw?;?B;+*RQI34r@& z2yyH=1YG_Fem_F^CE~xr@2?SlgYa8~-y!@S;SazU{RzK+#_zx2_umoz0(|aY@%uM~ zza#ty!aorHi7`O^rWLHI28eIDU;gfAd`5#bGq zFZnWlzk={}gl`~x6X9D3Zy~&m@Q&nOD!EG#%OXr8962W-xI zU~@MBn|A`RsuO|DKMB}^lYuSV2&{Uc$L(fIY8n|kg>7PNGuy)0sq8eyn%L=#HM17d zG)PS=HEpDQJFul4z>etz=05}2v0cE9>jrjw53rgLuw}i#YR?2#*9UBQKd==8z*Y_d zTXhz&z}di7p98FZE3k%bz}B1#Z0&i#)(rt$KMZWc2(S}Ift{!VJ82Bq$u90LW2aPy z(Uy`D5oXcIm^%4qoEn3gC^g%u*+ET=nh9#+)Fj9)lhm9~O_G`vHEC)x#4^PgXFGvy z+68R$ZeUw30CwuffSqgadm)?AIpN5z_F}#k;h%ZI+k6E$Kf(y zm*eLOV0-X$C9tc2%qUEDHIVhL5$(02y$*nNUXRTgH(=9sBR1VPVbgOnHlbUv>Ae-3 zGjGGD?{;kZ@4#kYFE)dBVsqAA*qn{7I0s#^6-~MgO?obx^gJ}_5Sn!OZe$oin~tJQ zRkZ0C+BA$djogC++P&DI5f`&YxDOAujGfI(?kDU%fCoU4E5#x21K0Jl&z}*`4!JsA z%$S!QnqL#CVV(0Oqbl3MMn4UK_aXEdfb8gJLH``+pNGJYVV_034*LQ>g?)xEtIr#9 zTAg2jRwt-o?6Nw(2!Wr&Q)*=N4H*3r=wAl)D?s>HLH`=4ZwlP4e;u;=y3jX3BC!STOW0WLpqIvt)-BO?NH=qgr%Ti>|D`crQZe zIBDoD=zI&6eSa#@`&SRLdG^;PIajM@2^ZOosm)$3; z2L*DaLw%o+TJ<~T=ezV-Ever#$?wxAr=I!)BJ0!-Xg^EEzI3!unf^mWHkuY%FskUU zA$hV%Ru_|1qmuq3B#*Vo(1$=bS5hRm=Mg~PW7m)IF8u`bpMvjZMp$4~R$mtS8F~Zp zKvu7{@zq(q&c^Gr`f?j@$m%O>93`%_@wHifm5r~<>H!;HpVe2}_=c=rZ{byR97Pi=P1^np3}mK^Zmi=M3w9S_;MPz`uJNYxQlixhIp4&2&3 z60-ZQ!y#dx{V2O3Z>~p+$rp>smx{?Ji^->o$w!LG=Znb~Y|?vGu_}j(RXJ3w%AsOa z4i&3%s92Rl#i|@CR^?E!DoCPS7aB7n`WM*hJOECaNwr zQFXD2stZkIl20SKR#3yM-M9`_lh?A`~7H8{>B@~M~$nVpkcBpO725y#+tF9g20d=;31wufzE#OvLfNPe3+m0Zh_6P!Qw*|N*aC@pS00i!!t{fw_xIqt%E~U}E z=ICM{7bm#&9M+Cp^kJ-olvqhzcUsFMU#Z}mUiJ~+-?|luIfpY=nwxRgk!Nfxn6Zs! zl+lb@nsGPI2;3vtvs~t^)^p7n^kuA^xK|MOy|(;$)8akSBbDXmjL>-?400Ytiwc_1 zL=)~Sm=Kxv%@xcw%;XkdZzAm`BhtPD`mYdql#pK|@)#k% zLF5yJ{8ngs0FCIQt!$yKJTA@J!7_U0+~URVbVFzz9DP4?6qnoc6$~=V!^-k7&jy~L z*IoY|uJ@F*kI>%>$@r!pM0g(|EK}n&*=pFcD}5N;SD37V#4>LMnq9vX^n-`>yiYy8 z8_>pLzB%pTOjyay$T4CsA5+G7=_;EW*EW$cj)P88$`ml>e5gixIP<=@&Sb?NzTe{h zJ>;|f*y_kr7*TfD8g&-#|qv07>kdmb_~1#1||_vv`e zl@zyUA-#c^Sxmo+X~pyy0nAZOcQ$Dv?H1Mi~Yk9HAb9+x=j6@!+X*p1>P~zxah;5fifVY$` zEMk&ZqShrXdV6L@x_)Nn>6sbCP>DqnX3BNhH5wb`YexSC){Oou!oMN>JAw!QJBKY1 ze?}#;FV5%HOJv;Xt+cP9Q#wy!Mq_qpbLVDeY|0Luy6n~Yb)Bb5#+K|*Q)d%1PRkB8 zcQ!NQbj-b%{SD0IyQPo_7G&kER;J#TrBK~srFMPh>lw7jkV&>$L)UC$D-W(*vuXmC zxTLI~HfU{BZyYVx|Ax$+rV@=9DgKV+8F}(Qkn9rVQ1djNBHX&(7TSp`woR)VwzcYb zHq_7BV1473f51Pn9;dgcf{qE`BZeGx>NxJjPi9_*-32(IwY0e!pAPkYA|wws)f6o|=&wzB*MDjwNJ8EFr#bR4ZzSG>~K3Rk+& zvTCT89@^t+7pv&%wAC;*A=Wm`-JlUnAukp0r3P?HH6?E)q->B!_-)f0pMG<4GFpZwbwvMnea!%-MvfqF>syir<3X|;+&RJoKjbm&m z5$}RE@mXI5vu4qBe9X6zJ^@nr7_mUD8qKTye6iZA^K5m6Y%dhE)jN>wIKkGso0<7T zTj&C8@m}B|-x4}Us%@v#t3k%V_ljih%$PbjGy~JEGXQ&6Qj#Uq9-s0pHaBC?6K7vX z^K{Tnp_8J=%p?f4-;TMRxo(NwlQmPdHhp4hWu+~bOk?B5Uf;7vHP|XKB95ee7!Qcp zF1G$u!PdPIQ;3*YUSTc21MkBCc-t+~$O^)6C<-@tP@0$j#VL3}$X5FM; zCe^TV({?7QCXVbDg_)r$aTIfyexcQDk09GL3U?wmxdiudxoNRt$N0iWbw)|f8D3wZ zv7Mq29x$Wd0hETLD+<(hAo=cI`r(B9mp9;ADyUDvi%3%T*F-RHG0m6PXL|v@!`$@& zkiJfuik(RdA45DP z7)F?yMgbcr>Yr@hk=ZBMBk)H3a)~lNk=8frS4d{p!|gRxo18%Ng@TRx9&#=>+4e~0 z_=#YT(~%H^A?n~(1#_H2@{r(ZmXhK7}%jxdgZ z?}iaY*p9FRA%-x45XXpTB&eCB=6q_B)TF3MBS$7X$c!lzy;+iE=UlRiw&y)sE<24& z)EYXDZJ}l9!NqH^(&rRDi0!qpi5wQ+i#tD+nCa23&8vYjPP4m7yV_iLQ(-r8*NX1b z(R>asn(r!MK7Nk0#BgDMGqGQd0buxk2;2w8{V@Ch1Ren60T_M|0{g&t5Qg_bU_Ti9 zVE7>jJOai;FuWfEkAblth98E&<6t}t!;e7VNiZIP;YT6xG#HP<@M92o28_pG_!F4l zg7FC$ejEbNgYh^FKLLRk!FU3OpM=26U_1%KPeI^SFrI?pry=lZFrJ3tPeS0cV0;pW zpMk*V!FUFSpM}5|z<3shpM$^~U_1xIpMt=b!T1ymKM#Sgg7G{IzW{+Z!FU0NUxdIn zz<3dcUxL85z<3FUUxvWjV7v^&uRtIR#w#%VDg+LI@hS|z27zyb@fw|x7FlsBb11o{ zs-we^Jz!jveb=}chk^ z1+r3LS_-@?1+Ip`HPCvk#F48F9U;%LlVLahca8&(tb+2}^NDW<8Vadvpdi(~7U;YWT>+g}pq$H5^UGnhLcb0#`5jiM4T$u_W#MLi zat393o2&>W9!$LTH8A&B`&lqEt@jU0{;s2}>jojCi&C?Oqo!te!;y2d?qc<>E>Q1k zTfH9QaZx&Uv!Fwi##9rXlR7Iu>!ledlhkTEKcg2WbQ?X9*P3foStC9%a=SHRu6QQB z2|7ipb!rzi2?rD;1-AFp69$!bRtm07UA0W=D=a zFs@mpPA=%g2-02Yt zS@Z5~J#q=VP0Cdklqgo_ONPtAAKEK{SudPR^}@UVvw8uw-1h&zUijwGFX)!SRn1a% z4+i$ool@aBI9d%6qv&Zxjq>>gHH3&ALM+u1_RcFS&DDeg^vkDM#w;|5`!0zRV_fvV zttHN*T4Imme^pE1k$9@W2bn1|jkJw`uBgga8f`S7-!0KE+y6gni`R}=TkJc!SAqD@9WJpIy$Z}qW#s=_sf^MsKIO=jido2^lDr z=`j+y#4b)sty$*b$yF%fT!k`RSfPvwA6XTONm#2yBp!PG2M`Fe4x^E-Sk@4=_?_w+9x_agFlK;%BD;OE6!H6xrdMJ+Om>yRey z(fcLSIhO*F^xHO0E$Y2b690Hkzth#`ey1Z1QQ5LwB7s?jsCc)sqU^#{^y$K=Gp|_v mKNdVY<96ltz|ku=*{a+YA%E!{`X5f&{}1)k5dTl-RQ~qVeMFD| literal 0 HcmV?d00001 diff --git a/wolnelektury/static/jplayer/jplayer.blue.monday.css b/wolnelektury/static/jplayer/jplayer.blue.monday.css new file mode 100644 index 000000000..0d90a22b1 --- /dev/null +++ b/wolnelektury/static/jplayer/jplayer.blue.monday.css @@ -0,0 +1,623 @@ +/* + * Skin for jPlayer Plugin (jQuery JavaScript Library) + * http://www.happyworm.com/jquery/jplayer + * + * Skin Name: Blue Monday + * + * Copyright (c) 2010-2011 Happyworm Ltd + * Dual licensed under the MIT and GPL licenses. + * - http://www.opensource.org/licenses/mit-license.php + * - http://www.gnu.org/copyleft/gpl.html + * + * Author: Silvia Benvenuti + * Skin Version: 4.0 (jPlayer 2.1.0) + * Date: 1st September 2011 + */ + +div.jp-audio, +div.jp-video { + + /* Edit the font-size to counteract inherited font sizing. + * Eg. 1.25em = 1 / 0.8em + */ + + font-size:1.25em; /* 1.25em for testing in site pages */ /* No parent CSS that can effect the size in the demos ZIP */ + + font-family:Verdana, Arial, sans-serif; + line-height:1.6; + color: #666; + border:1px solid #009be3; + background-color:#eee; + position:relative; +} +div.jp-audio { + width:420px; +} +div.jp-video-270p { + width:480px; +} +div.jp-video-360p { + width:640px; +} +div.jp-video-full { + /* Rules for IE6 (full-screen) */ + width:480px; + height:270px; + /* Rules for IE7 (full-screen) - Otherwise the relative container causes other page items that are not position:static (default) to appear over the video/gui. */ + position:static !important; position:relative +} + +div.jp-video-full div.jp-jplayer { + top: 0; + left: 0; + position: fixed !important; position: relative; /* Rules for IE6 (full-screen) */ + overflow: hidden; + z-index:1000; +} + +div.jp-video-full div.jp-gui { + position: fixed !important; position: static; /* Rules for IE6 (full-screen) */ + top: 0; + left: 0; + width:100%; + height:100%; + z-index:1000; +} + +div.jp-video-full div.jp-interface { + position: absolute !important; position: relative; /* Rules for IE6 (full-screen) */ + bottom: 0; + left: 0; + z-index:1000; +} + +div.jp-interface { + position: relative; + background-color:#eee; + width:100%; +} + +div.jp-audio div.jp-type-single div.jp-interface { + height:80px; +} +div.jp-audio div.jp-type-playlist div.jp-interface { + height:80px; +} + +div.jp-video div.jp-interface { + border-top:1px solid #009be3; +} + +/* @group CONTROLS */ + +div.jp-controls-holder { + clear: both; + width:440px; + margin:0 auto; + position: relative; + overflow:hidden; + top:-8px; /* This negative value depends on the size of the text in jp-currentTime and jp-duration */ +} + +div.jp-interface ul.jp-controls { + list-style-type:none; + margin:0; + padding: 0; + overflow:hidden; +} + +div.jp-audio ul.jp-controls { + width: 380px; + padding:20px 20px 0 20px; +} + +div.jp-video div.jp-type-single ul.jp-controls { + width: 78px; + margin-left: 200px; +} + +div.jp-video div.jp-type-playlist ul.jp-controls { + width: 134px; + margin-left: 172px; +} +div.jp-video ul.jp-controls, +div.jp-interface ul.jp-controls li { + display:inline; + float: left; +} + +div.jp-interface ul.jp-controls a { + display:block; + overflow:hidden; + text-indent:-9999px; +} +a.jp-play, +a.jp-pause { + width:40px; + height:40px; +} + +a.jp-play { + background: url("/static/jplayer/jplayer.blue.monday.jpg") 0 0 no-repeat; +} +a.jp-play:hover { + background: url("/static/jplayer/jplayer.blue.monday.jpg") -41px 0 no-repeat; +} +a.jp-pause { + background: url("/static/jplayer/jplayer.blue.monday.jpg") 0 -42px no-repeat; + display: none; +} +a.jp-pause:hover { + background: url("/static/jplayer/jplayer.blue.monday.jpg") -41px -42px no-repeat; +} + +a.jp-stop, a.jp-previous, a.jp-next { + width:28px; + height:28px; + margin-top:6px; +} + +a.jp-stop { + background: url("/static/jplayer/jplayer.blue.monday.jpg") 0 -83px no-repeat; + margin-left:10px; +} + +a.jp-stop:hover { + background: url("/static/jplayer/jplayer.blue.monday.jpg") -29px -83px no-repeat; +} + +a.jp-previous { + background: url("/static/jplayer/jplayer.blue.monday.jpg") 0 -112px no-repeat; +} +a.jp-previous:hover { + background: url("/static/jplayer/jplayer.blue.monday.jpg") -29px -112px no-repeat; +} + +a.jp-next { + background: url("/static/jplayer/jplayer.blue.monday.jpg") 0 -141px no-repeat; +} +a.jp-next:hover { + background: url("/static/jplayer/jplayer.blue.monday.jpg") -29px -141px no-repeat; +} + +/* @end */ + +/* @group progress bar */ + +div.jp-progress { + overflow:hidden; + background-color: #ddd; +} +div.jp-audio div.jp-progress { + position: absolute; + top:32px; + height:15px; +} +div.jp-audio div.jp-type-single div.jp-progress { + left:110px; + width:186px; +} +div.jp-audio div.jp-type-playlist div.jp-progress { + left:166px; + width:130px; +} +div.jp-video div.jp-progress { + top:0px; + left:0px; + width:100%; + height:10px; +} +div.jp-seek-bar { + background: url("/static/jplayer/jplayer.blue.monday.jpg") 0 -202px repeat-x; + width:0px; + height:100%; + cursor: pointer; +} +div.jp-play-bar { + background: url("/static/jplayer/jplayer.blue.monday.jpg") 0 -218px repeat-x ; + width:0px; + height:100%; +} + +/* The seeking class is added/removed inside jPlayer */ +div.jp-seeking-bg { + background: url("/static/jplayer/jplayer.blue.monday.seeking.gif"); +} + +/* @end */ + +/* @group volume controls */ + + +a.jp-mute, +a.jp-unmute, +a.jp-volume-max { + width:18px; + height:15px; + margin-top:12px; +} + +div.jp-audio div.jp-type-single a.jp-mute, +div.jp-audio div.jp-type-single a.jp-unmute { + margin-left: 210px; +} + +div.jp-audio div.jp-type-playlist a.jp-mute, +div.jp-audio div.jp-type-playlist a.jp-unmute { + margin-left: 154px; +} + +div.jp-audio a.jp-volume-max { + margin-left: 56px; +} + +div.jp-video a.jp-mute, +div.jp-video a.jp-unmute, +div.jp-video a.jp-volume-max { + position: absolute; + top:12px; + margin-top:0; +} + +div.jp-video a.jp-mute, +div.jp-video a.jp-unmute { + left: 50px; +} + +div.jp-video a.jp-volume-max { + left: 134px; +} + +a.jp-mute { + background: url("/static/jplayer/jplayer.blue.monday.jpg") 0 -170px no-repeat; +} +a.jp-mute:hover { + background: url("/static/jplayer/jplayer.blue.monday.jpg") -19px -170px no-repeat; +} +a.jp-unmute { + background: url("/static/jplayer/jplayer.blue.monday.jpg") -60px -170px no-repeat; + display: none; +} +a.jp-unmute:hover { + background: url("/static/jplayer/jplayer.blue.monday.jpg") -79px -170px no-repeat; +} + a.jp-volume-max { + background: url("/static/jplayer/jplayer.blue.monday.jpg") 0 -186px no-repeat; +} +a.jp-volume-max:hover { + background: url("/static/jplayer/jplayer.blue.monday.jpg") -19px -186px no-repeat; +} + +div.jp-volume-bar { + position: absolute; + overflow:hidden; + background: url("/static/jplayer/jplayer.blue.monday.jpg") 0 -250px repeat-x; + width:46px; + height:5px; + cursor: pointer; +} +div.jp-audio div.jp-volume-bar { + top:37px; + left:330px; +} +div.jp-video div.jp-volume-bar { + top:17px; + left:72px; +} +div.jp-volume-bar-value { + background: url("/static/jplayer/jplayer.blue.monday.jpg") 0 -256px repeat-x; + width:0px; + height:5px; +} + +/* @end */ + +/* @group current time and duration */ + +div.jp-audio div.jp-time-holder { + position:absolute; + top:50px; +} +div.jp-audio div.jp-type-single div.jp-time-holder { + left:110px; + width:186px; +} +div.jp-audio div.jp-type-playlist div.jp-time-holder { + left:166px; + width:130px; +} + +div.jp-current-time, +div.jp-duration { + width:60px; + font-size:.64em; + font-style:oblique; +} +div.jp-current-time { + float: left; + display:inline; +} +div.jp-duration { + float: right; + display:inline; + text-align: right; +} + +div.jp-video div.jp-current-time { + margin-left:20px; +} +div.jp-video div.jp-duration { + margin-right:20px; +} + +/* @end */ + +/* @group playlist */ + +div.jp-title { + font-weight:bold; + text-align:center; +} + +div.jp-title, +div.jp-playlist { + width:100%; + background-color:#ccc; + border-top:1px solid #009be3; +} +div.jp-type-single div.jp-title, +div.jp-type-playlist div.jp-title, +div.jp-type-single div.jp-playlist { + border-top:none; +} +div.jp-title ul, +div.jp-playlist ul { + list-style-type:none; + margin:0; + padding:0 20px; + font-size:.72em; +} + +div.jp-title li { + padding:5px 0; + font-weight:bold; +} +div.jp-playlist li { + padding:5px 0 4px 20px; + border-bottom:1px solid #eee; +} + +div.jp-playlist li div { + display:inline; +} + +/* Note that the first-child (IE6) and last-child (IE6/7/8) selectors do not work on IE */ + +div.jp-type-playlist div.jp-playlist li:last-child { + padding:5px 0 5px 20px; + border-bottom:none; +} +div.jp-type-playlist div.jp-playlist li.jp-playlist-current { + list-style-type:square; + list-style-position:inside; + padding-left:7px; +} +div.jp-type-playlist div.jp-playlist a { + color: #333; + text-decoration: none; +} +div.jp-type-playlist div.jp-playlist a:hover { + color:#0d88c1; +} +div.jp-type-playlist div.jp-playlist a.jp-playlist-current { + color:#0d88c1; +} + +div.jp-type-playlist div.jp-playlist a.jp-playlist-item-remove { + float:right; + display:inline; + text-align:right; + margin-right:10px; + font-weight:bold; + color:#666; +} +div.jp-type-playlist div.jp-playlist a.jp-playlist-item-remove:hover { + color:#0d88c1; +} +div.jp-type-playlist div.jp-playlist span.jp-free-media { + float:right; + display:inline; + text-align:right; + margin-right:10px; +} +div.jp-type-playlist div.jp-playlist span.jp-free-media a{ + color:#666; +} +div.jp-type-playlist div.jp-playlist span.jp-free-media a:hover{ + color:#0d88c1; +} +span.jp-artist { + font-size:.8em; + color:#666; +} + +/* @end */ + +div.jp-video-play { + position:absolute; + top:0; + left:0; + width:100%; + cursor:pointer; + background-color:rgba(0,0,0,0); /* Makes IE9 work with the active area over the whole video area. IE6/7/8 only have the button as active area. */ +} +div.jp-video-270p div.jp-video-play { + height:270px; +} +div.jp-video-360p div.jp-video-play { + height:360px; +} +div.jp-video-full div.jp-video-play { + height:100%; + z-index:1000; +} +a.jp-video-play-icon { + position:relative; + display:block; + width: 112px; + height: 100px; + + margin-left:-56px; + margin-top:-50px; + left:50%; + top:50%; + + background: url("/static/jplayer/jplayer.blue.monday.video.play.png") 0 0 no-repeat; + text-indent:-9999px; +} +div.jp-video-play:hover a.jp-video-play-icon { + background: url("/static/jplayer/jplayer.blue.monday.video.play.png") 0 -100px no-repeat; +} + + + + + +div.jp-jplayer audio, +div.jp-jplayer { + width:0px; + height:0px; +} + +div.jp-jplayer { + background-color: #000000; +} + + + + + +/* @group TOGGLES */ + +/* The audio toggles are nested inside jp-time-holder */ + +ul.jp-toggles { + list-style-type:none; + padding:0; + margin:0 auto; + overflow:hidden; +} + +div.jp-audio .jp-type-single ul.jp-toggles { + width:25px; +} +div.jp-audio .jp-type-playlist ul.jp-toggles { + width:55px; + margin: 0; + position: absolute; + left: 325px; + top: 50px; +} + +div.jp-video ul.jp-toggles { + margin-top:10px; + width:100px; +} + +ul.jp-toggles li { + display:block; + float:right; +} + +ul.jp-toggles li a { + display:block; + width:25px; + height:18px; + text-indent:-9999px; + line-height:100%; /* need this for IE6 */ +} + +a.jp-full-screen { + background: url("/static/jplayer/jplayer.blue.monday.jpg") 0 -310px no-repeat; + margin-left: 20px; +} + +a.jp-full-screen:hover { + background: url("/static/jplayer/jplayer.blue.monday.jpg") -30px -310px no-repeat; +} + +a.jp-restore-screen { + background: url("/static/jplayer/jplayer.blue.monday.jpg") -60px -310px no-repeat; + margin-left: 20px; +} + +a.jp-restore-screen:hover { + background: url("/static/jplayer/jplayer.blue.monday.jpg") -90px -310px no-repeat; +} + +a.jp-repeat { + background: url("/static/jplayer/jplayer.blue.monday.jpg") 0 -290px no-repeat; +} + +a.jp-repeat:hover { + background: url("/static/jplayer/jplayer.blue.monday.jpg") -30px -290px no-repeat; +} + +a.jp-repeat-off { + background: url("/static/jplayer/jplayer.blue.monday.jpg") -60px -290px no-repeat; +} + +a.jp-repeat-off:hover { + background: url("/static/jplayer/jplayer.blue.monday.jpg") -90px -290px no-repeat; +} + +a.jp-shuffle { + background: url("/static/jplayer/jplayer.blue.monday.jpg") 0 -270px no-repeat; + margin-left: 5px; +} + +a.jp-shuffle:hover { + background: url("/static/jplayer/jplayer.blue.monday.jpg") -30px -270px no-repeat; +} + +a.jp-shuffle-off { + background: url("/static/jplayer/jplayer.blue.monday.jpg") -60px -270px no-repeat; + margin-left: 5px; +} + +a.jp-shuffle-off:hover { + background: url("/static/jplayer/jplayer.blue.monday.jpg") -90px -270px no-repeat; +} + + +/* @end */ + +/* @group NO SOLUTION error feedback */ + +.jp-no-solution { + position:absolute; + width:390px; + margin-left:-202px; + left:50%; + top: 10px; + + padding:5px; + font-size:.8em; + background-color:#eee; + border:2px solid #009be3; + color:#000; + display:none; +} + +.jp-no-solution a { + color:#000; +} + +.jp-no-solution span { + font-size:1em; + display:block; + text-align:center; + font-weight:bold; +} + +/* @end */ diff --git a/wolnelektury/static/jplayer/jplayer.blue.monday.jpg b/wolnelektury/static/jplayer/jplayer.blue.monday.jpg new file mode 100644 index 0000000000000000000000000000000000000000..adab53ff7b6ea893972ce1f2c24323d319b8e5ea GIT binary patch literal 23189 zcmeFYbyQr>mOt8ffB?Zl0)d18!66VVxI;s*#%UzDrIE%xApwGG1Hr9vhsFsB?$$_f zcWB)0@x3#5X71dXx87Upy+3|y-RiaaoKtm9$=+x0`qZcPx&L#&2za6(3zP+*p#uPB z4?n>DE`V6t#njdVfCj(>0062FSttOZgqgjGIl%1KLk`;g4B#68{XY`M!x;wV!#@Tl z7Uo|G3mfZi2^$*+=MfGLHZCq69xm?Ve@ti?7#LWXSh(2OxCHpP_yk0R4~dBAZ#Vt> z;qkv+{o`~0adrO-K>P?T3eb&#MhrkFM#CURyKe`~Jaii!1MML_^z`2n_9ILz91L{a zzw-W8^!EY)01F!p0}~zR(G$Q!cOC|ShlBeV2OH;Mj1Pl(C?zJL#o>}5ReOyj8zYBl zQ9Ltt*oL|SRA_K;7tCS zo6MPdbGSfZcuzSy@Co)vL&q3sNKl@6ECSkW$ZZFsnuL8 zQYKq{^gG_NQJT;~|2`V>=o*9i(d44S58apE6F&RTHp4@mm!|FaIMLQtN#cK)*iC-< z%^`=gBr3e%aXNZ^OKbuFtc3h2p>P*@iutMX;i&rFoF`{o#teyC{_Qvx~TUgB{&1+XS zd=>&bg{ux1&Z#{tTjOMip1ULi;+|R4*KuAz57+=F({>rzW1fXs$rSIT6syk@SulHJ3U(N7>I^m(`qvd+2be2R|yYFpXk`H1p}@@3*- zUHi+|nfSD^;!Ie83^Mf3s(5IBKg}MYm@#tVPv{tNXWQTT^L>|@ZW+sRe{9e`i|^@% zoZ(K~&u`jL90c2kw4bojk9Un2E#=S%IF>z&7`Sl{Acot3{QRp=a&9nHsX{Q+naz{Wo?QtD+M}i3dQniXG5L&s zQGYL+xDfHs+;*IAytdOl;HkScyAO*&7jEsM>;|KeZU1hcpSMXB@#C~=co#i6+F=|~ z^9Re&FBM8t%FP2w?mB(*6PCgDP`*)A{p7DO{BN6Q-K2J)U}DJOTr7Q#9c@jNRtb*o zFl7kBM%yBj8j@2tCFFbZX$IEg!KL(*zHk7WHEG2oMT+A$R66F*gfy>P%=3Fd;>iAI z6wTK$+gk2k$M@c6oWDCzB3($z%Nf zkduosAI8Sdnbd_{9eMqun*P$5TBF}i81lwt2vt#N`~Dsvb)~rXq14sQknw70SN!|2 z+dTksG_)Ihhakt(>81W|z+Gg%&Ru~6&)q%prwS({KWn>S{e(`IhRO<584*^HRb=TP z*sURRr$At=aILUHd2T@NQDmOK=<%Mt<9J!>&~i9&4`}EqC{Dih+Y&>d*H5#%+wX8z zymN~7L%F)#12~9IW46NYx`?{O7N6VS1M)(zYVH9j;uPdvM4#C2ynBF~funiLoZi*p zL6)MxUGF{MWaCWtvgA`Tw00<*W#o-CIMkMgkW@76c94b75?zXlPNWI@LDkdR}t#I^G#tahfQg-Z|0iCQ_eJfnAZO~1zVvGqHBWiX?%U1WJC>leksu@QIC(`YEU zEZf>d^-tEMO6K_*pMQ1$cHl6NKO=a?A%qLO2$r}Wf0w)!9#lVqY=CpZ$!EWO>LaBn zgWK0F;kiGLjMXt+drAfK2M@|cI$81T>{&OzVq(=mVY;}nJga#2ESETZ$x&FV!%o^I zAMLfx^(EmwAa`ly9Q*5H217M=t{(p6ELWVPvmwSqi?4-x)UE?e9}HM6i+}B}7S~>8 z!e;LQooSS;MhfYUlGYb$^*zIS>#pW4&D^@*Yb0*WYF0mIiL$TkBcJDCm?X!f`&n+9 za)RHdF^K$`v8~%M!sAV_^>VW{7#uqqLuw0@NxO_$*E4(INia_aW>AJ!xJ=bkh3e~h zN94@2w6^P|=4C2y2p4{%!(Fpu{hG<~D|Dq6B$m?Dhi30c`DB5B>g8s*iZ1WF6xYb- zNn*$5@^&&-k@tZ4EyLY=fEo8qjM%DV)m%#Tz_}X*dgjPBW;O9HOaxvZNc!LyWIECH zRP;j3d6qD4HDVtuD-7fn@?cq$0a4VsNPovUrw7a8{Pn{PA|7f`O%qKX^&gfs{`!Bh zEX}4N{QA<>;z+99Nv?Km6yG^efj*Ny?z{~IV^l}z(Zv25>!c#JI4$+R1?ibG{51(R zG_LtQ|JoAt^H9KsZ=8Xm5NLy}XUL6IuFZ^M=xgD26$v!N&t5xw{V)q2YUD+!%#)`( z%j%i>&I!22UURm3c-&-X>DVF`Rc&Dd?!<>1_+15V3!BTAN|v(=$9vGeMAK!)@iM4X znopL&De5qV`+!=Eu7-Fx+%GJrsZ#W?*ei?mL)lCoHqy`B{X_8`XygU&^g%vDZ|=?G zbmK&W0Sfw6k5>>MBDiRKJF|eQsbCqKFR)BWgR3kiB}__0xML?7uVQ8m zjwgt4{D^%_9C?&jWLY)TC-YoYWDLULrOusaa$`J=y>xbl~f>iX4wlas;2*sjhL{ggg z*7HW4wL_j#+oG85cvP-=tLEOuCmIEad0C4}!^M0;F((TE5+9Ag@flM(UD~ml;~u!V-l|>Mc<(fcPNv9zK%_9Wgdty8X6_BNv9S? zJJeG<`jY>wO&W$cDSyl(QG{>AAEShGGk#lFmrRluj4;as8MB%`mUFk)!kJ1SoANV> z>#D5`nY}8KA)_;ka;#2CP5s_y(d;$d;j!~o4|3iBTzI{qR$0?F>@*-CR8M$pQm~`I z*(7)Jqq#$;b=IAM(MXHi;CN`-kGF-t!cTc4M$7Lq`P}E(alhYLZSfW8=G~U?@OqMR zsZ1q1U9iGSC&vry3i0dp@(I0Dp|miMb@f3l#DkdL{xE5pOz?<@@M|SlH8BXqK5wr9 zE&F{N9mUfSYM`O^)GAgsde#Q7Vy|$Y5;r?sa1-+~$t* z=&0sQC?+()qf3s8U-xUS+dC-WkmafTUe;@(C$Y?3Zu#DV+}O>n*5B>s(Wi}SnfdR? z3eEzFS*PGzinjtXoQL(=jmzqB2b&j#WEGTuCX=QAjJ|1WXiFfZRfVx9%sA$TV6WNh zX-K+8FsirKst~!7sKx2ogRFCRnw(}jrs^pKlsQZkq^!x4M#^){|+RXj*=xLPvXe61&o>nfb0AWyS5-2BqwHG6{AU=QC+ znB-^|M)p2KK%U_ep^e-x7HxXSyz4pRbL|`E9`{G0qcUhO=C4 zA)zwTTg$Uo;`Ou3Gi!mM+=(2f+}C(9&ik5N-|`){Uq#uFRlw9p%n;o{Np@1<2s*yU z-4Iq<4nFoRXIgP;JhvQHsWw$2`OKJcs5lJTLcv0x`i4M-2HkeiXGse4YUyeN{~kbX ze_foE)YCF~7kjPBnwm8kcvpE%%Kx6?Zo^XSXz?DP%aORS>u}?AhZcVixN>hPvcFp* z8|+ag`oT;O2Uh6It1e0qr>y94%NLj)Y;7s8ijE3;0U2MEk<)LEC&!&U2Oey^u!r3& zu$Ra)@R7yD$YFhOWO*U@U6Arx)Sm3v8%w)pUxK&W*QJ}0Y4tgrS}b~@4!NfH{G(W( z{Vl-*j->XxuGY)E2h&#Gx&GGLC)Coo*H43J?`(g+U(@vU9ZF2g z&qM}UeGmAfxC6ci6riJ~{WQU1mG5r8sJRK}bt+9CQIPPFFaG%-7CTXBN*nKR0&6FU zW)J^T#dgKtG~p$je<(KLXM*YWLuYrl?9tCZkN|Y=G4A{xhdqV3w!P+BH?6%S^apjk zz=VF~6^0A!hsY@%nE84fTStX=q>~>)zWh@3I^C};QW88}l4A5;ix+Y1aIhr^*^Ya& zgiBPYK&5q((@fjt??#CExtuVCH`5;|Om65{Q$Bx?*V_I?8a$w?SEI% zGrAvBzv;wtGq!H&8%+Hw+75>18C>RGUMe<6gY*{vxuqguCmjzjHP$_ygZf(zw_O@@Zm5~ZXuus*?ZWb;vWZNVclsF?Z)rgm3SCY^hcb@`RrW$E$ zCPaDI;8LiNWe;WUmY(G0P@j{0Alo^oO|uB1iz%;r+RAs#)UbR;G=Av%oemWf+oz4=ipfLO5kvQh7;xwZF` zS>~toj9Cy-wNek~L7Ba@D?|K9v#OAD9EViVqQM{cZ{Na6OM`;Pv_Gx9zzUYAMk&K` zQ(?^pXQwDD=}(MoNK*U|cHLU7!u$=!hoJpN}oXaD;u5V7|Y97y_)#k z>r|g=1U8Eqzw>G9gwi#wFCVPJQJd=TjGJBAfPlCIu6siU)zzi&?Ps z@~BD=OQ&JR>T8!T+_6zFh*%WEx7$^{&Z>vUnUdzoFt(>8u-Qa(TTXey3@$iufig!5 zkxhPnHe6*okaR`M+pDey$F$CkY|f0+{n?gcy_1!b^pRLoO9Rfcwe-bYbp^&{H0h+S zhKjUK-_R?Z7U*4tJu-6CVhVCpB~;SPl`TB(KS#@*Gw6c%N#3y~DsZ#}SDq0hzlEVp zPHQvNxWqhbn-h~2f~+!t93W)`4CIS0BE9s(8-XWkF>yKLd`V+jX-V3O&J!>m3Q0Uk zV=4JfMS?HqBrI4B@p1Y2=30+K{)1QfyX$8i1okrxZ-`hRR(9ue-{M5T>S|UhHHTrv z#oHsMfm$lTzO|AxccM;pyMJnHCgWyuOn=Sa1JISCEV-?f^GgN| zO#9Tf)Dq}!dv|og1jju$vg*4YmnRkoHi@JIk0rUyc)Wlq)m3$^e#>uO1U*Hinln3n z6nlz*`HrR1`C8u{jm+Es@^A+qfI)3wTTkxd#$*N7pU!=@f9Q)n`ZGg$u#LG8$^tnh z3_tN%@vuDo@tjBN?e44%HdB)AwNfo1>D|3u%2{z*&6EWtqGZxU?@Bq`CO+`J{^)mx zQi|Ha)ANj@lF$X*1E{2Q(_RwbC?#@- z=ROJw8AH)p>N>zUbt?s8k!|M2;9q5@jGLv7TXX&CS6a>Qom5zT!KN^WNQyVDufXHH z#&ze|@wJH@teJRfAdMJL1SQR$GgEy>lgPw0vhqaGzQp<{B_^hpr^5AhTq;@YbhumE zw@DXORP=$q$oezql|x>a>f%lEJa4q61r4{2vdi6sb33Z3uBdZmoS>Y5H^0e#Qcg7hj=hyHEQSQ^O0T2cGmTla#*&PLD!!cK72TTXS!~9meAnjjO zIv#sp_`4nT0}!X3FydA+)g5D&S8Ql?Wcg2=$=)h}DpN@LLc|WXd)Z5vOS6K}Se*Te zHC&2#BTwLsBP!$5c?@_KthesIeJv36olozf<4K=d-b4nixP6GwaAQC~B#A=##6e>_ z!PdBA2N=-KynGj$kl@~<%k^i8#*3p+&-pb{`=;RCeZE@QN0bTb%?eh5t&0sav|4*J zecg_=GSSYCW>qItPyMx``qw^5+~tg^i12nde!S*|2hIxBIuplP0A$CvfT8xp(2j06 zuYX&t{pnnP-pM|K5Giy(nrp{Z{2;<-=<#$L;5PVl{&YT6;yMH{PRinUsdZtNz!B&p zC9HVFU-x>!#oFM+fR!brcml?>4VU~Fdk~M*chh`xF-2=UF>8c=KqWW<`DbqJFRZ|f zRcyua@j%o${dTQ4cAzEYXO3tuZRN|o^vU|ORka9X=suqzBvv6bY?iS--7%VjtuBmx zeQX9rLAvmYI3Gt+FkF^Y=Etflzgwq6EafS&gZV(62p882N`b(Mz!`Zx&*0r2WW5xQ z2H>t^y-Dn@a?tk({Gf(6lP7k5#2-31Y3?6N$`)x~{HdDdoc-Co=bG62L*(p_>M-g~m$#h(UyC7pGkne&- zZiDTmsgu`aCQ+Y$xE_+JuEU{bBKt(>HBP_wN5SR#jC+8_r%ye{ko}zpgVo{gwNAjRaXCvasD^Yh^ATgheqX7gL0~Ab-ini zekwPOf2!&+M?N5HehC58O4^iP>C%gLstWn$x>g}js98c_rL#@WX1GuaGuv<0!xV~8a_JVgph)L-XaXD0-fTi7fA#b+ zVsm-+eP7+67n-ban6l=GQYtAQu5uUP#w6%rM+{}-E2xMYRJVg)$m$6khcX(9pbOj> zz^h!_b0&5vvzKy`@}8!>>+8QP%Xf=x>Ix|Km7J^%Dj9gU51!N8C>VT~`LW*RPU^9K zvHcX=rX%5py~d6CN2;n2X4wLHdPl)jIRaMm(y!QOV`m%n(r4NPhCd0gixz(LQ^@W$ zy3X;{BuzUbD`N_QsD|wk$w^83iU8sgICU(0w)LXy*q2-w$(#Xj^?#Yt{<0pj?Mv?l zGdIOh$gCN7x8@Q*q}{!6cj9Vh5mEAc0W-iRiZpZ4eXM=s1)1ryj`!8c5G~kP%y^M< z671S_BXQ6vWsOQLHbl*NI=5!NL0eKGDV#GY_7sNPI`U?)IY-=XdJ@W<6r3{0naeIEyohkA;|CaQL^A`Xo9Cw zcJ{BKH~gRHQBJywlgYxJizU?#ie>q1Jeg(UhKs&?cJ5S8Ed;4gVy2fBdsnl0*ar8C zc?S^S^7yWa*dwEh!gS{ijtXenhfu_k_YXVjL06I&5u>;_TEinMXg4NGHt`nATQ7wwx7!T;8r; zcIw+AX2=~I!@wy8@$2zl9BcX0Q1{D_0GGL@YBwktW~Hw4Kps!vV;InAZQh_sE$Yz7x7V_ z{d>T!-W^7YlhBWnqbnX~eyeE5I{hcv1mo!6ztgGRtTZ!7<$YY1(<#x#mOKWBL-IRp zj~k z!|YVZNsru76J_T@#9w1;U>c?dWrC1Vm{%OKz%HGo>Tb>AZTKXMf|IS~Kn=`R9#yMZ zg0hPj!fDXA*Ut)fYUqQyv})+COV=tFX-#ov3Z{Q~g{G0>N;yv@xVcv-qfBPdoeu2d zN~lOp*(y`Cfz}GvHs&h+@(tCw2Y?>NUEXz9!mHc7))$_@CU&&a-h@4hNDlaC)bM~O z@#L_ug8A!|Z4#E}7!sm9#a&vriP}LGojJz_{B$B+kC1h7Mm|sKsnifcUKRC%=Vq&o z1H||n>|Ba-+cQx@_R?U)PcSHfA?{l+b&`L`OSv8$tt1jz#4{zD2~k7S#Cae;T}9`y zQ|+UWP!YW7tbZEDYwVol5EG$0cvap zETv1pzS=Sjb7@FJGIcJpIL>)Pg=SoB|M}j8Da-cmw;aQBL87euk4TW&74r)E5Nl!C zjBO%kP1m`esiZh?Sp4?z2&BwNe*5z%0I5*_Hvh~OJB?i^F zZl^Fe#2)tEt!0r^e!)(2L6_1@{2T+`mQi)8FuY)Up5@GoI3BE>IWj@PbY`^6Yeajg zXn6VRYq(r{xwFae>U;K@(OoNL0URqtzmJWAvkD=elQOlG-nc!Lu&YT036KxzJT4E{ z?hZeP?&w;mv9VD(syT+c)befCn;F@1lg(v~O$UZZ^O0m81dg&iT`8VfGLPuZd0j_b zS%&@lf1vhM@z4>Lt~Y4)VFUH^xw`wxd8Wz|pP0`<0-JjHS_h&}Y%J0D2WKK$VTcVY z#Tz~5UhOJ@rII=InK+J?cOT$g)DL{y-~%}lbL-M4LYR(Y(2@{YS#_R%NZgW?Ufb?7 zd4tpB>fwGIY`2zVX2CK>vTXkQ$+T7zU%^;lH%@(t;PWn}My;AUhMHRn#>JP3-(Z|Z z&6THnQrQMmOkJ%yp*}4%rZ3-SK4DwriKAtwvls|P2Hx_FX4uU&vX+AEZQ!{B$y}A0 zm^I9^nZG#H-rM7;AC`DPj_8!wf=s|BxM9_$)p>@)bjy?)+W|Ad3a3So<|#$vpVT%@ z=7!pAQ+Ch1CN@x$y&*kDu9xbPPoG&fo-|yw&(jbm(uJPL7tZQ~!Qe4^&rU-I^(1x`!`CVHNs}J|=3EFNQ}DLalFuS^fNfc7bGA)EX0-boAY& zDhycMYr)*Jn-)C0%v3Jh@{d@=aCw=3^mMe29+^ICaOlzeDZ)d`ty9pscD~jN)m#j9 zR1B4IpISvJMIx{X2KxG?!KKQX-`|B6?CLxIQc#8MGIjhOMQR!xn`E~iF*M3 z-4b?XvT}YKuuw#9RxvE!yRHx7xXPZJWn==*5{O;k>Hl3imR9m*0H;Gso&ma7G^%Mg zn{%PhS{9L{;L;0hH|6ixMNRCgo-~{%)X;XoWKMsgm zsH*Z;!y5-ikcxpMEB_&j8wi^FL&UlC(kz~i;H&HIds!+?)rdl*o_<~t#g1@t^$aQh z8w&SYOcwK6*GO89SL}?|!_f%wZC<1y@3K3%qL!+Z(?m0S)_M3OnKryaXtd&a9+aop zc7>&|8+-yuS zt^Z1>_U)uUC<<8cvvr}?*n|R-A7CAagg@C)UzX5%T9IwzR@PE*xr zjk=tznN`K442d%@>Mk+az2U&OJo2GdR<1b3-2D>_gVC*7NPg_9o8dz6v_3@ zK$w_@iBlkZ3|+0DxQnHOKbOFv86TErp7t@Ou<^Ob=EcAvgO>85@uanWQ}>e{N#5ms zN0SX2mWwkRGp6!@tqF7zLfG78k!zVGc`+RY_8r1CY0a6=kIwUqa;+D%x?W0u&Szbj z4rR#`Gnk$T0&9*iBW6ud5*9W>{CX_IY_tlLPn@@a!;|z!O$vqI^s~-Q;i4*&Is1Vw zUt^S4Dx<5(iQ6{?2yEN>xWd)5)9FNt zH#n6Lo^wX~bTcyMcuMiTFr~wB2GTc~^yPh+5yJ%od@<_z0BV;D);!#AX-{oV*DAB& zj3vzjl&3qEG-5C9sUS-S`;Z|i+YhD`CG|EXJoMFL4HKPhd^28jnfU{+{0cv_yar7} z;vdObFx%7dHKqM82xKkc*q*FPm%cy9wQ^zo=6n3bPUtIGl=2z_Woib2Ct{J8az__r ztG2ENnkY0s8a+U%1Wa?*%B#LecF^w-P$%RTS72xg97P9XG)qdlAk}m8!o9ZgHbm8p z*7h!oqN!%qjr~m`+rqtDOm+5tU;N}wM5cZvU#l^2EMHbdjg!UrqhTmXl=h3_P=!Q{ z7r8M~j#hwTN>9~ZhV~!rJ|455L)dAQFfer>HLa$dhc5}K`jmZU-zN+-t_#@`Pqjg` z-g9TxB<{k19wb?O$Q@k?Ra!z$X@9_6CT0%PR8?tz7h4~#r>DJwwJ28An{k0rWS*ub zt7P)j{M3-(V5ts5#^h%Ec8p}6Y)uO#9GFoMHrMg#G! znKA=)@*Z}jbZJ<=qMk~fy>OS=RT|OA%ss%h3aK*aotNG>bFBSZWV!~dOZm<|Y#2qG z!+0Lx5to9fGBbX*4I~b$eOBZFQl$qdSq3iKx8B2^R=&hymhwLbr-?Z1C$Hc{VCIVG8*9%>cq$5 z(4@QqTkQyKZ<589a`V_5aQ*HW*ytAZBr3p9upEzy(my9W8*0N!9XIRVSYCm>SlT;l zwWGi<1D^yAE(yT6 zbo7ccz{8sc#h~GjNabaOe2dA>W^;^*QL;-e{Jr_#CLCYqHSaGCrW4i_R{ZVrVaiw1 z(w%Hk^Ff*c6xzS2d6#}UGKyOfj(2;#bDhOwn;%(uefUOdG|@S=ggGeT1;e8oLxOG3 z(~Q^N2M^Iw`%d$l~*kE?F<9r3PetFZ7s?b*b^_4YW0yc|kjuH6ITDX17l==Cba zo(H^pQmatc#YQsy(xp}>5+Rq9kt@3=_6#Bn$}DL?{Fvoj-Cj%Lg(`3~SS3N%4y}LZ zxscJcy4smp7d!GgICoJXmVwM-!0Pq?#Ad?c;g&6+-z)Xk?K= zp=sr6V6So^^TL#svsf!&J2vAf!Mwf!gY;mc=NdtRN4QNY-pS6V?bpIr)HZ@EpP%SU zs&36c&CtkfRMoI8LkqqseLc6WZK4L_)i%#lK5R?+cn=Wz)*Mti1jLoBF5$~s(X%RH zErdT`kGwQ(fug#{udsH_Ra>v$7uYb2tC%rbW<-VKLyMF}-s<;3}(qETQdSSGsty_DcqU#aGMNaI^1 zAL}Rm+pQNfIs_OCsZgL2$>w~OlZK*@lLyK4U6fNa09gA(M zx9U#EH*>>5mcvJBz4|TjA5-gdN?Q4|t80opozwcOt*Q|qL9mV9a%g=d#bW|P1=T~N z;{pj5d9C(s5_uq<;n$2pvH}Cj$o$S~%?W#3oXme*cc2+Se>#{<;PV6@*lUH3%@eeL zA#luLR@7=BXDCq~$<52I&!5!80DfDaH1%JeG?%K`udj!?nzre( z#Ui9PHA=kJRH@N7`ir?o$IA_mil_rWbXzDo*g=gu{VYH2m<7(M3S!md94ysQ?zNfM z6@PFoo=zFJ*6HZc_ADT->)*iePwRYr+X%I(j$5Hn(#*C-O0BX65)U!f2M_cY^{cY? zRGB6Qbhvti)V{5^ubZuFAQ!2sVK2(7l28C{&4OT>eeZS`8lim#tX@vd)NsV|L&U|< zo0g{Si&(2ml+p~5*9L#Xqps{)6E9Bl_dNwt`b{MX>u=(3^+0Sx98NN;F=kT9)ZZtu z>0GzCMnBICkVM>K32x<{#PBm&<>WJqA3SVlzf8!<+M&o+e%MZyK7X2aP|C0y0R)496&fz~2WTHPcw^eDR~o zaAp93@0zPPnrWiDqu0u=oqFVcyx1djPxB`yc$2 z%|)!TqI*Akmhoy!DZ;6}ea)gYmW@8y=7i^oyNA21Xd4bZ1am#}&~>fZ;b+-X*Hehg zK2}LSYb)m-I&^Gj-Jb6!W3ECJf?&4QO?!K-jnH@K31Lpjuf9aXFD4BQr!3~__>dtH zNK|DmSlkgz2ItNZ1y1t_^RTF3IPZztzR=RN)3RFebo@G^6w75zhGpB zrpm0~Ul%}}so+M=pn0r6y=rX77mL)K1*)syk2Ia|ODBnTMNOxmdan&xl^pAVK&0Wf zodSd`3=Cr96@U8Qto6T;J!sl~5U9$K?iIZembht0Z`oVj4_3-DLDVdIF>Oz>_&(>6 zX;qTMi;pX)F*YbyW2+4ufdKE4Xz7}1n5jNb8sHooAjLnX#BW;WZhOnm&KRqK>z_Tj zb}mOS*jNeuK|NxT)KD+|_Sv(@Qc)x#(t3BIx-?|FgjXw4H}B}|N;$zC0dxX%XLw|2@yr_-}_Vp`#S7*pDR5GME|YO;W9G)G!nG%)~oZ_YXaXdgDp); z#lJGrbVnsc)O6$4bgZ_Y=07V%E*5A1xk7FbC0^bGbWYiiahmM;n?KfGq4%uvT(B~o ziB?~>{j0TqN%H^kThjJjGe75_1JVAGMp1p)WY~47cxLeZiUj9N*}&axzhK9LvJvna zXDKDGQ1`mdNv`4!bemuz`t8f2_5N&IvBAmONn{{2t-unsG(hv859hTw5mvY%6Gla! z_jZwH7H>#lf3(LZ}!C%xoyJhAb{0vw<7h?=Zy zj$PoPZJ?~YJGw>+4#+TuW`{3BBeT^PnQ+qUGmj}=%I5u2B@%OzTIUcASNWVl}>DD5uwp0$f>bm%vP1=1E#JZV}DL9Wgj^xt*)#|?_{Qtw-BnA@Rv%gt6 zqPSpP-q@_zu=U$`8P)Pz{O`F;IH@J~fD~WJd%%M;K(ri3b{j2b(74v#IbP!5kYNly zpxZ$)T0j5KV}?~9OsT*Bs7_L{8hm-bLO{TQeNMZYi!DjJehMIfBssqcl2) zlf1~wyWEY{YMk?F6xM0Vg7oZ7{O4%Bcfxj8$Ve~;!%4Nc3!?m*babtiTOaSIFr!wzJXu;LMjBcW(8i1(XA!#8_|uct9tn(q`C} zikG+hH{@v;pG8b4($$?rl|1z&Aiqo3L>skwBrAL827$^vZ<5A$?AdCt*E zi&EoOXg|TI3C9Y)MSRv0)dViCd~JX=Haf)KHWWzMYf-UiEZEqGIt2DJChFSlMMO9# zG*cRwj~SDm(eKodvLi&6CBZFvuki`FblW!_;9g>KtEEbZ_SMj zUI*;TwkzD-;zh8G%g#D0N~uFbY|SBbRm)t^MEBdDqLwZ}9^J{Zv8X4{xTf@eV#c;d zATTptF1{)9_;zsI%orFN-rT{cedVBeSh)bx>zHYi73k@710BN2D_fK)Wq;bduFrAJ zF|Pv|iaWB)HVS@Y{f@5^0w@v6Ak{Yu#%^AnA|x9vt*3J+(>XM0DK9#^NmNR#ReD zLgJ`SqRC)C%(X&uJ}4|%1~~NW8!L2{ay_kKAEmM)2f9kW;1KBgn2IY7I^T|h*_KZ2 zco;st%?tjvMx}a$%uWFxY3SuV1ft6Cj*Q!sn4nz3+YBQJ*Xnw2ht_)8J7;5#)_Bg~ z1Of2Qs_8;C&#jWKPfhcdiP}4`4cgiw0`D&7T6jR5a89Q+%OPLfacBfdMh2XDfGk6E z>U@@e#ZmV|<+bY`IN?K4ZjoHrO>Je|2`E@jas-jB!U6s=l$kWw=zAi@cIK@osMsFv zDNqlb$u5NSb;8Um@?;_^XqmpFhOsx|4_vL#UBe>Y_@EcF!)!tklUQFxCQecP!g2K@z88U+6efg#cTChKw z!zaXA9~K+lH>zb=C&RSIVjY&J)SS>*xqQwlmF9#4bL%>6oTbrTCE)^Ud%Rush^{3W zXeO#ku!Ujd=l3x9uc2^ zxJ6QYup2%l_)bqP$WOzVGxpTjb6{sQHPq(?P5z`!>cV)PZe=ka9Z_I-lhSe1{+DH0l|)6;N;e8qHapN;*SS>hub|yx_su+)Jr?&GFqd zAzEKnYoA=*Zvg@UeDH#JWKqa;7MflhcJn4qINC$3`aR%1f3cEIniI#IWtID|kCSV+ zW4u1wr;mWRAIw6Js4Ey=c~@HD6MZyZWvW}^fLQU>R?jqh^S<&HRDIG2FWsEm=U@Z! z)!K7PM}Mk21A5T1%bLT^3rFa;r!Z;4B}!^6A1S@>vbNWPp^~_s)C<}1tyQsi$*{OR ze{RH4Qblr8)G@f^(rpM<=87$VRB=!yvamlgj$^0+#UN~=Vkim-?J>s+2T|Z0h$L4{ zk&d+^%w=!9_RVJXWJlK2)T_0rMt)l%WXirEFSPJo;DD>ANL)5%;fZ=@(adxKMc3P( zrr`Pj-=DMZV7(pi#HB{la6Uvod#5#mT`7aMZk1Wk?vqW4K#r}!#qK0$9KHY<*u6xDm4_t3u;X)a{#`Oe$*L zM$NPcvD@7hlZ(u8;f_?NB}kYAdHCoGXc~Hj$Q2?{pvmsoyx~=PgaZ-`NeV zaLF_?BzZ*rxEFO$eqMRO5mXZX@RO^#O+UP zo>Vfbr(2vS?*d(^Xhg3a7!1P_%nV*YIO27Jr|bwaJ#z|VMmoel^DfO|B&}Um7t_qw z*AANXDCV<6Y7mqY=5-0wisq2YToC-5-E4{Amkho$@Xbx&0^yd$x)bu_keal%I>~$t zJjW*YjODGWDh4QqmkdX~Gp#Y8nPkiHxct!`WnxXT`A8-JE|CMrbR4saKKJ|_Jcqhe z8e(dkhVUDIMCb@%r}^YQ_qikz?R`m~FuV+kgMS2&mNp!24ilQkI9bev%$L+C*7D9P zo}(Yp_=I1`iBAKhMciKGoZTt9P{H+ox5R_Ay zHN8qJgpmZ04Dm#Jb%+dXrgYZ+YF+yet5F=TfoL>}KC*szKu;T8`GF4$5K8y1cUafe z)EYp#E|C`V52}&ZM>XBsHJ%+a?h( zgQrH0{oS#qetG+jrM@)=Yn==gB#a~UY;9`MFZEMa5>#J74Q-=k=2ZIwI;SA~Jcds0ZB z`3{k|zO(8~`IO~sY1XAjAHZnyb*-a7g`AN3Y}u35e|y03A^@sZ|6l@}H-;>nPgBSk z*n}flTZPnMiMqLQYfs^)g)_!DOFw>7Dq~fx4SA1&&sc~ub4Sz>C$~$~d#@$*Zgc-40QgmiP zr_ICN=T+Nv>ZO(f%n$aJu}itlE2{--$&N!5(jdxo8gWb+d7qbBKf}x^M0N35`B-E2 zCb3BXRi@4`Z>At@jygj62PF~po_5pUX34hwXZNt@ z3E73o%6ahUa<;7qt^4KAnK>-?e%Y9PUp}@nIAds*;hwL_Vv}O8!IRfo<0Hb1%HmaU zPfG00vwN1azMR%yqu&IBrqRW(v9@H2a7q~Ni8M~WRRV)-Z8&Ub^h=mIqGgk8R_dw| z8r4;llP_ZuoTnIPWCb!KaAZn3)^)k`y{hK*!zyDE&>+J4%8R}vwvEKX>s>(nRL;w| zX-~sZO*nng@|Le+Hl0KKwNUV8sothvZ)xucb>NoX@6nh;?YNrgvi?$mK?#ql+8}w0 zoqiqA@&#gWxVo;+Vf0FqUd5+G^%=W8RxOdYif3wPt+^|gfbBc?oRvUFn5hUAD0^yi z`DAZTy)z=Gy5Ys;1S3vZJ6PvUURYQ*5bWX*I6AXsPK`G|IQl2~F!@kZk<08J&{}^F z;JF7d@TzlKEdK+iPnd@Dod-MGeNbQ}S1R!qVGqNifDy+uw`HK1AGSlG-)O7h1Ua;i zC>d}m0XVAYpRY26>1D&YU|k8+^=h+}0&U&+l0mI7bOO(BYdAVJ>1fMqb+Zxuqt+-9 zrjgdRLn`NQ3bo4<-${}$Nlbr7AcJ=nb6yb}Xuo@*)V({nJ3SADxGS^1SH|nFDVeH$ zZl5nBsu4`>7=tOqX()!3PZ7G-!B_jLt|mi%xDKB1YkpKup2cwSv$OnU^JE4kIx*jV z-*pktZgnT&X%JcMn-1Qw$nPBQ$#7EegE&fLEG7u5>yP@uk2~GGz39TN2t&gMs%G^( zWlBxpC6o+{E6Q(|(Uy6w$KW5S1?5-7GVQBF*@yl)21ev2V zxO&m^d3JeUkU$#pkp8xLa{_DQ(`}%yY z`}6s{U+8mLamcITH0b^pxr8+Z4X^>#W!;MOHdxgy&8Rpk% zWI@!>_ktIjlbz9Fjlh{AU<0J<)XTBf8zBpJErEn^rN5xeBmOghV8ziQIEB5#G z8v|!E%^AFCQIPhQ?^QvAoHuDWAR@ttO)FBE9EGhxvF-{N0PS5 ztuf>Jjfyti(8_Lz%`>K!ElBqxx5EmiTocESU-O@TB;Ts(j z)~-1mz%z3}l#F#)ny;>WNCtk}%5JG~z&1Mc?qw)5tXUFv|OJDqUY0Eb_zsl(!HzbP3b zt5^^t3*AgOeWx?%bd;i~)7S5bQrHpowm&7NYm;L(fa_K0S$nhoA>J)rMsc=_nT*KPM3vINGV5YP7lHhXBw@y!P4+tq7OMU^nCW$N+DNApeN5y2wmVK#^wmr)KYq+~;sryXu@c<9Yy$G{KQ^ryyy=e4T-9Np5p;yxcisoc~(XWks6nHExDdJSUb-3R3j z%vBGIo7t2|ite5i4-SKU=Wc)0%JlH}1+TNqZMw2;eq>FD@>VqPds8pAQ5bwZW})o4 zgWZ~Tun)wedbTdAJ;Yb~q@AAUbI}NDZIEyl!%Z*OR5d^IOx9zZR>VG41Tp;(eRRbA zYN+1q{@uOFA$DbZN$}*KW*CbppXxFiTv$XbH;%;GQU^A&&$;CN1VE8cRSQj1l@{MA zfvq1bOJ6Mi_gXOI>!G0EWFLQ6K1d#-gzZLa>16>W5qM5=yR`$<$~&<>UxJ&iJRu@XlvBCKl2LFOd4_B^33wAHiSz$qG;#-9PN;t zbb_A|6B9iZw=y+Z3Ny(WIe#SN?QAYCAk7YW9oJ1}qMX}>$pYW@(PeYzTEc~+hZ#mi zX(*+kte#xpnda4PGV9#sriEq3&AqbFl#v7x(z`7nD< zah^d)i+|}(y@w1smMM@$1kV8vPx;}wj`>UPh0 z+x0d2JL1*!5D;S+(5DB~K}BH}+16ypmTdeo}4*ktYL8 z3l^Pu!moXaEd)nCF&-zI&jn%L&-JTG&Om*4+UPW1OR9ih5i~+=p%GQ5x09o`uTAbR z+a%N|UAyPAm~cB`bQ2=Mrbp=v1Pr-`b&W-9nL5ge>@qQt&=8BEXneji+HzXEB43RO z7LOUqKTa?8=#@D;X*3{bxANkWJ7H2k**gQ+Dt3MQ62j!bPG%5>)Q7w(~4x7L|@Au`n1<=Oh>dJMk?X)ZCwus!9%;FNa z(ZLG#9r?y&T%n4Ov3j2T9rs<=NSd;lyA=1a`~teRA$BPjmRpYBVSQdlSc>*xvucIw z-vFsebmiw?wzAi>#`woH)~98oID2m8C^;$d3=4AgQBRtp#jL8}R%;EuSLP+X8y{=)1KS#L7qNYfh~h`BGzQLV6CO7v$F#VHIEK zrwbU4$5zWZ$q3+Z&#^}5@Trzfl>m8#pKrpv12Qj!Wd3Cla`rkq|6Ry^Nr5!jpvFa+ zAiPzo765tDkGj<8OlSkOG^j(Lm5FJ&m`A{OY^z8^4sSEDe}j4)m1a8l(``h$6(X~j zAY~E*@jV;4Rz>DGhM)XWc0!4fv}c1X+0SY3z_sJuCMRw+~l7bhtfi7q)ZxBwtSN{QC5fc<< z?ilvysW8L2IC8G@^_`AxM>4GrVbBmCGfgg~U^UQBi;K?S@5y=8Q)Cm!fX|ks1iGr{ zAaT_1Hz=B$X@}{+Rm(ukkg|by-5X42h^~lk5Sh^L86>z2-;t)YY_?ad8+Njhlw@?iJ@w6~otS z>BQH2^W5Aot?o# z_)S+qA#3X|KRM60EE%8S8_omt^|iFrUAMqjmmh(j&Y?0u;O3J3zbvvq}5t@o&G1)lG?*9>RTl~G;pUuKG6z0h-SzC zY9$jFSBVddzaMs|q`sVClpx1=S3J^39^)7I3T?C(qs{;`E0oD@bW!zIw0Jz4DY6fu z56qb*1tq<*&{#D5sp6pffF0LISXS0GoiDu5UvYw3KXta%HD5##EQ0J3`SD53qfVcL z>ra5+H!kffg}IGidO?`>T$0eco5M-Sf@7pJgY~LVFOERO)SOO6_HWyN07hu!mO5qI zyZWruLPMqG0Yd?#5LFn_ZT!3bz+!wES?#IQvl~Pm8a`~|F_10A+BCj>$~NaH(hHSX zN>Vcv;`AOr_ZtEA6WWBoL~KT^f~3!C>2{n1+`yq47;?P4?V1ZS4)qOga|h0@GPK3D zv8jcvt}oMS$9;s7QbE(BaFAQ2_AOG!U5ryIn%G<|SN5p_>#Ikb9Y`3eInx4OtSae* z#H9vWW!yRl8V&4zdX&nb3Eu6&grcexbyKXsBhHu5u`FS|qNyRT{DBYT0S)t6C&RL> zV!_8A1jG{vTYh(SxM6-VbG4`z;9Q;BTzwdi`WYuu{GQ`uv5=}YN|mBN&2zDG`>l-p zFEz~$>`3frx@RpQhZx)bP;{1kjEM@$HsXLPQmrCBH@l+U4ug!q{sjgzh3N_}2VNH% zJZAowjbEjV>*zN{teziJcU$V*7PWLUHttQ*k`A%?_#*>sot1iXJJkL(UzWnNcvKB6 zc8a@yov2m3>kcL3la{HXs2R6@hs5hB{ywnk!J6;Pw;#iUmLc8^GnRhxW6yKU@5<_Iad7up^MO^aaF|3#9v}tO$YVM-(JXXlZn62G5?3% I?Z5i}0Xq{#rvLx| literal 0 HcmV?d00001 diff --git a/wolnelektury/static/jplayer/jplayer.blue.monday.seeking.gif b/wolnelektury/static/jplayer/jplayer.blue.monday.seeking.gif new file mode 100644 index 0000000000000000000000000000000000000000..dbd2105ab933f0336e1f732e32a54c6d6a8e1441 GIT binary patch literal 3284 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 literal 0 HcmV?d00001 diff --git a/wolnelektury/static/jplayer/jplayer.playlist.min.js b/wolnelektury/static/jplayer/jplayer.playlist.min.js new file mode 100644 index 000000000..42c0e22a1 --- /dev/null +++ b/wolnelektury/static/jplayer/jplayer.playlist.min.js @@ -0,0 +1,30 @@ +/* + * Playlist Object for the jPlayer Plugin + * http://www.jplayer.org + * + * Copyright (c) 2009 - 2011 Happyworm Ltd + * Dual licensed under the MIT and GPL licenses. + * - http://www.opensource.org/licenses/mit-license.php + * - http://www.gnu.org/copyleft/gpl.html + * + * Author: Mark J Panaghiston + * Version: 2.1.0 (jPlayer 2.1.0) + * Date: 1st September 2011 + */ + +(function(b,f){jPlayerPlaylist=function(a,c,d){var e=this;this.current=0;this.removing=this.shuffled=this.loop=!1;this.cssSelector=b.extend({},this._cssSelector,a);this.options=b.extend(!0,{},this._options,d);this.playlist=[];this.original=[];this._initPlaylist(c);this.cssSelector.title=this.cssSelector.cssSelectorAncestor+" .jp-title";this.cssSelector.playlist=this.cssSelector.cssSelectorAncestor+" .jp-playlist";this.cssSelector.next=this.cssSelector.cssSelectorAncestor+" .jp-next";this.cssSelector.previous= +this.cssSelector.cssSelectorAncestor+" .jp-previous";this.cssSelector.shuffle=this.cssSelector.cssSelectorAncestor+" .jp-shuffle";this.cssSelector.shuffleOff=this.cssSelector.cssSelectorAncestor+" .jp-shuffle-off";this.options.cssSelectorAncestor=this.cssSelector.cssSelectorAncestor;this.options.repeat=function(a){e.loop=a.jPlayer.options.loop};b(this.cssSelector.jPlayer).bind(b.jPlayer.event.ready,function(){e._init()});b(this.cssSelector.jPlayer).bind(b.jPlayer.event.ended,function(){e.next()}); +b(this.cssSelector.jPlayer).bind(b.jPlayer.event.play,function(){b(this).jPlayer("pauseOthers")});b(this.cssSelector.jPlayer).bind(b.jPlayer.event.resize,function(a){a.jPlayer.options.fullScreen?b(e.cssSelector.title).show():b(e.cssSelector.title).hide()});b(this.cssSelector.previous).click(function(){e.previous();b(this).blur();return!1});b(this.cssSelector.next).click(function(){e.next();b(this).blur();return!1});b(this.cssSelector.shuffle).click(function(){e.shuffle(!0);return!1});b(this.cssSelector.shuffleOff).click(function(){e.shuffle(!1); +return!1}).hide();this.options.fullScreen||b(this.cssSelector.title).hide();b(this.cssSelector.playlist+" ul").empty();this._createItemHandlers();b(this.cssSelector.jPlayer).jPlayer(this.options)};jPlayerPlaylist.prototype={_cssSelector:{jPlayer:"#jquery_jplayer_1",cssSelectorAncestor:"#jp_container_1"},_options:{playlistOptions:{autoPlay:!1,loopOnPrevious:!1,shuffleOnLoop:!0,enableRemoveControls:!1,displayTime:"slow",addTime:"fast",removeTime:"fast",shuffleTime:"slow",itemClass:"jp-playlist-item", +freeGroupClass:"jp-free-media",freeItemClass:"jp-playlist-item-free",removeItemClass:"jp-playlist-item-remove"}},option:function(a,b){if(b===f)return this.options.playlistOptions[a];this.options.playlistOptions[a]=b;switch(a){case "enableRemoveControls":this._updateControls();break;case "itemClass":case "freeGroupClass":case "freeItemClass":case "removeItemClass":this._refresh(!0),this._createItemHandlers()}return this},_init:function(){var a=this;this._refresh(function(){a.options.playlistOptions.autoPlay? +a.play(a.current):a.select(a.current)})},_initPlaylist:function(a){this.current=0;this.removing=this.shuffled=!1;this.original=b.extend(!0,[],a);this._originalPlaylist()},_originalPlaylist:function(){var a=this;this.playlist=[];b.each(this.original,function(b){a.playlist[b]=a.original[b]})},_refresh:function(a){var c=this;if(a&&!b.isFunction(a))b(this.cssSelector.playlist+" ul").empty(),b.each(this.playlist,function(a){b(c.cssSelector.playlist+" ul").append(c._createListItem(c.playlist[a]))}),this._updateControls(); +else{var d=b(this.cssSelector.playlist+" ul").children().length?this.options.playlistOptions.displayTime:0;b(this.cssSelector.playlist+" ul").slideUp(d,function(){var d=b(this);b(this).empty();b.each(c.playlist,function(a){d.append(c._createListItem(c.playlist[a]))});c._updateControls();b.isFunction(a)&&a();c.playlist.length?b(this).slideDown(c.options.playlistOptions.displayTime):b(this).show()})}},_createListItem:function(a){var c=this,d="
  • ";d+="×";if(a.free){var e=!0;d+="(";b.each(a,function(a,f){b.jPlayer.prototype.format[a]&&(e?e=!1:d+=" | ",d+=""+a+"")});d+=")"}d+=""+a.title+(a.artist?" ":"")+"";d+="
  • ";return d},_createItemHandlers:function(){var a= +this;b(this.cssSelector.playlist+" a."+this.options.playlistOptions.itemClass).die("click").live("click",function(){var c=b(this).parent().parent().index();a.current!==c?a.play(c):b(a.cssSelector.jPlayer).jPlayer("play");b(this).blur();return!1});b(a.cssSelector.playlist+" a."+this.options.playlistOptions.freeItemClass).die("click").live("click",function(){b(this).parent().parent().find("."+a.options.playlistOptions.itemClass).click();b(this).blur();return!1});b(a.cssSelector.playlist+" a."+this.options.playlistOptions.removeItemClass).die("click").live("click", +function(){var c=b(this).parent().parent().index();a.remove(c);b(this).blur();return!1})},_updateControls:function(){this.options.playlistOptions.enableRemoveControls?b(this.cssSelector.playlist+" ."+this.options.playlistOptions.removeItemClass).show():b(this.cssSelector.playlist+" ."+this.options.playlistOptions.removeItemClass).hide();this.shuffled?(b(this.cssSelector.shuffleOff).show(),b(this.cssSelector.shuffle).hide()):(b(this.cssSelector.shuffleOff).hide(),b(this.cssSelector.shuffle).show())}, +_highlight:function(a){this.playlist.length&&a!==f&&(b(this.cssSelector.playlist+" .jp-playlist-current").removeClass("jp-playlist-current"),b(this.cssSelector.playlist+" li:nth-child("+(a+1)+")").addClass("jp-playlist-current").find(".jp-playlist-item").addClass("jp-playlist-current"),b(this.cssSelector.title+" li").html(this.playlist[a].title+(this.playlist[a].artist?" ":"")))},setPlaylist:function(a){this._initPlaylist(a);this._init()}, +add:function(a,c){b(this.cssSelector.playlist+" ul").append(this._createListItem(a)).find("li:last-child").hide().slideDown(this.options.playlistOptions.addTime);this._updateControls();this.original.push(a);this.playlist.push(a);c?this.play(this.playlist.length-1):this.original.length===1&&this.select(0)},remove:function(a){var c=this;if(a===f)return this._initPlaylist([]),this._refresh(function(){b(c.cssSelector.jPlayer).jPlayer("clearMedia")}),!0;else if(this.removing)return!1;else{a=a<0?c.original.length+ +a:a;if(0<=a&&a1?this.shuffle(!0,!0):this.play(a):a>0&&this.play(a)},previous:function(){var a=this.current-1>=0?this.current-1:this.playlist.length-1;(this.loop&&this.options.playlistOptions.loopOnPrevious||a','','','',''];c=document.createElement('');for(var e=0;e0?100*d/this.status.duration:0;typeof a.seekable==="object"&&a.seekable.length>0?(g=this.status.duration>0?100*a.seekable.end(a.seekable.length-1)/this.status.duration:100,f=100*a.currentTime/a.seekable.end(a.seekable.length-1)):(g=100,f=e);b&&(e=f=d=0);this.status.seekPercent=g;this.status.currentPercentRelative=f;this.status.currentPercentAbsolute=e;this.status.currentTime=d;this.status.readyState=a.readyState;this.status.networkState=a.networkState;this.status.playbackRate= +a.playbackRate;this.status.ended=a.ended},_resetStatus:function(){this.status=b.extend({},this.status,b.jPlayer.prototype.status)},_trigger:function(a,c,d){a=b.Event(a);a.jPlayer={};a.jPlayer.version=b.extend({},this.version);a.jPlayer.options=b.extend(!0,{},this.options);a.jPlayer.status=b.extend(!0,{},this.status);a.jPlayer.html=b.extend(!0,{},this.html);a.jPlayer.flash=b.extend(!0,{},this.flash);if(c)a.jPlayer.error=b.extend({},c);if(d)a.jPlayer.warning=b.extend({},d);this.element.trigger(a)}, +jPlayerFlashEvent:function(a,c){if(a===b.jPlayer.event.ready)if(this.internal.ready){if(this.flash.gate){if(this.status.srcSet){var d=this.status.currentTime,e=this.status.paused;this.setMedia(this.status.media);d>0&&(e?this.pause(d):this.play(d))}this._trigger(b.jPlayer.event.flashreset)}}else this.internal.ready=!0,this.internal.flash.jq.css({width:"0px",height:"0px"}),this.version.flash=c.version,this.version.needFlash!==this.version.flash&&this._error({type:b.jPlayer.error.VERSION,context:this.version.flash, +message:b.jPlayer.errorMsg.VERSION+this.version.flash,hint:b.jPlayer.errorHint.VERSION}),this._trigger(b.jPlayer.event.repeat),this._trigger(a);if(this.flash.gate)switch(a){case b.jPlayer.event.progress:this._getFlashStatus(c);this._updateInterface();this._trigger(a);break;case b.jPlayer.event.timeupdate:this._getFlashStatus(c);this._updateInterface();this._trigger(a);break;case b.jPlayer.event.play:this._seeked();this._updateButtons(!0);this._trigger(a);break;case b.jPlayer.event.pause:this._updateButtons(!1); +this._trigger(a);break;case b.jPlayer.event.ended:this._updateButtons(!1);this._trigger(a);break;case b.jPlayer.event.click:this._trigger(a);break;case b.jPlayer.event.error:this.status.waitForLoad=!0;this.status.waitForPlay=!0;this.status.video&&this.internal.flash.jq.css({width:"0px",height:"0px"});this._validString(this.status.media.poster)&&this.internal.poster.jq.show();this.css.jq.videoPlay.length&&this.status.video&&this.css.jq.videoPlay.show();this.status.video?this._flash_setVideo(this.status.media): +this._flash_setAudio(this.status.media);this._updateButtons(!1);this._error({type:b.jPlayer.error.URL,context:c.src,message:b.jPlayer.errorMsg.URL,hint:b.jPlayer.errorHint.URL});break;case b.jPlayer.event.seeking:this._seeking();this._trigger(a);break;case b.jPlayer.event.seeked:this._seeked();this._trigger(a);break;case b.jPlayer.event.ready:break;default:this._trigger(a)}return!1},_getFlashStatus:function(a){this.status.seekPercent=a.seekPercent;this.status.currentPercentRelative=a.currentPercentRelative; +this.status.currentPercentAbsolute=a.currentPercentAbsolute;this.status.currentTime=a.currentTime;this.status.duration=a.duration;this.status.readyState=4;this.status.networkState=0;this.status.playbackRate=1;this.status.ended=!1},_updateButtons:function(a){if(a!==f)this.status.paused=!a,this.css.jq.play.length&&this.css.jq.pause.length&&(a?(this.css.jq.play.hide(),this.css.jq.pause.show()):(this.css.jq.play.show(),this.css.jq.pause.hide()));this.css.jq.restoreScreen.length&&this.css.jq.fullScreen.length&& +(this.status.noFullScreen?(this.css.jq.fullScreen.hide(),this.css.jq.restoreScreen.hide()):this.options.fullScreen?(this.css.jq.fullScreen.hide(),this.css.jq.restoreScreen.show()):(this.css.jq.fullScreen.show(),this.css.jq.restoreScreen.hide()));this.css.jq.repeat.length&&this.css.jq.repeatOff.length&&(this.options.loop?(this.css.jq.repeat.hide(),this.css.jq.repeatOff.show()):(this.css.jq.repeat.show(),this.css.jq.repeatOff.hide()))},_updateInterface:function(){this.css.jq.seekBar.length&&this.css.jq.seekBar.width(this.status.seekPercent+ +"%");this.css.jq.playBar.length&&this.css.jq.playBar.width(this.status.currentPercentRelative+"%");this.css.jq.currentTime.length&&this.css.jq.currentTime.text(b.jPlayer.convertTime(this.status.currentTime));this.css.jq.duration.length&&this.css.jq.duration.text(b.jPlayer.convertTime(this.status.duration))},_seeking:function(){this.css.jq.seekBar.length&&this.css.jq.seekBar.addClass("jp-seeking-bg")},_seeked:function(){this.css.jq.seekBar.length&&this.css.jq.seekBar.removeClass("jp-seeking-bg")}, +_resetGate:function(){this.html.audio.gate=!1;this.html.video.gate=!1;this.flash.gate=!1},_resetActive:function(){this.html.active=!1;this.flash.active=!1},setMedia:function(a){var c=this,d=!1,e=this.status.media.poster!==a.poster;this._resetMedia();this._resetGate();this._resetActive();b.each(this.formats,function(e,f){var i=c.format[f].media==="video";b.each(c.solutions,function(b,e){if(c[e].support[f]&&c._validString(a[f])){var g=e==="html";i?(g?(c.html.video.gate=!0,c._html_setVideo(a),c.html.active= +!0):(c.flash.gate=!0,c._flash_setVideo(a),c.flash.active=!0),c.css.jq.videoPlay.length&&c.css.jq.videoPlay.show(),c.status.video=!0):(g?(c.html.audio.gate=!0,c._html_setAudio(a),c.html.active=!0):(c.flash.gate=!0,c._flash_setAudio(a),c.flash.active=!0),c.css.jq.videoPlay.length&&c.css.jq.videoPlay.hide(),c.status.video=!1);d=!0;return!1}});if(d)return!1});if(d){if((!this.status.nativeVideoControls||!this.html.video.gate)&&this._validString(a.poster))e?this.htmlElement.poster.src=a.poster:this.internal.poster.jq.show(); +this.status.srcSet=!0;this.status.media=b.extend({},a);this._updateButtons(!1);this._updateInterface()}else this._error({type:b.jPlayer.error.NO_SUPPORT,context:"{supplied:'"+this.options.supplied+"'}",message:b.jPlayer.errorMsg.NO_SUPPORT,hint:b.jPlayer.errorHint.NO_SUPPORT})},_resetMedia:function(){this._resetStatus();this._updateButtons(!1);this._updateInterface();this._seeked();this.internal.poster.jq.hide();clearTimeout(this.internal.htmlDlyCmdId);this.html.active?this._html_resetMedia():this.flash.active&& +this._flash_resetMedia()},clearMedia:function(){this._resetMedia();this.html.active?this._html_clearMedia():this.flash.active&&this._flash_clearMedia();this._resetGate();this._resetActive()},load:function(){this.status.srcSet?this.html.active?this._html_load():this.flash.active&&this._flash_load():this._urlNotSetError("load")},play:function(a){a=typeof a==="number"?a:NaN;this.status.srcSet?this.html.active?this._html_play(a):this.flash.active&&this._flash_play(a):this._urlNotSetError("play")},videoPlay:function(){this.play()}, +pause:function(a){a=typeof a==="number"?a:NaN;this.status.srcSet?this.html.active?this._html_pause(a):this.flash.active&&this._flash_pause(a):this._urlNotSetError("pause")},pauseOthers:function(){var a=this;b.each(this.instances,function(b,d){a.element!==d&&d.data("jPlayer").status.srcSet&&d.jPlayer("pause")})},stop:function(){this.status.srcSet?this.html.active?this._html_pause(0):this.flash.active&&this._flash_pause(0):this._urlNotSetError("stop")},playHead:function(a){a=this._limitValue(a,0,100); +this.status.srcSet?this.html.active?this._html_playHead(a):this.flash.active&&this._flash_playHead(a):this._urlNotSetError("playHead")},_muted:function(a){this.options.muted=a;this.html.used&&this._html_mute(a);this.flash.used&&this._flash_mute(a);!this.html.video.gate&&!this.html.audio.gate&&(this._updateMute(a),this._updateVolume(this.options.volume),this._trigger(b.jPlayer.event.volumechange))},mute:function(a){a=a===f?!0:!!a;this._muted(a)},unmute:function(a){a=a===f?!0:!!a;this._muted(!a)},_updateMute:function(a){if(a=== +f)a=this.options.muted;this.css.jq.mute.length&&this.css.jq.unmute.length&&(this.status.noVolume?(this.css.jq.mute.hide(),this.css.jq.unmute.hide()):a?(this.css.jq.mute.hide(),this.css.jq.unmute.show()):(this.css.jq.mute.show(),this.css.jq.unmute.hide()))},volume:function(a){a=this._limitValue(a,0,1);this.options.volume=a;this.html.used&&this._html_volume(a);this.flash.used&&this._flash_volume(a);!this.html.video.gate&&!this.html.audio.gate&&(this._updateVolume(a),this._trigger(b.jPlayer.event.volumechange))}, +volumeBar:function(a){if(this.css.jq.volumeBar.length){var b=this.css.jq.volumeBar.offset(),d=a.pageX-b.left,e=this.css.jq.volumeBar.width(),a=this.css.jq.volumeBar.height()-a.pageY+b.top,b=this.css.jq.volumeBar.height();this.options.verticalVolume?this.volume(a/b):this.volume(d/e)}this.options.muted&&this._muted(!1)},volumeBarValue:function(a){this.volumeBar(a)},_updateVolume:function(a){if(a===f)a=this.options.volume;a=this.options.muted?0:a;this.status.noVolume?(this.css.jq.volumeBar.length&&this.css.jq.volumeBar.hide(), +this.css.jq.volumeBarValue.length&&this.css.jq.volumeBarValue.hide(),this.css.jq.volumeMax.length&&this.css.jq.volumeMax.hide()):(this.css.jq.volumeBar.length&&this.css.jq.volumeBar.show(),this.css.jq.volumeBarValue.length&&(this.css.jq.volumeBarValue.show(),this.css.jq.volumeBarValue[this.options.verticalVolume?"height":"width"](a*100+"%")),this.css.jq.volumeMax.length&&this.css.jq.volumeMax.show())},volumeMax:function(){this.volume(1);this.options.muted&&this._muted(!1)},_cssSelectorAncestor:function(a){var c= +this;this.options.cssSelectorAncestor=a;this._removeUiClass();this.ancestorJq=a?b(a):[];a&&this.ancestorJq.length!==1&&this._warning({type:b.jPlayer.warning.CSS_SELECTOR_COUNT,context:a,message:b.jPlayer.warningMsg.CSS_SELECTOR_COUNT+this.ancestorJq.length+" found for cssSelectorAncestor.",hint:b.jPlayer.warningHint.CSS_SELECTOR_COUNT});this._addUiClass();b.each(this.options.cssSelector,function(a,b){c._cssSelector(a,b)})},_cssSelector:function(a,c){var d=this;typeof c==="string"?b.jPlayer.prototype.options.cssSelector[a]? +(this.css.jq[a]&&this.css.jq[a].length&&this.css.jq[a].unbind(".jPlayer"),this.options.cssSelector[a]=c,this.css.cs[a]=this.options.cssSelectorAncestor+" "+c,this.css.jq[a]=c?b(this.css.cs[a]):[],this.css.jq[a].length&&this.css.jq[a].bind("click.jPlayer",function(c){d[a](c);b(this).blur();return!1}),c&&this.css.jq[a].length!==1&&this._warning({type:b.jPlayer.warning.CSS_SELECTOR_COUNT,context:this.css.cs[a],message:b.jPlayer.warningMsg.CSS_SELECTOR_COUNT+this.css.jq[a].length+" found for "+a+" method.", +hint:b.jPlayer.warningHint.CSS_SELECTOR_COUNT})):this._warning({type:b.jPlayer.warning.CSS_SELECTOR_METHOD,context:a,message:b.jPlayer.warningMsg.CSS_SELECTOR_METHOD,hint:b.jPlayer.warningHint.CSS_SELECTOR_METHOD}):this._warning({type:b.jPlayer.warning.CSS_SELECTOR_STRING,context:c,message:b.jPlayer.warningMsg.CSS_SELECTOR_STRING,hint:b.jPlayer.warningHint.CSS_SELECTOR_STRING})},seekBar:function(a){if(this.css.jq.seekBar){var b=this.css.jq.seekBar.offset(),a=a.pageX-b.left,b=this.css.jq.seekBar.width(); +this.playHead(100*a/b)}},playBar:function(a){this.seekBar(a)},repeat:function(){this._loop(!0)},repeatOff:function(){this._loop(!1)},_loop:function(a){if(this.options.loop!==a)this.options.loop=a,this._updateButtons(),this._trigger(b.jPlayer.event.repeat)},currentTime:function(){},duration:function(){},gui:function(){},noSolution:function(){},option:function(a,c){var d=a;if(arguments.length===0)return b.extend(!0,{},this.options);if(typeof a==="string"){var e=a.split(".");if(c===f){for(var d=b.extend(!0, +{},this.options),g=0;g0?this._html_load():clearTimeout(this.internal.htmlDlyCmdId);this.htmlElement.media.pause();if(!isNaN(a))try{this.htmlElement.media.currentTime=a}catch(d){this.internal.htmlDlyCmdId=setTimeout(function(){b.pause(a)},100);return}a>0&&this._html_checkWaitForPlay()},_html_playHead:function(a){var b=this;this._html_load();try{if(typeof this.htmlElement.media.seekable==="object"&&this.htmlElement.media.seekable.length>0)this.htmlElement.media.currentTime= +a*this.htmlElement.media.seekable.end(this.htmlElement.media.seekable.length-1)/100;else if(this.htmlElement.media.duration>0&&!isNaN(this.htmlElement.media.duration))this.htmlElement.media.currentTime=a*this.htmlElement.media.duration/100;else throw"e";}catch(d){this.internal.htmlDlyCmdId=setTimeout(function(){b.playHead(a)},100);return}this.status.waitForLoad||this._html_checkWaitForPlay()},_html_checkWaitForPlay:function(){if(this.status.waitForPlay)this.status.waitForPlay=!1,this.css.jq.videoPlay.length&& +this.css.jq.videoPlay.hide(),this.status.video&&(this.internal.poster.jq.hide(),this.internal.video.jq.css({width:this.status.width,height:this.status.height}))},_html_volume:function(a){if(this.html.audio.available)this.htmlElement.audio.volume=a;if(this.html.video.available)this.htmlElement.video.volume=a},_html_mute:function(a){if(this.html.audio.available)this.htmlElement.audio.muted=a;if(this.html.video.available)this.htmlElement.video.muted=a},_flash_setAudio:function(a){var c=this;try{if(b.each(this.formats, +function(b,d){if(c.flash.support[d]&&a[d]){switch(d){case "m4a":case "fla":c._getMovie().fl_setAudio_m4a(a[d]);break;case "mp3":c._getMovie().fl_setAudio_mp3(a[d])}c.status.src=a[d];c.status.format[d]=!0;c.status.formatType=d;return!1}}),this.options.preload==="auto")this._flash_load(),this.status.waitForLoad=!1}catch(d){this._flashError(d)}},_flash_setVideo:function(a){var c=this;try{if(b.each(this.formats,function(b,d){if(c.flash.support[d]&&a[d]){switch(d){case "m4v":case "flv":c._getMovie().fl_setVideo_m4v(a[d])}c.status.src= +a[d];c.status.format[d]=!0;c.status.formatType=d;return!1}}),this.options.preload==="auto")this._flash_load(),this.status.waitForLoad=!1}catch(d){this._flashError(d)}},_flash_resetMedia:function(){this.internal.flash.jq.css({width:"0px",height:"0px"});this._flash_pause(NaN)},_flash_clearMedia:function(){try{this._getMovie().fl_clearMedia()}catch(a){this._flashError(a)}},_flash_load:function(){try{this._getMovie().fl_load()}catch(a){this._flashError(a)}this.status.waitForLoad=!1},_flash_play:function(a){try{this._getMovie().fl_play(a)}catch(b){this._flashError(b)}this.status.waitForLoad= +!1;this._flash_checkWaitForPlay()},_flash_pause:function(a){try{this._getMovie().fl_pause(a)}catch(b){this._flashError(b)}if(a>0)this.status.waitForLoad=!1,this._flash_checkWaitForPlay()},_flash_playHead:function(a){try{this._getMovie().fl_play_head(a)}catch(b){this._flashError(b)}this.status.waitForLoad||this._flash_checkWaitForPlay()},_flash_checkWaitForPlay:function(){if(this.status.waitForPlay)this.status.waitForPlay=!1,this.css.jq.videoPlay.length&&this.css.jq.videoPlay.hide(),this.status.video&& +(this.internal.poster.jq.hide(),this.internal.flash.jq.css({width:this.status.width,height:this.status.height}))},_flash_volume:function(a){try{this._getMovie().fl_volume(a)}catch(b){this._flashError(b)}},_flash_mute:function(a){try{this._getMovie().fl_mute(a)}catch(b){this._flashError(b)}},_getMovie:function(){return document[this.internal.flash.id]},_checkForFlash:function(a){var b=!1,d;if(window.ActiveXObject)try{new ActiveXObject("ShockwaveFlash.ShockwaveFlash."+a),b=!0}catch(e){}else navigator.plugins&& +navigator.mimeTypes.length>0&&(d=navigator.plugins["Shockwave Flash"])&&navigator.plugins["Shockwave Flash"].description.replace(/.*\s(\d+\.\d+).*/,"$1")>=a&&(b=!0);return b},_validString:function(a){return a&&typeof a==="string"},_limitValue:function(a,b,d){return ad?d:a},_urlNotSetError:function(a){this._error({type:b.jPlayer.error.URL_NOT_SET,context:a,message:b.jPlayer.errorMsg.URL_NOT_SET,hint:b.jPlayer.errorHint.URL_NOT_SET})},_flashError:function(a){var c;c=this.internal.ready?"FLASH_DISABLED": +"FLASH";this._error({type:b.jPlayer.error[c],context:this.internal.flash.swf,message:b.jPlayer.errorMsg[c]+a.message,hint:b.jPlayer.errorHint[c]});this.internal.flash.jq.css({width:"1px",height:"1px"})},_error:function(a){this._trigger(b.jPlayer.event.error,a);this.options.errorAlerts&&this._alert("Error!"+(a.message?"\n\n"+a.message:"")+(a.hint?"\n\n"+a.hint:"")+"\n\nContext: "+a.context)},_warning:function(a){this._trigger(b.jPlayer.event.warning,f,a);this.options.warningAlerts&&this._alert("Warning!"+ +(a.message?"\n\n"+a.message:"")+(a.hint?"\n\n"+a.hint:"")+"\n\nContext: "+a.context)},_alert:function(a){alert("jPlayer "+this.version.script+" : id='"+this.internal.self.id+"' : "+a)},_emulateHtmlBridge:function(){var a=this;b.each(b.jPlayer.emulateMethods.split(/\s+/g),function(b,d){a.internal.domNode[d]=function(b){a[d](b)}});b.each(b.jPlayer.event,function(c,d){var e=!0;b.each(b.jPlayer.reservedEvent.split(/\s+/g),function(a,b){if(b===c)return e=!1});e&&a.element.bind(d+".jPlayer.jPlayerHtml", +function(){a._emulateHtmlUpdate();var b=document.createEvent("Event");b.initEvent(c,!1,!0);a.internal.domNode.dispatchEvent(b)})})},_emulateHtmlUpdate:function(){var a=this;b.each(b.jPlayer.emulateStatus.split(/\s+/g),function(b,d){a.internal.domNode[d]=a.status[d]});b.each(b.jPlayer.emulateOptions.split(/\s+/g),function(b,d){a.internal.domNode[d]=a.options[d]})},_destroyHtmlBridge:function(){var a=this;this.element.unbind(".jPlayerHtml");b.each((b.jPlayer.emulateMethods+" "+b.jPlayer.emulateStatus+ +" "+b.jPlayer.emulateOptions).split(/\s+/g),function(b,d){delete a.internal.domNode[d]})}};b.jPlayer.error={FLASH:"e_flash",FLASH_DISABLED:"e_flash_disabled",NO_SOLUTION:"e_no_solution",NO_SUPPORT:"e_no_support",URL:"e_url",URL_NOT_SET:"e_url_not_set",VERSION:"e_version"};b.jPlayer.errorMsg={FLASH:"jPlayer's Flash fallback is not configured correctly, or a command was issued before the jPlayer Ready event. Details: ",FLASH_DISABLED:"jPlayer's Flash fallback has been disabled by the browser due to the CSS rules you have used. Details: ", +NO_SOLUTION:"No solution can be found by jPlayer in this browser. Neither HTML nor Flash can be used.",NO_SUPPORT:"It is not possible to play any media format provided in setMedia() on this browser using your current options.",URL:"Media URL could not be loaded.",URL_NOT_SET:"Attempt to issue media playback commands, while no media url is set.",VERSION:"jPlayer "+b.jPlayer.prototype.version.script+" needs Jplayer.swf version "+b.jPlayer.prototype.version.needFlash+" but found "};b.jPlayer.errorHint= +{FLASH:"Check your swfPath option and that Jplayer.swf is there.",FLASH_DISABLED:"Check that you have not display:none; the jPlayer entity or any ancestor.",NO_SOLUTION:"Review the jPlayer options: support and supplied.",NO_SUPPORT:"Video or audio formats defined in the supplied option are missing.",URL:"Check media URL is valid.",URL_NOT_SET:"Use setMedia() to set the media URL.",VERSION:"Update jPlayer files."};b.jPlayer.warning={CSS_SELECTOR_COUNT:"e_css_selector_count",CSS_SELECTOR_METHOD:"e_css_selector_method", +CSS_SELECTOR_STRING:"e_css_selector_string",OPTION_KEY:"e_option_key"};b.jPlayer.warningMsg={CSS_SELECTOR_COUNT:"The number of css selectors found did not equal one: ",CSS_SELECTOR_METHOD:"The methodName given in jPlayer('cssSelector') is not a valid jPlayer method.",CSS_SELECTOR_STRING:"The methodCssSelector given in jPlayer('cssSelector') is not a String or is empty.",OPTION_KEY:"The option requested in jPlayer('option') is undefined."};b.jPlayer.warningHint={CSS_SELECTOR_COUNT:"Check your css selector and the ancestor.", +CSS_SELECTOR_METHOD:"Check your method name.",CSS_SELECTOR_STRING:"Check your css selector is a string.",OPTION_KEY:"Check your option name."}})(jQuery); \ No newline at end of file diff --git a/wolnelektury/static/js/base.js b/wolnelektury/static/js/base.js index b5693830b..028ab33f4 100755 --- a/wolnelektury/static/js/base.js +++ b/wolnelektury/static/js/base.js @@ -8,6 +8,15 @@ $('#themes-list-toggle').click(function(event) { }); +$('.open-player').click(function(event) { + event.preventDefault(); + window.open($(this).attr('href'), + 'player', + 'width=420, height=500' + ); +}); + + }); })(jQuery) diff --git a/wolnelektury/static/js/player.js b/wolnelektury/static/js/player.js new file mode 100755 index 000000000..fea845029 --- /dev/null +++ b/wolnelektury/static/js/player.js @@ -0,0 +1,33 @@ +(function($) { + $(function() { + + + $("#jplayer").jPlayer({ + swfPath: "/static/jplayer/", + solution: "html,flash", + supplied: $(this).attr('data-supplied'), + + ready: function() { + var player = $(this); + var setMedia = function(elem) { + var li = $(elem).parent(); + $('.jp-playlist-current').removeClass('jp-playlist-current'); + $(li).addClass('jp-playlist-current'); + var media = {} + + $('.mp3', li).each(function() {media['mp3'] = $(this).attr('href')}); + $('.ogg', li).each(function() {media['oga'] = $(this).attr('href')}); + + return player.jPlayer("setMedia", media); + }; + setMedia($('.play').first()).jPlayer("play"); + + $('.play').click(function() { + setMedia(this).jPlayer("play"); + }); + } + }); + + + }); +})(jQuery) \ No newline at end of file diff --git a/wolnelektury/templates/catalogue/book_short.html b/wolnelektury/templates/catalogue/book_short.html index 524617425..5557126da 100644 --- a/wolnelektury/templates/catalogue/book_short.html +++ b/wolnelektury/templates/catalogue/book_short.html @@ -60,7 +60,7 @@
  • {% if book.has_mp3_file %} - {% trans "Listen" %} + {% trans "Listen" %} {% endif %}
  • diff --git a/wolnelektury/templates/catalogue/player.html b/wolnelektury/templates/catalogue/player.html new file mode 100755 index 000000000..95fcb5e62 --- /dev/null +++ b/wolnelektury/templates/catalogue/player.html @@ -0,0 +1,149 @@ + + + {% load i18n compressed %} + + + + + {% trans "Wolne Lektury" %} :: + {{ book.title }} - {{ audiobook }} + + {% compressed_css "all" %} + {% compressed_css "player" %} + + + + + + + + + + +

    {% trans "Download as" %}: + MP3{% if have_oggs %}, + Ogg Vorbis{% endif %}. +

    + + + {% if book.has_daisy_file %} +

    DAISY:

    + + {% endif %} + + + {% if projects|length > 1 %} +

    {% trans "Audiobooks were prepared as a part of the projects:" %}

    +
      + {% for cs, fb in projects %} +
    • + {% if fb %} + {% blocktrans %}{{ cs }}, funded by {{ fb }}{% endblocktrans %} + {% else %} + {{ cs }} + {% endif %} +
    • + {% endfor %} +
    + {% else %} +

    + {% with projects.0.0 as cs %} + {% with projects.0.1 as fb %} + {% if fb %} + {% blocktrans %}Audiobooks were prepared as a part of the {{ cs }} project funded by {{ fb }}.{% endblocktrans %} + {% else %} + {% blocktrans %}Audiobooks were prepared as a part of the {{ cs }} project.{% endblocktrans %} + {% endif %} + {% endwith %} + {% endwith %} +

    + {% endif %} + + + + + + + + + +
    + + + + + + + {% compressed_js "player" %} + + + + -- 2.20.1 From 27645bc61a8cb186116d6410217011dd755823f2 Mon Sep 17 00:00:00 2001 From: Marcin Koziej Date: Thu, 29 Dec 2011 17:13:05 +0100 Subject: [PATCH 06/16] detail page getting shape --- apps/catalogue/templatetags/catalogue_tags.py | 17 ++ scripts/make-tags | 3 + wolnelektury/static/css/base.css | 2 + wolnelektury/static/css/book_box.css | 55 ++++- wolnelektury/static/css/header.css | 2 +- .../templates/catalogue/book_detail.html | 200 +----------------- .../templates/catalogue/book_short.html | 6 +- .../templates/catalogue/book_wide.html | 35 +++ 8 files changed, 116 insertions(+), 204 deletions(-) create mode 100644 wolnelektury/templates/catalogue/book_wide.html diff --git a/apps/catalogue/templatetags/catalogue_tags.py b/apps/catalogue/templatetags/catalogue_tags.py index b4a3ec51a..c5b904d40 100644 --- a/apps/catalogue/templatetags/catalogue_tags.py +++ b/apps/catalogue/templatetags/catalogue_tags.py @@ -15,6 +15,7 @@ from django.conf import settings from django.utils.translation import ugettext as _ from catalogue.forms import SearchForm +from catalogue.utils import split_tags register = template.Library() @@ -275,3 +276,19 @@ def inline_tag_list(tags, choices=None): @register.inclusion_tag('catalogue/book_info.html') def book_info(book): return locals() + + +@register.inclusion_tag('catalogue/book_wide.html') +def book_wide(book): + tags = book.tags.filter(category__in=('author', 'kind', 'genre', 'epoch')) + tags = split_tags(tags) + + formats = {} + # files generated during publication + for ebook_format in book.ebook_formats: + if book.has_media(ebook_format): + formats[ebook_format] = book.get_media(ebook_format) + + extra_info = book.get_extra_info_value() + + return locals() diff --git a/scripts/make-tags b/scripts/make-tags index 0afb8592a..754e63598 100755 --- a/scripts/make-tags +++ b/scripts/make-tags @@ -8,3 +8,6 @@ if [ -n "$VIRTUAL_ENV" ]; then else echo "No Virtual env enabled, will not add it to TAGS" fi + +find $ROOT/wolnelektury/static/css -name '*.css' |xargs etags -a -o ${ROOT}/TAGS +find $ROOT/wolnelektury/static/js -name '*.js' |xargs etags -a -o ${ROOT}/TAGS diff --git a/wolnelektury/static/css/base.css b/wolnelektury/static/css/base.css index dd686c792..922915ac6 100755 --- a/wolnelektury/static/css/base.css +++ b/wolnelektury/static/css/base.css @@ -69,9 +69,11 @@ h2 { .page-desc { margin-left: 1.5em; } + .inline-tag-lists { font-size: 1.1em; } + #themes-list-toggle:after { padding-left: 1em; content: "↓"; diff --git a/wolnelektury/static/css/book_box.css b/wolnelektury/static/css/book_box.css index b51750287..aa50e67f4 100755 --- a/wolnelektury/static/css/book_box.css +++ b/wolnelektury/static/css/book_box.css @@ -1,4 +1,4 @@ -.book-mini-box, .book-box { +.book-wide-box, .book-mini-box, .book-box { display: inline-block; margin: 0; vertical-align: top; @@ -13,6 +13,10 @@ width: 16.15em; } +.book-wide-box { + width: 98.5em; +} + .book-mini-box a, .book-box-inner { display: block; color: black; @@ -36,14 +40,14 @@ margin: .5em; } -.book-mini-box img, .book-box img { +.book-mini-box img, .book-box img, .book-wide-box img { width: 13.9em; height: 19.3em; } .book-mini-box img { margin-bottom: 1.8em; } -.book-box img { +.book-box img, .book-wide-box img { float: left; margin-right: 1.5em; } @@ -132,6 +136,16 @@ margin-right: .25em; vertical-align: middle; } + +ul.book-box-tools { + margin: 0; + padding: 0; +} + +.book-box-tools li { + display: inline-block; +} + .book-box-read { width: 11em; } @@ -141,3 +155,38 @@ .book-box-audiobook { width: 8em; } + +.book-wide-box .right-column { + float: right; + width: 41.5em; +} + +.book-wide-box blockquote.cite-body { + /* @ 18pt */ + width: 100%; /*23.055em;*/ + height: 7.222em; + background-color: #f7f7f7; + margin: 0; + position: relative; + top: -0.444em; + right: -0.555em; + vertical-align: center; +} + +.book-wide-box blockquote div { + padding: 0.888em; +} + +.book-wide-box #other-tools { + float: left; + width: 14.5em; + margin-left: 1.5em; + +} + + +.book-wide-box #other-download { + float: left; + width 22.5em; + margin: 0em 1.5em 0em 1.5em +} diff --git a/wolnelektury/static/css/header.css b/wolnelektury/static/css/header.css index 8615d8b59..04f2c8714 100755 --- a/wolnelektury/static/css/header.css +++ b/wolnelektury/static/css/header.css @@ -37,7 +37,7 @@ #logo { position: absolute; - top: 5.5em; + top: 1.9em; margin-left: 1.5em; } diff --git a/wolnelektury/templates/catalogue/book_detail.html b/wolnelektury/templates/catalogue/book_detail.html index 4b4ee2f18..0a8b2236c 100644 --- a/wolnelektury/templates/catalogue/book_detail.html +++ b/wolnelektury/templates/catalogue/book_detail.html @@ -9,206 +9,8 @@ {% block bodyid %}book-detail{% endblock %} {% block body %} -

    {% book_title book %}

    -
    - +{% book_wide book %} - {% if extra_info.license %} -

    {% trans "Work is licensed under " %} {{ extra_info.license_description }}.

    - {% endif %} -

    {% trans "Based on" %}: {{ extra_info.source_name }}

    - {% if book.has_description %} -
    -
    {{ book.description|safe }}
    -
    {{ book.description|safe|truncatewords_html:30 }}
    -
    -

    - {% endif %} -
    -

    {% trans "Put a book" %} {% trans "on the shelf!" %}

    -
    -
    - {% if book.has_html_file %} - {% trans "Read online" %} - {% endif %} -
    - {% if book.pdf_file %} - {% trans - {% endif %} - {% if book.epub_file %} - {% trans - {% endif %} - {% if book.mobi_file %} - {% trans - {% endif %} - {% if book.txt_file %} - {% trans - {% endif %} - - {% if book.pdf_file %} -
    {% trans "Dowload customized PDF" %}. - {% endif %} - -
    - {% if book.has_mp3_file or book.has_ogg_file or book.has_daisy_file %} -

    - {% trans "Audiobooks" %}: - - {% if book.has_mp3_file %}MP3{% endif %} - {% if book.has_ogg_file %}Ogg Vorbis{% endif %} - {% if book.has_daisy_file %}DAISY{% endif %} - -

    -
    - Speaker icon - {% if book.has_mp3_file %} -
      - {% for media in book.get_mp3 %} -
    • - {{ media.name }}
      - {% trans "Artist" %}: {{ media.get_extra_info_value.artist_name }}
      - {% trans "Director"%}: {{ media.get_extra_info_value.director_name }}
      - - - - - - -
    • - {% endfor %} -
    - {% endif %} - {% if book.has_ogg_file %} - - {% endif %} - {% if book.has_daisy_file %} - - {% endif %} -
    - {% if projects|length > 1 %} -

    {% trans "Audiobooks were prepared as a part of the projects:" %}

    -
      - {% for cs, fb in projects %} -
    • - {% if fb %} - {% blocktrans %}{{ cs }}, funded by {{ fb }}{% endblocktrans %} - {% else %} - {{ cs }} - {% endif %} -
    • - {% endfor %} -
    - {% else %} -

    - {% with projects.0.0 as cs %} - {% with projects.0.1 as fb %} - {% if fb %} - {% blocktrans %}Audiobooks were prepared as a part of the {{ cs }} project funded by {{ fb }}.{% endblocktrans %} - {% else %} - {% blocktrans %}Audiobooks were prepared as a part of the {{ cs }} project.{% endblocktrans %} - {% endif %} - {% endwith %} - {% endwith %} -

    - {% endif %} - {% endif %} -
    -
    - - {% if book_children %} - {% autopaginate book_children 10 %} -
    -
      - {% for book in book_children %} -
    1. {{ book.short_html }}
    2. - {% endfor %} -
    -
    - {% paginate %} - {% endif %} - -
    - -
    -
    -

    {% trans "Details" %}

    -
      -
    • - {% trans "Author" %}: - {% for tag in categories.author %} - {{ tag }}{% if not forloop.last %}, {% endif %} - {% endfor %} -
    • -
    • - {% trans "Epoch" %}: - {% for tag in categories.epoch %} - {{ tag }}{% if not forloop.last %}, {% endif %} - {% endfor %} -
    • -
    • - {% trans "Kind" %}: - {% for tag in categories.kind %} - {{ tag }}{% if not forloop.last %}, {% endif %} - {% endfor %} -
    • -
    • - {% trans "Genre" %}: - {% for tag in categories.genre %} - {{ tag }}{% if not forloop.last %}, {% endif %} - {% endfor %} -
    • -
    -

    {% trans "Other resources" %}

    - -

    {% trans "View XML source" %}

    -

    Miksuj ten utwór

    -
    -
    -

    {% trans "Work's themes " %}

    - -
    -
    -
    {% endblock %} diff --git a/wolnelektury/templates/catalogue/book_short.html b/wolnelektury/templates/catalogue/book_short.html index 8ac23a3a6..5a9b17de9 100644 --- a/wolnelektury/templates/catalogue/book_short.html +++ b/wolnelektury/templates/catalogue/book_short.html @@ -1,6 +1,6 @@ {% load i18n %} {% load thumbnail %} -
    +
    {% if book.cover %} @@ -13,6 +13,8 @@ " alt="Cover" /> {% endif %} + {% block right-column %} + {% endblock %}
    @@ -75,5 +77,7 @@ {% endif %} + {% block box-append %} + {% endblock %}
    diff --git a/wolnelektury/templates/catalogue/book_wide.html b/wolnelektury/templates/catalogue/book_wide.html new file mode 100644 index 000000000..1cfde2e14 --- /dev/null +++ b/wolnelektury/templates/catalogue/book_wide.html @@ -0,0 +1,35 @@ +{% extends "catalogue/book_short.html" %} +{% load i18n %} + +{% block box-class %}book-wide-box{% endblock %} + +{% block right-column %} +
    +
    +
    Ten, który walczy z potworami powinien zadbać, by sam nie stał się potworem. + Gdy długo spoglądamy w otchłań, otchłań spogląda również w nas.
    +
    + +
    +

    {% trans "See" %}

    + +
    +
    +

    {% trans "Download" %}

    +

    {% trans "Download all audiobooks for this book" %}

    +
    +
    +{% endblock %} -- 2.20.1 From 938fc832934e508085dad3645c01060a4dc9f0c9 Mon Sep 17 00:00:00 2001 From: Radek Czajka Date: Thu, 29 Dec 2011 17:23:06 +0100 Subject: [PATCH 07/16] change in multilingual --- apps/api/handlers.py | 25 +--- apps/api/urls.py | 4 +- apps/catalogue/import_utils.py | 17 +++ .../management/commands/importbooks.py | 3 +- ...g__add_unique_book_slug__del_unique_boo.py | 137 ++++++++++++++++++ apps/catalogue/migrations/0023_common_slug.py | 123 ++++++++++++++++ apps/catalogue/models.py | 101 ++++--------- apps/catalogue/test_utils.py | 6 +- apps/catalogue/tests/book_import.py | 10 +- apps/catalogue/tests/bookmedia.py | 4 +- apps/catalogue/tests/tags.py | 10 +- apps/catalogue/urls.py | 23 +-- apps/catalogue/utils.py | 21 +-- apps/catalogue/views.py | 62 +++----- .../templates/dictionary/note_list.html | 2 +- apps/lesmianator/urls.py | 2 +- apps/lesmianator/views.py | 7 +- apps/picture/models.py | 8 +- lib/librarian | 2 +- wolnelektury/settings.py | 3 +- .../templates/catalogue/book_detail.html | 81 +---------- .../templates/catalogue/book_sets.html | 2 +- .../templates/catalogue/book_short.html | 2 +- wolnelektury/templates/catalogue/player.html | 4 +- .../catalogue/tagged_object_list.html | 2 +- wolnelektury/templates/lesmianator/poem.html | 4 +- 26 files changed, 393 insertions(+), 272 deletions(-) create mode 100755 apps/catalogue/import_utils.py create mode 100644 apps/catalogue/migrations/0022_auto__add_field_book_common_slug__add_unique_book_slug__del_unique_boo.py create mode 100644 apps/catalogue/migrations/0023_common_slug.py diff --git a/apps/api/handlers.py b/apps/api/handlers.py index d34806f0a..586e921ca 100644 --- a/apps/api/handlers.py +++ b/apps/api/handlers.py @@ -99,14 +99,10 @@ class BookDetailHandler(BaseHandler): 'media', 'url'] + category_singular.keys() @piwik_track - def read(self, request, book): + def read(self, request, slug): """ Returns details of a book, identified by a slug and lang. """ - kwargs = Book.split_urlid(book) - if not kwargs: - return rc.NOT_FOUND - try: - return Book.objects.get(**kwargs) + return Book.objects.get(slug=slug) except Book.DoesNotExist: return rc.NOT_FOUND @@ -127,7 +123,7 @@ class AnonymousBooksHandler(AnonymousBaseHandler): @classmethod def href(cls, book): """ Returns an URI for a Book in the API. """ - return API_BASE + reverse("api_book", args=[book.urlid()]) + return API_BASE + reverse("api_book", args=[book.slug]) @classmethod def url(cls, book): @@ -269,18 +265,10 @@ class FragmentDetailHandler(BaseHandler): fields = ['book', 'anchor', 'text', 'url', 'themes'] @piwik_track - def read(self, request, book, anchor): + def read(self, request, slug, anchor): """ Returns details of a fragment, identified by book slug and anchor. """ - kwargs = Book.split_urlid(book) - if not kwargs: - return rc.NOT_FOUND - - fragment_kwargs = {} - for field, value in kwargs.items(): - fragment_kwargs['book__' + field] = value - try: - return Fragment.objects.get(anchor=anchor, **fragment_kwargs) + return Fragment.objects.get(book__slug=slug, anchor=anchor) except Fragment.DoesNotExist: return rc.NOT_FOUND @@ -317,7 +305,8 @@ class FragmentsHandler(BaseHandler): def href(cls, fragment): """ Returns URI in the API for the fragment. """ - return API_BASE + reverse("api_fragment", args=[fragment.book.urlid(), fragment.anchor]) + return API_BASE + reverse("api_fragment", + args=[fragment.book.slug, fragment.anchor]) @classmethod def url(cls, fragment): diff --git a/apps/api/urls.py b/apps/api/urls.py index 8f7bed3fe..fd97d63d9 100644 --- a/apps/api/urls.py +++ b/apps/api/urls.py @@ -47,10 +47,10 @@ urlpatterns = patterns( # objects details - url(r'^books/(?P%s)/$' % Book.URLID_RE, book_resource, name="api_book"), + url(r'^books/(?P[a-z0-9-]+)/$', book_resource, name="api_book"), url(r'^(?P[a-z0-9-]+)/(?P[a-z0-9-]+)/$', tag_resource, name="api_tag"), - url(r'^books/(?P%s)/fragments/(?P[a-z0-9-]+)/$' % Book.URLID_RE, + url(r'^books/(?P[a-z0-9-]+)/fragments/(?P[a-z0-9-]+)/$', fragment_resource, name="api_fragment"), # books by tags diff --git a/apps/catalogue/import_utils.py b/apps/catalogue/import_utils.py new file mode 100755 index 000000000..bf36ea57f --- /dev/null +++ b/apps/catalogue/import_utils.py @@ -0,0 +1,17 @@ +# -*- 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 librarian import DocProvider + +class ORMDocProvider(DocProvider): + """Used for getting books' children.""" + + def __init__(self, book): + self.book = book + + def by_slug(self, slug): + if slug == self.book.slug: + return open(self.book.xml_file.path) + else: + return type(self.book).objects.get(slug=slug).xml_file \ No newline at end of file diff --git a/apps/catalogue/management/commands/importbooks.py b/apps/catalogue/management/commands/importbooks.py index d097ddd1a..995132a82 100644 --- a/apps/catalogue/management/commands/importbooks.py +++ b/apps/catalogue/management/commands/importbooks.py @@ -46,11 +46,10 @@ class Command(BaseCommand): build_txt=options.get('build_txt'), build_pdf=options.get('build_pdf'), build_mobi=options.get('build_mobi')) - fileid = book.fileid() for ebook_format in Book.ebook_formats: if os.path.isfile(file_base + '.' + ebook_format): getattr(book, '%s_file' % ebook_format).save( - '%s.%s' % (fileid, ebook_format), + '%s.%s' % (book.slug, ebook_format), File(file(file_base + '.' + ebook_format))) if verbose: print "Importing %s.%s" % (file_base, ebook_format) diff --git a/apps/catalogue/migrations/0022_auto__add_field_book_common_slug__add_unique_book_slug__del_unique_boo.py b/apps/catalogue/migrations/0022_auto__add_field_book_common_slug__add_unique_book_slug__del_unique_boo.py new file mode 100644 index 000000000..75a1c99f4 --- /dev/null +++ b/apps/catalogue/migrations/0022_auto__add_field_book_common_slug__add_unique_book_slug__del_unique_boo.py @@ -0,0 +1,137 @@ +# 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): + + # Removing unique constraint on 'Book', fields ['slug', 'language'] + db.delete_unique('catalogue_book', ['slug', 'language']) + + # Adding field 'Book.common_slug' + db.add_column('catalogue_book', 'common_slug', self.gf('django.db.models.fields.SlugField')(default='-', max_length=120, db_index=True), keep_default=False) + + # Adding unique constraint on 'Book', fields ['slug'] + db.create_unique('catalogue_book', ['slug']) + + + def backwards(self, orm): + + # Removing unique constraint on 'Book', fields ['slug'] + db.delete_unique('catalogue_book', ['slug']) + + # Deleting field 'Book.common_slug' + db.delete_column('catalogue_book', 'common_slug') + + # Adding unique constraint on 'Book', fields ['slug', 'language'] + db.create_unique('catalogue_book', ['slug', 'language']) + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'catalogue.book': { + 'Meta': {'ordering': "('sort_key',)", 'object_name': 'Book'}, + 'changed_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'db_index': 'True', 'blank': 'True'}), + 'common_slug': ('django.db.models.fields.SlugField', [], {'max_length': '120', 'db_index': 'True'}), + 'cover': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'db_index': 'True', 'blank': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'epub_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'blank': 'True'}), + 'extra_info': ('catalogue.fields.JSONField', [], {'default': "'{}'"}), + 'gazeta_link': ('django.db.models.fields.CharField', [], {'max_length': '240', 'blank': 'True'}), + 'html_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'language': ('django.db.models.fields.CharField', [], {'default': "'pol'", 'max_length': '3', 'db_index': 'True'}), + 'mobi_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'blank': 'True'}), + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['catalogue.Book']"}), + 'parent_number': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'pdf_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'blank': 'True'}), + 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '120', 'db_index': 'True'}), + 'sort_key': ('django.db.models.fields.CharField', [], {'max_length': '120', 'db_index': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '120'}), + 'txt_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'blank': 'True'}), + 'wiki_link': ('django.db.models.fields.CharField', [], {'max_length': '240', 'blank': 'True'}), + 'xml_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'blank': 'True'}) + }, + 'catalogue.bookmedia': { + 'Meta': {'ordering': "('type', 'name')", 'object_name': 'BookMedia'}, + 'book': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'media'", 'to': "orm['catalogue.Book']"}), + 'extra_info': ('catalogue.fields.JSONField', [], {'default': "'{}'"}), + 'file': ('catalogue.fields.OverwritingFileField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': "'100'"}), + 'source_sha1': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'}), + 'type': ('django.db.models.fields.CharField', [], {'max_length': "'100'"}), + 'uploaded_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}) + }, + 'catalogue.fragment': { + 'Meta': {'ordering': "('book', 'anchor')", 'object_name': 'Fragment'}, + 'anchor': ('django.db.models.fields.CharField', [], {'max_length': '120'}), + 'book': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'fragments'", 'to': "orm['catalogue.Book']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'short_text': ('django.db.models.fields.TextField', [], {}), + 'text': ('django.db.models.fields.TextField', [], {}) + }, + 'catalogue.tag': { + 'Meta': {'ordering': "('sort_key',)", 'unique_together': "(('slug', 'category'),)", 'object_name': 'Tag'}, + 'book_count': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'category': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'}), + 'changed_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'db_index': 'True', 'blank': 'True'}), + 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'db_index': 'True', 'blank': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'gazeta_link': ('django.db.models.fields.CharField', [], {'max_length': '240', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '120', 'db_index': 'True'}), + 'sort_key': ('django.db.models.fields.CharField', [], {'max_length': '120', 'db_index': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}), + 'wiki_link': ('django.db.models.fields.CharField', [], {'max_length': '240', 'blank': 'True'}) + }, + 'catalogue.tagrelation': { + 'Meta': {'unique_together': "(('tag', 'content_type', 'object_id'),)", 'object_name': 'TagRelation', 'db_table': "'catalogue_tag_relation'"}, + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}), + 'tag': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'items'", 'to': "orm['catalogue.Tag']"}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + } + } + + complete_apps = ['catalogue'] diff --git a/apps/catalogue/migrations/0023_common_slug.py b/apps/catalogue/migrations/0023_common_slug.py new file mode 100644 index 000000000..386314915 --- /dev/null +++ b/apps/catalogue/migrations/0023_common_slug.py @@ -0,0 +1,123 @@ +# encoding: utf-8 +import datetime +from south.db import db +from south.v2 import DataMigration +from django.db import models + +class Migration(DataMigration): + + def forwards(self, orm): + "Write your forwards methods here." + orm.Book.objects.all().update(common_slug=models.F('slug')) + + + def backwards(self, orm): + "Write your backwards methods here." + pass + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'catalogue.book': { + 'Meta': {'ordering': "('sort_key',)", 'object_name': 'Book'}, + 'changed_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'db_index': 'True', 'blank': 'True'}), + 'common_slug': ('django.db.models.fields.SlugField', [], {'max_length': '120', 'db_index': 'True'}), + 'cover': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'db_index': 'True', 'blank': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'epub_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'blank': 'True'}), + 'extra_info': ('catalogue.fields.JSONField', [], {'default': "'{}'"}), + 'gazeta_link': ('django.db.models.fields.CharField', [], {'max_length': '240', 'blank': 'True'}), + 'html_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'language': ('django.db.models.fields.CharField', [], {'default': "'pol'", 'max_length': '3', 'db_index': 'True'}), + 'mobi_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'blank': 'True'}), + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['catalogue.Book']"}), + 'parent_number': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'pdf_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'blank': 'True'}), + 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '120', 'db_index': 'True'}), + 'sort_key': ('django.db.models.fields.CharField', [], {'max_length': '120', 'db_index': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '120'}), + 'txt_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'blank': 'True'}), + 'wiki_link': ('django.db.models.fields.CharField', [], {'max_length': '240', 'blank': 'True'}), + 'xml_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'blank': 'True'}) + }, + 'catalogue.bookmedia': { + 'Meta': {'ordering': "('type', 'name')", 'object_name': 'BookMedia'}, + 'book': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'media'", 'to': "orm['catalogue.Book']"}), + 'extra_info': ('catalogue.fields.JSONField', [], {'default': "'{}'"}), + 'file': ('catalogue.fields.OverwritingFileField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': "'100'"}), + 'source_sha1': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'}), + 'type': ('django.db.models.fields.CharField', [], {'max_length': "'100'"}), + 'uploaded_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}) + }, + 'catalogue.fragment': { + 'Meta': {'ordering': "('book', 'anchor')", 'object_name': 'Fragment'}, + 'anchor': ('django.db.models.fields.CharField', [], {'max_length': '120'}), + 'book': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'fragments'", 'to': "orm['catalogue.Book']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'short_text': ('django.db.models.fields.TextField', [], {}), + 'text': ('django.db.models.fields.TextField', [], {}) + }, + 'catalogue.tag': { + 'Meta': {'ordering': "('sort_key',)", 'unique_together': "(('slug', 'category'),)", 'object_name': 'Tag'}, + 'book_count': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'category': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'}), + 'changed_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'db_index': 'True', 'blank': 'True'}), + 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'db_index': 'True', 'blank': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'gazeta_link': ('django.db.models.fields.CharField', [], {'max_length': '240', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '120', 'db_index': 'True'}), + 'sort_key': ('django.db.models.fields.CharField', [], {'max_length': '120', 'db_index': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}), + 'wiki_link': ('django.db.models.fields.CharField', [], {'max_length': '240', 'blank': 'True'}) + }, + 'catalogue.tagrelation': { + 'Meta': {'unique_together': "(('tag', 'content_type', 'object_id'),)", 'object_name': 'TagRelation', 'db_table': "'catalogue_tag_relation'"}, + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}), + 'tag': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'items'", 'to': "orm['catalogue.Tag']"}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + } + } + + complete_apps = ['catalogue'] diff --git a/apps/catalogue/models.py b/apps/catalogue/models.py index 112ec1b99..43a9496d8 100644 --- a/apps/catalogue/models.py +++ b/apps/catalogue/models.py @@ -220,7 +220,7 @@ def get_customized_pdf_path(book, customizations): customizations.sort() h = hash(tuple(customizations)) - pdf_name = '%s-custom-%s' % (book.fileid(), h) + pdf_name = '%s-custom-%s' % (book.slug, h) pdf_file = get_dynamic_path(None, pdf_name, ext='pdf') return pdf_file @@ -230,7 +230,7 @@ def get_existing_customized_pdf(book): """ Returns a list of paths to generated customized pdf of a book """ - pdf_glob = '%s-custom-' % (book.fileid(),) + 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)) @@ -269,7 +269,7 @@ class BookMedia(models.Model): try: old = BookMedia.objects.get(pk=self.pk) except BookMedia.DoesNotExist, e: - pass + old = None else: # if name changed, change the file name, too if slughifi(self.name) != slughifi(old.name): @@ -278,7 +278,9 @@ class BookMedia(models.Model): super(BookMedia, self).save(*args, **kwargs) # remove the zip package for book with modified media - remove_zip(self.book.fileid()) + if old: + remove_zip("%s_%s" % (old.book.slug, old.type)) + remove_zip("%s_%s" % (self.book.slug, self.type)) extra_info = self.get_extra_info_value() extra_info.update(self.read_meta()) @@ -347,7 +349,9 @@ class BookMedia(models.Model): class Book(models.Model): title = models.CharField(_('title'), max_length=120) sort_key = models.CharField(_('sort key'), max_length=120, db_index=True, editable=False) - slug = models.SlugField(_('slug'), max_length=120, db_index=True) + slug = models.SlugField(_('slug'), max_length=120, db_index=True, + unique=True) + common_slug = models.SlugField(_('slug'), max_length=120, db_index=True) language = models.CharField(_('language code'), max_length=3, db_index=True, default=settings.CATALOGUE_DEFAULT_LANGUAGE) description = models.TextField(_('description'), blank=True) @@ -372,14 +376,10 @@ class Book(models.Model): html_built = django.dispatch.Signal() published = django.dispatch.Signal() - URLID_RE = r'[a-z0-9-]+(?:/[a-z]{3})?' - FILEID_RE = r'[a-z0-9-]+(?:_[a-z]{3})?' - class AlreadyExists(Exception): pass class Meta: - unique_together = [['slug', 'language']] ordering = ('sort_key',) verbose_name = _('book') verbose_name_plural = _('books') @@ -387,42 +387,6 @@ class Book(models.Model): def __unicode__(self): return self.title - def urlid(self, sep='/'): - stem = self.slug - if self.language != settings.CATALOGUE_DEFAULT_LANGUAGE: - stem += sep + self.language - return stem - - def fileid(self): - return self.urlid('_') - - @staticmethod - def split_urlid(urlid, sep='/', default_lang=settings.CATALOGUE_DEFAULT_LANGUAGE): - """Splits a URL book id into slug and language code. - - Returns a dictionary usable i.e. for object lookup, or None. - - >>> Book.split_urlid("a-slug/pol", default_lang="eng") - {'slug': 'a-slug', 'language': 'pol'} - >>> Book.split_urlid("a-slug", default_lang="eng") - {'slug': 'a-slug', 'language': 'eng'} - >>> Book.split_urlid("a-slug_pol", "_", default_lang="eng") - {'slug': 'a-slug', 'language': 'pol'} - >>> Book.split_urlid("a-slug/eng", default_lang="eng") - - """ - parts = urlid.rsplit(sep, 1) - if len(parts) == 2: - if parts[1] == default_lang: - return None - return {'slug': parts[0], 'language': parts[1]} - else: - return {'slug': urlid, 'language': default_lang} - - @classmethod - def split_fileid(cls, fileid): - return cls.split_urlid(fileid, '_') - def save(self, force_insert=False, force_update=False, reset_short_html=True, **kwargs): from sortify import sortify @@ -437,18 +401,14 @@ class Book(models.Model): @permalink def get_absolute_url(self): - return ('catalogue.views.book_detail', [self.urlid()]) + return ('catalogue.views.book_detail', [self.slug]) @property def name(self): return self.title def book_tag_slug(self): - stem = 'l-' + self.slug - if self.language != settings.CATALOGUE_DEFAULT_LANGUAGE: - return stem[:116] + ' ' + self.language - else: - return stem[:120] + return ('l-' + self.slug)[:120] def book_tag(self): slug = self.book_tag_slug() @@ -565,7 +525,7 @@ class Book(models.Model): has_daisy_file.boolean = True def wldocument(self, parse_dublincore=True): - from catalogue.utils import ORMDocProvider + from catalogue.import_utils import ORMDocProvider from librarian.parser import WLDocument return WLDocument.from_file(self.xml_file.path, @@ -601,7 +561,7 @@ class Book(models.Model): # 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.fileid(), + current_self.pdf_file.save('%s.pdf' % self.slug, File(open(pdf.get_filename()))) self.pdf_file = current_self.pdf_file @@ -624,7 +584,7 @@ class Book(models.Model): mobi = self.wldocument().as_mobi() - self.mobi_file.save('%s.mobi' % self.fileid(), File(open(mobi.get_filename()))) + 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) @@ -636,7 +596,7 @@ class Book(models.Model): epub = self.wldocument().as_epub() - self.epub_file.save('%s.epub' % self.fileid(), + self.epub_file.save('%s.epub' % self.slug, File(open(epub.get_filename()))) # remove zip package with all epub files @@ -646,7 +606,7 @@ class Book(models.Model): from django.core.files.base import ContentFile text = self.wldocument().as_text() - self.txt_file.save('%s.txt' % self.fileid(), ContentFile(text.get_string())) + self.txt_file.save('%s.txt' % self.slug, ContentFile(text.get_string())) def build_html(self): @@ -661,7 +621,7 @@ class Book(models.Model): html_output = self.wldocument(parse_dublincore=False).as_html() if html_output: - self.html_file.save('%s.html' % self.fileid(), + self.html_file.save('%s.html' % self.slug, ContentFile(html_output.get_string())) # get ancestor l-tags for adding to new fragments @@ -712,7 +672,7 @@ class Book(models.Model): def pretty_file_name(book): return "%s/%s.%s" % ( b.get_extra_info_value()['author'], - b.fileid(), + b.slug, format_) field_name = "%s_file" % format_ @@ -726,7 +686,7 @@ class Book(models.Model): 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.fileid(), format_)) + result = create_zip.delay(paths, "%s_%s" % (self.slug, format_)) return result.wait() @classmethod @@ -756,30 +716,33 @@ class Book(models.Model): if hasattr(book_info, 'parts'): for part_url in book_info.parts: try: - children.append(Book.objects.get( - slug=part_url.slug, language=part_url.language)) + children.append(Book.objects.get(slug=part_url.slug)) except Book.DoesNotExist, e: - raise Book.DoesNotExist(_('Book "%s/%s" does not exist.') % - (part_url.slug, part_url.language)) + raise Book.DoesNotExist(_('Book "%s" does not exist.') % + part_url.slug) # Read book metadata book_slug = book_info.url.slug - language = book_info.language - if re.search(r'[^a-zA-Z0-9-]', book_slug): + if re.search(r'[^a-z0-9-]', book_slug): raise ValueError('Invalid characters in slug') - book, created = Book.objects.get_or_create(slug=book_slug, language=language) + book, created = Book.objects.get_or_create(slug=book_slug) if created: book_shelves = [] else: if not overwrite: - raise Book.AlreadyExists(_('Book %s/%s already exists') % ( - book_slug, language)) + raise Book.AlreadyExists(_('Book %s already exists') % ( + book_slug)) # Save shelves for this book book_shelves = list(book.tags.filter(category='set')) + book.language = book_info.language book.title = book_info.title + if book_info.variant_of: + book.common_slug = book_info.variant_of.slug + else: + book.common_slug = book.slug book.set_extra_info_value(book_info.to_dict()) book.save() @@ -940,7 +903,7 @@ class Book(models.Model): books_by_parent = {} books = cls.objects.all().order_by('parent_number', 'sort_key').only( - 'title', 'parent', 'slug', 'language') + 'title', 'parent', 'slug') if filter: books = books.filter(filter).distinct() book_ids = set((book.pk for book in books)) diff --git a/apps/catalogue/test_utils.py b/apps/catalogue/test_utils.py index 58ef58acd..eeda03f04 100644 --- a/apps/catalogue/test_utils.py +++ b/apps/catalogue/test_utils.py @@ -13,10 +13,12 @@ class WLTestCase(TestCase): self._MEDIA_ROOT, settings.MEDIA_ROOT = settings.MEDIA_ROOT, tempfile.mkdtemp(prefix='djangotest_') settings.NO_BUILD_PDF = settings.NO_BUILD_MOBI = settings.NO_BUILD_EPUB = settings.NO_BUILD_TXT = True settings.CELERY_ALWAYS_EAGER = True + self._CACHE_BACKEND, settings.CACHE_BACKEND = settings.CACHE_BACKEND, 'dummy://' def tearDown(self): shutil.rmtree(settings.MEDIA_ROOT, True) settings.MEDIA_ROOT = self._MEDIA_ROOT + settings.CACHE_BACKEND = self._CACHE_BACKEND class PersonStub(object): @@ -29,7 +31,7 @@ class PersonStub(object): class BookInfoStub(object): - _empty_fields = ['cover_url'] + _empty_fields = ['cover_url', 'variant_of'] # allow single definition for multiple-value fields _salias = { 'authors': 'author', @@ -65,7 +67,7 @@ def info_args(title, language=None): language = u'pol' return { 'title': unicode(title), - 'url': WLURI.from_slug_and_lang(slug, language), + 'url': WLURI.from_slug(slug), 'about': u"http://wolnelektury.pl/example/URI/%s" % slug, 'language': language, } diff --git a/apps/catalogue/tests/book_import.py b/apps/catalogue/tests/book_import.py index a97c41711..3af1bb486 100644 --- a/apps/catalogue/tests/book_import.py +++ b/apps/catalogue/tests/book_import.py @@ -15,7 +15,7 @@ class BookImportLogicTests(WLTestCase): def setUp(self): WLTestCase.setUp(self) self.book_info = BookInfoStub( - url=WLURI.from_slug_and_lang(u"default-book", None), + url=WLURI.from_slug(u"default-book"), about=u"http://wolnelektury.pl/example/URI/default_book", title=u"Default Book", author=PersonStub(("Jim",), "Lazy"), @@ -114,7 +114,7 @@ class BookImportLogicTests(WLTestCase): @raises(ValueError) def test_book_with_invalid_slug(self): """ Book with invalid characters in slug shouldn't be imported """ - self.book_info.url = WLURI.from_slug_and_lang(u"default_book", None) + self.book_info.url = WLURI.from_slug(u"default_book") BOOK_TEXT = "" book = models.Book.from_text_and_meta(ContentFile(BOOK_TEXT), self.book_info) @@ -247,12 +247,15 @@ class ChildImportTests(WLTestCase): class MultilingualBookImportTest(WLTestCase): def setUp(self): WLTestCase.setUp(self) + common_uri = WLURI.from_slug('common-slug') + self.pol_info = BookInfoStub( genre='X-Genre', epoch='X-Epoch', kind='X-Kind', author=PersonStub(("Joe",), "Doe"), - **info_args("A book") + variant_of=common_uri, + **info_args(u"Książka") ) self.eng_info = BookInfoStub( @@ -260,6 +263,7 @@ class MultilingualBookImportTest(WLTestCase): epoch='X-Epoch', kind='X-Kind', author=PersonStub(("Joe",), "Doe"), + variant_of=common_uri, **info_args("A book", "eng") ) diff --git a/apps/catalogue/tests/bookmedia.py b/apps/catalogue/tests/bookmedia.py index b6598b33d..5d2ba66c3 100644 --- a/apps/catalogue/tests/bookmedia.py +++ b/apps/catalogue/tests/bookmedia.py @@ -103,8 +103,8 @@ class BookMediaTests(WLTestCase): bm.file.save(None, self.file) bm.save() - zip_url = self.book.zip_audiobooks() - self.assertEqual('zip/'+self.book.slug+'.zip', zip_url) + zip_url = self.book.zip_audiobooks('ogg') + self.assertEqual('zip/'+self.book.slug+'_ogg.zip', zip_url) self.assertTrue(exists(join(settings.MEDIA_ROOT, zip_url))) bm2 = models.BookMedia(book=self.book, type='ogg', name="Other title") diff --git a/apps/catalogue/tests/tags.py b/apps/catalogue/tests/tags.py index 7e6e66716..a47e426a5 100644 --- a/apps/catalogue/tests/tags.py +++ b/apps/catalogue/tests/tags.py @@ -3,9 +3,6 @@ from catalogue import models from catalogue.test_utils import * from django.core.files.base import ContentFile -from nose.tools import raises - - class BooksByTagTests(WLTestCase): """ tests the /katalog/category/tag page for found books """ @@ -66,12 +63,13 @@ class BooksByTagTests(WLTestCase): ['Child']) - +from django.test import Client class TagRelatedTagsTests(WLTestCase): """ tests the /katalog/category/tag/ page for related tags """ def setUp(self): WLTestCase.setUp(self) + self.client = Client() author = PersonStub(("Common",), "Man") gchild_info = BookInfoStub(author=author, genre="GchildGenre", epoch='Epoch', kind="Kind", @@ -132,14 +130,14 @@ class TagRelatedTagsTests(WLTestCase): def test_related_differ(self): """ related tags shouldn't include filtering tags """ - cats = self.client.get('/katalog/rodzaj/kind/').context['categories'] + response = self.client.get('/katalog/rodzaj/kind/') + cats = response.context['categories'] self.assertFalse('Kind' in [tag.name for tag in cats['kind']], 'filtering tag wrongly included in related') cats = self.client.get('/katalog/motyw/theme/').context['categories'] self.assertFalse('Theme' in [tag.name for tag in cats['theme']], 'filtering theme wrongly included in related') - def test_parent_tag_once(self): """ if parent and descendants have a common tag, count it only once """ diff --git a/apps/catalogue/urls.py b/apps/catalogue/urls.py index 241afca3a..e5087b6d0 100644 --- a/apps/catalogue/urls.py +++ b/apps/catalogue/urls.py @@ -7,22 +7,25 @@ from catalogue.feeds import AudiobookFeed from catalogue.models import Book from picture.models import Picture + +SLUG = r'[a-z0-9-]*' + urlpatterns = patterns('picture.views', # pictures - currently pictures are coupled with catalogue, hence the url is here url(r'^obraz/?$', 'picture_list'), - url(r'^obraz/(?P%s)/?$' % Picture.URLID_RE, 'picture_detail') + url(r'^obraz/(?P%s)/?$' % SLUG, 'picture_detail') ) + \ patterns('catalogue.views', url(r'^$', 'catalogue', name='catalogue'), url(r'^polki/(?P[a-zA-Z0-9-]+)/formaty/$', 'shelf_book_formats', name='shelf_book_formats'), - url(r'^polki/(?P[a-zA-Z0-9-]+)/(?P%s)/usun$' % Book.URLID_RE, 'remove_from_shelf', name='remove_from_shelf'), + url(r'^polki/(?P[a-zA-Z0-9-]+)/(?P%s)/usun$' % SLUG, 'remove_from_shelf', name='remove_from_shelf'), url(r'^polki/$', 'user_shelves', name='user_shelves'), url(r'^polki/(?P[a-zA-Z0-9-]+)/usun/$', 'delete_shelf', name='delete_shelf'), url(r'^polki/(?P[a-zA-Z0-9-]+)\.zip$', 'download_shelf', name='download_shelf'), url(r'^lektury/', 'book_list', name='book_list'), url(r'^audiobooki/$', 'audiobook_list', name='audiobook_list'), url(r'^daisy/$', 'daisy_list', name='daisy_list'), - url(r'^lektura/(?P%s)/polki/' % Book.URLID_RE, 'book_sets', name='book_shelves'), + url(r'^lektura/(?P%s)/polki/' % SLUG, 'book_sets', name='book_shelves'), url(r'^polki/nowa/$', 'new_set', name='new_set'), url(r'^tags/$', 'tags_starting_with', name='hint'), url(r'^jtags/$', 'json_tags_starting_with', name='jhint'), @@ -32,20 +35,20 @@ urlpatterns = patterns('picture.views', 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'^zip/mobi\.zip$', 'download_zip', {'format': 'mobi', 'slug': None}, 'download_zip_mobi'), - url(r'^zip/mp3/(?P%s)\.zip' % Book.FILEID_RE, 'download_zip', {'format': 'mp3'}, 'download_zip_mp3'), - url(r'^zip/ogg/(?P%s)\.zip' % Book.FILEID_RE, 'download_zip', {'format': 'ogg'}, 'download_zip_ogg'), + url(r'^zip/mp3/(?P%s)\.zip' % SLUG, 'download_zip', {'format': 'mp3'}, 'download_zip_mp3'), + url(r'^zip/ogg/(?P%s)\.zip' % SLUG, 'download_zip', {'format': 'ogg'}, 'download_zip_ogg'), # Public interface. Do not change this URLs. - url(r'^lektura/(?P%s)\.html$' % Book.FILEID_RE, 'book_text', name='book_text'), - url(r'^lektura/(?P%s)/audiobook/$' % Book.URLID_RE, 'player', name='book_player'), - url(r'^lektura/(?P%s)/$' % Book.URLID_RE, 'book_detail', name='book_detail'), - url(r'^lektura/(?P%s)/motyw/(?P[a-zA-Z0-9-]+)/$' % Book.URLID_RE, + url(r'^lektura/(?P%s)\.html$' % SLUG, 'book_text', name='book_text'), + url(r'^lektura/(?P%s)/audiobook/$' % SLUG, 'player', name='book_player'), + url(r'^lektura/(?P%s)/$' % SLUG, 'book_detail', name='book_detail'), + url(r'^lektura/(?P%s)/motyw/(?P[a-zA-Z0-9-]+)/$' % SLUG, 'book_fragments', name='book_fragments'), url(r'^(?P[a-zA-Z0-9-/]*)/$', 'tagged_object_list', name='tagged_object_list'), url(r'^audiobooki/(?Pmp3|ogg|daisy|all).xml$', AudiobookFeed(), name='audiobook_feed'), - url(r'^custompdf/(?P%s).pdf' % Book.FILEID_RE, 'download_custom_pdf'), + url(r'^custompdf/(?P%s).pdf' % SLUG, 'download_custom_pdf'), ) diff --git a/apps/catalogue/utils.py b/apps/catalogue/utils.py index 145511e06..a48ec0370 100644 --- a/apps/catalogue/utils.py +++ b/apps/catalogue/utils.py @@ -20,7 +20,6 @@ from errno import EEXIST, ENOENT from fcntl import flock, LOCK_EX from zipfile import ZipFile -from librarian import DocProvider from reporting.utils import read_chunks from celery.task import task import catalogue.models @@ -60,20 +59,6 @@ class ExistingFile(UploadedFile): pass -class ORMDocProvider(DocProvider): - """Used for getting books' children.""" - - def __init__(self, book): - self.book = book - - def by_slug_and_lang(self, slug, language): - if slug == self.book.slug and language == self.language: - return open(self.book.xml_file.path) - else: - return type(self.book).objects.get( - slug=slug, language=language).xml_file - - class LockFile(object): """ A file lock monitor class; createas an ${objname}.lock @@ -180,7 +165,11 @@ class MultiQuerySet(object): return self.count() def __getitem__(self, item): - indices = (offset, stop, step) = item.indices(self.count()) + try: + indices = (offset, stop, step) = item.indices(self.count()) + except AttributeError: + # it's not a slice - make it one + return self[item : item + 1][0] items = [] total_len = stop - offset for qs in self.querysets: diff --git a/apps/catalogue/views.py b/apps/catalogue/views.py index 90fe22fd0..3a3283229 100644 --- a/apps/catalogue/views.py +++ b/apps/catalogue/views.py @@ -185,11 +185,8 @@ def tagged_object_list(request, tags=''): context_instance=RequestContext(request)) -def book_fragments(request, book, theme_slug): - kwargs = models.Book.split_urlid(book) - if kwargs is None: - raise Http404 - book = get_object_or_404(models.Book, **kwargs) +def book_fragments(request, slug, theme_slug): + book = get_object_or_404(models.Book, slug=slug) book_tag = book.book_tag() theme = get_object_or_404(models.Tag, slug=theme_slug, category='theme') @@ -199,12 +196,9 @@ def book_fragments(request, book, theme_slug): context_instance=RequestContext(request)) -def book_detail(request, book): - kwargs = models.Book.split_urlid(book) - if kwargs is None: - raise Http404 +def book_detail(request, slug): try: - book = models.Book.objects.get(**kwargs) + book = models.Book.objects.get(slug=slug) except models.Book.DoesNotExist: return pdcounter_views.book_stub_detail(request, kwargs['slug']) @@ -233,11 +227,8 @@ def book_detail(request, book): context_instance=RequestContext(request)) -def player(request, book): - kwargs = models.Book.split_urlid(book) - if kwargs is None: - raise Http404 - book = get_object_or_404(models.Book, **kwargs) +def player(request, slug): + book = get_object_or_404(models.Book, slug=slug) if not book.has_media('mp3'): raise Http404 @@ -274,11 +265,8 @@ def player(request, book): context_instance=RequestContext(request)) -def book_text(request, book): - kwargs = models.Book.split_fileid(book) - if kwargs is None: - raise Http404 - book = get_object_or_404(models.Book, **kwargs) +def book_text(request, slug): + book = get_object_or_404(models.Book, slug=slug) if not book.has_html_file(): raise Http404 @@ -513,14 +501,11 @@ def user_shelves(request): context_instance=RequestContext(request)) @cache.never_cache -def book_sets(request, book): +def book_sets(request, slug): if not request.user.is_authenticated(): return HttpResponse(_('

    To maintain your shelves you need to be logged in.

    ')) - kwargs = models.Book.split_urlid(book) - if kwargs is None: - raise Http404 - book = get_object_or_404(models.Book, **kwargs) + book = get_object_or_404(models.Book, slug=slug) user_sets = models.Tag.objects.filter(category='set', user=request.user) book_sets = book.tags.filter(category='set', user=request.user) @@ -553,11 +538,8 @@ def book_sets(request, book): @login_required @require_POST @cache.never_cache -def remove_from_shelf(request, shelf, book): - kwargs = models.Book.split_urlid(book) - if kwargs is None: - raise Http404 - book = get_object_or_404(models.Book, **kwargs) +def remove_from_shelf(request, shelf, slug): + book = get_object_or_404(models.Book, slug=slug) shelf = get_object_or_404(models.Tag, slug=shelf, category='set', user=request.user) @@ -608,11 +590,10 @@ def download_shelf(request, slug): archive = zipfile.ZipFile(temp, 'w') for book in collect_books(models.Book.tagged.with_all(shelf)): - fileid = book.fileid() for ebook_format in models.Book.ebook_formats: if ebook_format in formats and book.has_media(ebook_format): filename = book.get_media(ebook_format).path - archive.write(filename, str('%s.%s' % (fileid, ebook_format))) + archive.write(filename, str('%s.%s' % (book.slug, ebook_format))) archive.close() response = HttpResponse(content_type='application/zip', mimetype='application/x-zip-compressed') @@ -711,25 +692,20 @@ def tag_info(request, id): return HttpResponse(tag.description) -def download_zip(request, format, book=None): - kwargs = models.Book.split_fileid(book) - +def download_zip(request, format, slug=None): url = None if format in models.Book.ebook_formats: url = models.Book.zip_format(format) - elif format in ('mp3', 'ogg') and kwargs is not None: - book = get_object_or_404(models.Book, **kwargs) + elif format in ('mp3', 'ogg') and slug is not None: + book = get_object_or_404(models.Book, slug=slug) url = book.zip_audiobooks(format) else: raise Http404('No format specified for zip package') return HttpResponseRedirect(urlquote_plus(settings.MEDIA_URL + url, safe='/?=')) -def download_custom_pdf(request, book_fileid): - kwargs = models.Book.split_fileid(book_fileid) - if kwargs is None: - raise Http404 - book = get_object_or_404(models.Book, **kwargs) +def download_custom_pdf(request, slug): + book = get_object_or_404(models.Book, slug=slug) if request.method == 'GET': form = forms.CustomPDFForm(request.GET) @@ -740,7 +716,7 @@ def download_custom_pdf(request, book_fileid): 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_fileid), file_path=pdf_file, mimetype="application/pdf") + return AttachmentHttpResponse(file_name=("%s.pdf" % book.slug), file_path=pdf_file, mimetype="application/pdf") else: raise Http404(_('Incorrect customization options for PDF')) else: diff --git a/apps/dictionary/templates/dictionary/note_list.html b/apps/dictionary/templates/dictionary/note_list.html index cd88c17bc..aad0ddfbe 100755 --- a/apps/dictionary/templates/dictionary/note_list.html +++ b/apps/dictionary/templates/dictionary/note_list.html @@ -42,7 +42,7 @@ {% endfor %} diff --git a/apps/lesmianator/urls.py b/apps/lesmianator/urls.py index eeba6103f..de48644f1 100644 --- a/apps/lesmianator/urls.py +++ b/apps/lesmianator/urls.py @@ -8,7 +8,7 @@ from catalogue.models import Book urlpatterns = patterns('lesmianator.views', url(r'^$', 'main_page', name='lesmianator'), url(r'^wiersz/$', 'new_poem', name='new_poem'), - url(r'^lektura/(?P%s)/$' % Book.URLID_RE, 'poem_from_book', name='poem_from_book'), + url(r'^lektura/(?P[a-z0-9-]+)/$', 'poem_from_book', name='poem_from_book'), url(r'^polka/(?P[a-zA-Z0-9-]+)/$', 'poem_from_set', name='poem_from_set'), url(r'^wiersz/(?P[a-zA-Z0-9-]+)/$', 'get_poem', name='get_poem'), ) diff --git a/apps/lesmianator/views.py b/apps/lesmianator/views.py index 28cb32a87..e86febe91 100644 --- a/apps/lesmianator/views.py +++ b/apps/lesmianator/views.py @@ -33,11 +33,8 @@ def new_poem(request): @cache.never_cache -def poem_from_book(request, book): - kwargs = Book.split_urlid(book) - if kwargs is None: - raise Http404 - book = get_object_or_404(Book, **kwargs) +def poem_from_book(request, slug): + book = get_object_or_404(Book, slug=slug) user = request.user if request.user.is_authenticated() else None text = Poem.write(Continuations.get(book)) p = Poem(slug=get_random_hash(text), text=text, created_by=user) diff --git a/apps/picture/models.py b/apps/picture/models.py index 862c172c2..0217d8bad 100644 --- a/apps/picture/models.py +++ b/apps/picture/models.py @@ -44,9 +44,6 @@ class Picture(models.Model): verbose_name = _('picture') verbose_name_plural = _('pictures') - URLID_RE = r'[a-z0-9-]+' - FILEID_RE = r'[a-z0-9-]+' - def save(self, force_insert=False, force_update=False, reset_short_html=True, **kwargs): from sortify import sortify @@ -64,10 +61,7 @@ class Picture(models.Model): @permalink def get_absolute_url(self): - return ('picture.views.picture_detail', [self.urlid()]) - - def urlid(self): - return self.slug + return ('picture.views.picture_detail', [self.slug]) @classmethod def from_xml_file(cls, xml_file, image_file=None, overwrite=False): diff --git a/lib/librarian b/lib/librarian index 5fed78856..a34b95aa7 160000 --- a/lib/librarian +++ b/lib/librarian @@ -1 +1 @@ -Subproject commit 5fed78856949474a36bc5e268517775a9a802e27 +Subproject commit a34b95aa7ba5fd4838541d1cdcd28358fb808062 diff --git a/wolnelektury/settings.py b/wolnelektury/settings.py index bc63aa3fa..20a3fc653 100644 --- a/wolnelektury/settings.py +++ b/wolnelektury/settings.py @@ -156,7 +156,8 @@ INSTALLED_APPS = [ ] #CACHE_BACKEND = 'locmem:///?max_entries=3000' -#CACHE_BACKEND = 'memcached://127.0.0.1:11211/' +CACHE_BACKEND = 'memcached://127.0.0.1:11211/' +#CACHE_BACKEND = None CACHE_MIDDLEWARE_ANONYMOUS_ONLY=True # CSS and JavaScript file groups diff --git a/wolnelektury/templates/catalogue/book_detail.html b/wolnelektury/templates/catalogue/book_detail.html index 4b4ee2f18..62b16f242 100644 --- a/wolnelektury/templates/catalogue/book_detail.html +++ b/wolnelektury/templates/catalogue/book_detail.html @@ -36,11 +36,11 @@

    {% endif %}
    -

    {% trans "Put a book" %} {% trans "on the shelf!" %}

    +

    {% trans "Put a book" %} {% trans "on the shelf!" %}

    {% if book.has_html_file %} - {% trans "Read online" %} + {% trans "Read online" %} {% endif %}
    {% if book.pdf_file %} @@ -60,83 +60,12 @@
    {% trans "Dowload customized PDF" %}. {% endif %}
    - {% if book.has_mp3_file or book.has_ogg_file or book.has_daisy_file %} -

    - {% trans "Audiobooks" %}: - - {% if book.has_mp3_file %}MP3{% endif %} - {% if book.has_ogg_file %}Ogg Vorbis{% endif %} - {% if book.has_daisy_file %}DAISY{% endif %} - -

    -
    - Speaker icon - {% if book.has_mp3_file %} -
      - {% for media in book.get_mp3 %} -
    • - {{ media.name }}
      - {% trans "Artist" %}: {{ media.get_extra_info_value.artist_name }}
      - {% trans "Director"%}: {{ media.get_extra_info_value.director_name }}
      - - - - - - -
    • - {% endfor %} -
    - {% endif %} - - {% if book.has_ogg_file %} - - {% endif %} - {% if book.has_daisy_file %} - - {% endif %} -
    - {% if projects|length > 1 %} -

    {% trans "Audiobooks were prepared as a part of the projects:" %}

    -
      - {% for cs, fb in projects %} -
    • - {% if fb %} - {% blocktrans %}{{ cs }}, funded by {{ fb }}{% endblocktrans %} - {% else %} - {{ cs }} - {% endif %} -
    • - {% endfor %} -
    - {% else %} -

    - {% with projects.0.0 as cs %} - {% with projects.0.1 as fb %} - {% if fb %} - {% blocktrans %}Audiobooks were prepared as a part of the {{ cs }} project funded by {{ fb }}.{% endblocktrans %} - {% else %} - {% blocktrans %}Audiobooks were prepared as a part of the {{ cs }} project.{% endblocktrans %} - {% endif %} - {% endwith %} - {% endwith %} -

    - {% endif %} - {% endif %}
    @@ -199,13 +128,13 @@ {% endif %}

    {% trans "View XML source" %}

    -

    Miksuj ten utwór

    +

    Miksuj ten utwór

    {% trans "Work's themes " %}

    diff --git a/wolnelektury/templates/catalogue/book_sets.html b/wolnelektury/templates/catalogue/book_sets.html index f151650d1..1eee61d21 100644 --- a/wolnelektury/templates/catalogue/book_sets.html +++ b/wolnelektury/templates/catalogue/book_sets.html @@ -9,7 +9,7 @@ {% if not user.tag_set.count %}

    {% trans "You do not have any shelves. You can create one below, if you want to."%}

    {% else %} -
    +
    1. {{ form.set_ids }}
    2. diff --git a/wolnelektury/templates/catalogue/book_short.html b/wolnelektury/templates/catalogue/book_short.html index 5557126da..9800a0cae 100644 --- a/wolnelektury/templates/catalogue/book_short.html +++ b/wolnelektury/templates/catalogue/book_short.html @@ -49,7 +49,7 @@
      • {% if book.html_file %} - {% trans "Read online" %} + {% trans "Read online" %} {% endif %}
      • diff --git a/wolnelektury/templates/catalogue/player.html b/wolnelektury/templates/catalogue/player.html index 95fcb5e62..414bf0eeb 100755 --- a/wolnelektury/templates/catalogue/player.html +++ b/wolnelektury/templates/catalogue/player.html @@ -77,8 +77,8 @@

        {% trans "Download as" %}: - MP3{% if have_oggs %}, - Ogg Vorbis{% endif %}. + MP3{% if have_oggs %}, + Ogg Vorbis{% endif %}.

        diff --git a/wolnelektury/templates/catalogue/tagged_object_list.html b/wolnelektury/templates/catalogue/tagged_object_list.html index fce00dcbd..7c33a3740 100644 --- a/wolnelektury/templates/catalogue/tagged_object_list.html +++ b/wolnelektury/templates/catalogue/tagged_object_list.html @@ -134,7 +134,7 @@ {% for book in object_list %}
      • {% if user_is_owner %} - {% trans "Delete" %} + {% trans "Delete" %} {% endif %} {{ book.short_html }}
      • {% endfor %} diff --git a/wolnelektury/templates/lesmianator/poem.html b/wolnelektury/templates/lesmianator/poem.html index 23e943759..03d1e9d03 100644 --- a/wolnelektury/templates/lesmianator/poem.html +++ b/wolnelektury/templates/lesmianator/poem.html @@ -15,7 +15,7 @@ {# shelf or global mixing #} Twórzże się jeszcze raz! {% else %}{% if book %} - Twórzże się jeszcze raz! + Twórzże się jeszcze raz! {% endif %}{% endif %} Wolne Lektury przepuszczone przez mikser.
    @@ -40,7 +40,7 @@ {% if book %}

    Tekst powstał przez zmiksowanie utworu {{ book.title }}.
    - Zmiksuj go ponownie + Zmiksuj go ponownie albo zobacz, co jeszcze możesz zamieszać.

    {% else %}{% if books %}

    Tekst powstał przez zmiksowanie utworów:

    -- 2.20.1 From d90edc4826d6c3d3a40f52fa5c88cf4b1741ab63 Mon Sep 17 00:00:00 2001 From: Radek Czajka Date: Fri, 30 Dec 2011 12:55:52 +0100 Subject: [PATCH 08/16] search fixes --- apps/search/index.py | 2 +- apps/search/management/commands/reindex.py | 16 ++++++++++++++++ lib/librarian | 2 +- requirements.txt | 2 +- 4 files changed, 19 insertions(+), 3 deletions(-) create mode 100755 apps/search/management/commands/reindex.py diff --git a/apps/search/index.py b/apps/search/index.py index 10069e4e4..6c7690146 100644 --- a/apps/search/index.py +++ b/apps/search/index.py @@ -280,7 +280,7 @@ class Index(IndexStore): return def fix_format(text): - return re.sub("/$", "", text, flags=re.M) + return re.sub("(?m)/$", "", text) def add_part(snippets, **fields): doc = self.create_book_doc(book) diff --git a/apps/search/management/commands/reindex.py b/apps/search/management/commands/reindex.py new file mode 100755 index 000000000..9639a5ae7 --- /dev/null +++ b/apps/search/management/commands/reindex.py @@ -0,0 +1,16 @@ +from django.core.management.base import BaseCommand + +class Command(BaseCommand): + help = 'Reindex everything.' + args = '' + + def handle(self, *args, **opts): + from catalogue.models import Book + import search + idx = search.ReusableIndex() + idx.open() + for b in Book.objects.all(): + print b.title + idx.index_book(b, None) + print 'Reindexing tags.' + idx.index_tags() diff --git a/lib/librarian b/lib/librarian index a34b95aa7..cbabbdaa7 160000 --- a/lib/librarian +++ b/lib/librarian @@ -1 +1 @@ -Subproject commit a34b95aa7ba5fd4838541d1cdcd28358fb808062 +Subproject commit cbabbdaa7ab6c6be8cd22b77860331444ddf6119 diff --git a/requirements.txt b/requirements.txt index ed684cc53..2b69d7f6f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -17,7 +17,7 @@ Feedparser>=4.1 # PIL PIL>=1.1.6 mutagen>=1.17 -sorl-thumbnail>=11.09<12 +sorl-thumbnail>=11.09,<12 # home-brewed & dependencies lxml>=2.2.2 -- 2.20.1 From 9771b4edcbb556927312f624de6263395f82e0ba Mon Sep 17 00:00:00 2001 From: Marcin Koziej Date: Fri, 30 Dec 2011 13:00:25 +0100 Subject: [PATCH 09/16] book detail page - now with media / custom pdf --- apps/catalogue/templatetags/catalogue_tags.py | 4 +++ apps/catalogue/urls.py | 3 +- apps/catalogue/views.py | 31 +++++++++++++------ wolnelektury/static/css/book_box.css | 31 ++++++++++++++++--- wolnelektury/static/css/dialogs.css | 8 +++++ .../templates/catalogue/book_wide.html | 19 +++++++++--- 6 files changed, 78 insertions(+), 18 deletions(-) diff --git a/apps/catalogue/templatetags/catalogue_tags.py b/apps/catalogue/templatetags/catalogue_tags.py index c5b904d40..df938a624 100644 --- a/apps/catalogue/templatetags/catalogue_tags.py +++ b/apps/catalogue/templatetags/catalogue_tags.py @@ -291,4 +291,8 @@ def book_wide(book): extra_info = book.get_extra_info_value() + has_media = {} + for media_format in ['mp3', 'ogg']: + has_media[media_format] = book.has_media(media_format) + return locals() diff --git a/apps/catalogue/urls.py b/apps/catalogue/urls.py index 4020592d2..dce1b8f10 100644 --- a/apps/catalogue/urls.py +++ b/apps/catalogue/urls.py @@ -6,6 +6,7 @@ from django.conf.urls.defaults import * from catalogue.feeds import AudiobookFeed from catalogue.models import Book from picture.models import Picture +from catalogue.views import CustomPDFFormView urlpatterns = patterns('picture.views', # pictures - currently pictures are coupled with catalogue, hence the url is here @@ -47,5 +48,5 @@ urlpatterns = patterns('picture.views', url(r'^audiobooki/(?Pmp3|ogg|daisy|all).xml$', AudiobookFeed(), name='audiobook_feed'), url(r'^custompdf/(?P%s).pdf' % Book.FILEID_RE, 'download_custom_pdf'), - + url(r'^custompdf$', CustomPDFFormView(), name='custom_pdf_form') ) diff --git a/apps/catalogue/views.py b/apps/catalogue/views.py index 90fe22fd0..d80e570bc 100644 --- a/apps/catalogue/views.py +++ b/apps/catalogue/views.py @@ -23,7 +23,8 @@ from django.utils import translation from django.utils.translation import ugettext as _ from django.views.generic.list_detail import object_list -from ajaxable.utils import LazyEncoder, JSONResponse +from ajaxable.utils import LazyEncoder, JSONResponse, AjaxableFormView + from catalogue import models from catalogue import forms from catalogue.utils import (split_tags, AttachmentHttpResponse, @@ -207,12 +208,12 @@ def book_detail(request, book): book = models.Book.objects.get(**kwargs) except models.Book.DoesNotExist: return pdcounter_views.book_stub_detail(request, kwargs['slug']) - + book_tag = book.book_tag() tags = list(book.tags.filter(~Q(category='set'))) categories = split_tags(tags) book_children = book.children.all().order_by('parent_number', 'sort_key') - + _book = book parents = [] while _book.parent: @@ -466,7 +467,7 @@ def search(request): context_instance=RequestContext(request)) else: form = PublishingSuggestForm(initial={"books": prefix + ", "}) - return render_to_response('catalogue/search_no_hits.html', + return render_to_response('catalogue/search_no_hits.html', {'tags':tag_list, 'prefix':prefix, "pubsuggest_form": form}, context_instance=RequestContext(request)) @@ -477,7 +478,7 @@ def tags_starting_with(request): if len(prefix) < 2: return HttpResponse('') tags_list = [] - result = "" + result = "" for tag in _tags_starting_with(prefix, request.user): if not tag.name in tags_list: result += "\n" + tag.name @@ -725,18 +726,18 @@ def download_zip(request, format, book=None): return HttpResponseRedirect(urlquote_plus(settings.MEDIA_URL + url, safe='/?=')) -def download_custom_pdf(request, book_fileid): +def download_custom_pdf(request, book_fileid, method='GET'): kwargs = models.Book.split_fileid(book_fileid) if kwargs is None: raise Http404 book = get_object_or_404(models.Book, **kwargs) - if request.method == 'GET': - form = forms.CustomPDFForm(request.GET) + 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() @@ -745,3 +746,15 @@ def download_custom_pdf(request, book_fileid): raise Http404(_('Incorrect customization options for PDF')) else: raise Http404(_('Bad method')) + + +class CustomPDFFormView(AjaxableFormView): + form_class = forms.CustomPDFForm + title = _('Download custom PDF') + submit = _('Download') + + def __call__(self, request): + if request.method == 'POST': + return download_custom_pdf(request, request.GET['book_id'], method='POST') + else: + return super(CustomPDFFormView, self).__call__(request) diff --git a/wolnelektury/static/css/book_box.css b/wolnelektury/static/css/book_box.css index 9b589dc52..17330083a 100755 --- a/wolnelektury/static/css/book_box.css +++ b/wolnelektury/static/css/book_box.css @@ -15,6 +15,7 @@ .book-wide-box { width: 98.5em; + margin-left: -0.1em; } .book-mini-box a, .book-box-inner { @@ -40,6 +41,10 @@ margin: .5em; } +.book-wide-box .book-box-inner { + height: 24.4em; +} + .book-mini-box img, .book-box img, .book-wide-box img { width: 13.9em; height: 19.3em; @@ -69,6 +74,11 @@ height: 17em; overflow: hidden; } + +.book-wide-box .book-box-body { + height: 21.8em; +} + .book-box-head { min-height: 7em; margin-top: 1.4em; @@ -130,6 +140,10 @@ font-size: 1.1em; } +.book-wide-box .book-box-tools { + margin-left: 14em; +} + .book-box-tools a.downarrow:before { content: "\2609"; font-family: WL-Nav; @@ -186,16 +200,25 @@ ul.book-box-tools { padding: 0.888em; } +ul.inline-items, ul.inline-items li { + margin: 0; + padding: 0; +} + +ul.inline-items li { + display: inline-block; +} + .book-wide-box #other-tools { float: left; width: 14.5em; - margin-left: 1.5em; + margin: 6em 0 0 1.5em; } - .book-wide-box #other-download { float: left; - width 22.5em; - margin: 0em 1.5em 0em 1.5em + width: 22.5em; + margin: 6em 1.5em 0em 1.5em } + diff --git a/wolnelektury/static/css/dialogs.css b/wolnelektury/static/css/dialogs.css index dc76e6cd8..b3876c39e 100755 --- a/wolnelektury/static/css/dialogs.css +++ b/wolnelektury/static/css/dialogs.css @@ -85,3 +85,11 @@ width: 21em; height: 3em; } + +#custom-pdf-window { + width: 24em; +} + +#custom-pdf-window label { + display: inline; +} \ No newline at end of file diff --git a/wolnelektury/templates/catalogue/book_wide.html b/wolnelektury/templates/catalogue/book_wide.html index 1cfde2e14..59abe0f0f 100644 --- a/wolnelektury/templates/catalogue/book_wide.html +++ b/wolnelektury/templates/catalogue/book_wide.html @@ -12,12 +12,12 @@

    {% trans "See" %}

    -

    {% trans "Download" %}

    -

    {% trans "Download all audiobooks for this book" %}

    +
      +
    • + {% if has_media.mp3 or has_media.ogg %} + {% trans "Download all audiobooks for this book" %}: + {% if has_media.mp3 %}MP3{% endif %}{% if has_media.mp4 and has_media.ogg %},{% endif %} + {% if has_media.ogg %}OGG{% endif %}. + {% endif %} +
    • +
    • + {% trans "Download a custom PDF" %} +
    • +
    {% endblock %} -- 2.20.1 From b07520a11332374de645ed22c74959afa8a4b7ed Mon Sep 17 00:00:00 2001 From: Radek Czajka Date: Fri, 30 Dec 2011 13:19:18 +0100 Subject: [PATCH 10/16] ajaxable dialogs fixes, some layout tweaking --- apps/ajaxable/templates/ajaxable/form.html | 1 + apps/ajaxable/utils.py | 10 ++-- .../templates/infopages/infopage.html | 12 +++-- apps/infopages/views.py | 2 - wolnelektury/settings.py | 18 ++++---- wolnelektury/static/css/base.css | 12 +++-- wolnelektury/static/css/catalogue.css | 46 +++++++++++++++++++ wolnelektury/static/css/dialogs.css | 21 ++++++--- wolnelektury/static/js/base.js | 9 ++++ wolnelektury/static/js/dialogs.js | 12 ++++- wolnelektury/templates/404.html | 36 ++++++--------- wolnelektury/templates/base.html | 4 +- .../templates/catalogue/book_list.html | 12 +++-- .../templates/catalogue/book_text.html | 2 +- .../templates/catalogue/catalogue.html | 2 + .../templates/lesmianator/lesmianator.html | 8 +++- wolnelektury/templates/main_page.html | 3 +- wolnelektury/templates/publish_plan.html | 2 +- wolnelektury/views.py | 4 +- 19 files changed, 150 insertions(+), 66 deletions(-) diff --git a/apps/ajaxable/templates/ajaxable/form.html b/apps/ajaxable/templates/ajaxable/form.html index d8f00361f..ba79e4b69 100755 --- a/apps/ajaxable/templates/ajaxable/form.html +++ b/apps/ajaxable/templates/ajaxable/form.html @@ -3,6 +3,7 @@
      +
      {{ form.as_ul }}
    diff --git a/apps/ajaxable/utils.py b/apps/ajaxable/utils.py index 14b5dfc7a..a6faafa05 100755 --- a/apps/ajaxable/utils.py +++ b/apps/ajaxable/utils.py @@ -35,9 +35,8 @@ class AjaxableFormView(object): form_class = None # override to customize form look template = "ajaxable/form.html" - # set to redirect after succesful ajax-less post submit = _('Send') - redirect = None + title = '' success_message = '' formname = "form" @@ -51,10 +50,11 @@ class AjaxableFormView(object): if form.is_valid(): self.success(form, request) redirect = request.GET.get('next') - if not ajax and redirect is not None: + if not ajax and redirect: return HttpResponseRedirect(urlquote_plus( - redirect, safe='/?=')) - response_data = {'success': True, 'message': self.success_message} + redirect, safe='/?=')) + response_data = {'success': True, + 'message': self.success_message, 'redirect': redirect} else: response_data = {'success': False, 'errors': form.errors} if ajax: diff --git a/apps/infopages/templates/infopages/infopage.html b/apps/infopages/templates/infopages/infopage.html index dc9efe1aa..db2d49b29 100755 --- a/apps/infopages/templates/infopages/infopage.html +++ b/apps/infopages/templates/infopages/infopage.html @@ -10,11 +10,15 @@

    {{ page.title }}

    {% autoescape off %} -
    - {{ left_column }} +
    +
    + {{ left_column }} +
    -
    - {{ right_column }} +
    +
    + {{ right_column }} +
    {% endautoescape %} {% endblock %} diff --git a/apps/infopages/views.py b/apps/infopages/views.py index d457653d1..e1e745148 100644 --- a/apps/infopages/views.py +++ b/apps/infopages/views.py @@ -9,8 +9,6 @@ from infopages.models import InfoPage def infopage(request, slug): - page = InfoPage.objects.get(slug=slug) - page = get_object_or_404(InfoPage, slug=slug) rc = RequestContext(request) left_column = Template(page.left_column).render(rc) diff --git a/wolnelektury/settings.py b/wolnelektury/settings.py index 55c845e60..ed48332e6 100644 --- a/wolnelektury/settings.py +++ b/wolnelektury/settings.py @@ -232,16 +232,14 @@ COMPRESS_JS = { ], 'output_filename': 'js/player.min?.js', }, - #~ 'book': { - #~ 'source_filenames': ('js/jquery.eventdelegation.js', 'js/jquery.scrollto.js', 'js/jquery.highlightfade.js', 'js/book.js',), - #~ 'source_filenames': [], - #~ 'output_filename': 'js/book?.min.js', - #~ }, - #~ 'book_ie': { - #~ 'source_filenames': ('js/ierange-m2.js',), - #~ 'source_filenames': [], - #~ 'output_filename': 'js/book_ie?.min.js', - #~ } + 'book': { + 'source_filenames': ('js/jquery.eventdelegation.js', 'js/jquery.scrollto.js', 'js/jquery.highlightfade.js', 'js/book.js',), + 'output_filename': 'js/book?.min.js', + }, + 'book_ie': { + 'source_filenames': ('js/ierange-m2.js',), + 'output_filename': 'js/book_ie?.min.js', + } } diff --git a/wolnelektury/static/css/base.css b/wolnelektury/static/css/base.css index 7c3b682a6..fde0115a5 100755 --- a/wolnelektury/static/css/base.css +++ b/wolnelektury/static/css/base.css @@ -43,6 +43,10 @@ h1 { float:right; width: 48em; } +.normal-text { + font-size: 1.3em; + line-height: 1.3em; +} h2 { margin: 0; @@ -60,7 +64,7 @@ h2 { clear: both; } -#header-content, div#main-content, div#half-header-content { +#header-content, div#main-content, div#half-header-content, #footer-content { width: 97.5em; margin: auto; } @@ -147,6 +151,8 @@ a.cite { #footer { color: #777; - border-top: 1px solid #ddd; - margin-top: 2.5em; + eborder-top: 1px solid #ddd; + margin-top: 5em; + padding-top:3em; + background: #fff; } diff --git a/wolnelektury/static/css/catalogue.css b/wolnelektury/static/css/catalogue.css index c971ca80b..b75429992 100755 --- a/wolnelektury/static/css/catalogue.css +++ b/wolnelektury/static/css/catalogue.css @@ -9,3 +9,49 @@ margin: 0; padding: 0; } + + +/* listing of all books */ +#book-list { + padding-left: 50px; +} +#book-list-nav { + position: absolute; + right: 50px; + width: 200px; + border-left: 1px #cfcfcf solid; + padding: 10px; + font-size: 1.2em; +} + +#book-list-nav ul { + list-style-type: none; + margin: 5px; + padding: 0; +} + +.book-list-show-index { + display: none; + margin: 0; + padding: 0; +} + + +#book-a-list #book-list ol { + padding-left: 1em; + margin: 0.3em 0 1.2em 0; + list-style: none; +} + +#book-a-list #book-list h2 a { + color: black; +} + +#book-list-up { + position: fixed; + bottom: 50px; + right: 50px; + border-left: 1px #cfcfcf solid; + padding: 10px; + background-color: white; +} \ No newline at end of file diff --git a/wolnelektury/static/css/dialogs.css b/wolnelektury/static/css/dialogs.css index dc76e6cd8..05b74dcc6 100755 --- a/wolnelektury/static/css/dialogs.css +++ b/wolnelektury/static/css/dialogs.css @@ -1,7 +1,11 @@ +.cuteform { + font-size: 1.1em; +} .cuteform ol, .cuteform ul { padding: 0; margin: 0; list-style: none; + font-style: 1.1em; } .cuteform ol li, .cuteform ul li { @@ -22,6 +26,9 @@ color: #BF3024; display: block; } +.cuteform .errorlist { + color: #BF3024; +} .jqmOverlay { background-color: #000; } @@ -36,6 +43,7 @@ } .dialog-window div.header { + font-size: 1.1em; width: 4em; background-color: #FFF; border-right: 0.3em solid #DDD; @@ -60,28 +68,29 @@ font-size: 1.2em; } +.dialog-window textarea, .dialog-window input { + width: 100%; +} #login-window { - width: 24em; + width: 26em; } #register-window { - width: 24em; + width: 26em; } #suggest-window { - width: 24em; + width: 26em; } #suggest-window textarea { - width: 19em; height: 6em; } #suggest-publishing-window { - width: 26em; + width: 29em; } #suggest-publishing-window textarea { - width: 21em; height: 3em; } diff --git a/wolnelektury/static/js/base.js b/wolnelektury/static/js/base.js index 028ab33f4..51107fe8b 100755 --- a/wolnelektury/static/js/base.js +++ b/wolnelektury/static/js/base.js @@ -17,6 +17,15 @@ $('.open-player').click(function(event) { }); + $('.book-list-index').click(function(){ + $('.book-list-show-index').hide('slow'); + if($(this).parent().next('ul:not(:hidden)').length == 0){ + $(this).parent().next('ul').toggle('slow'); + } + return false; + }); + + }); })(jQuery) diff --git a/wolnelektury/static/js/dialogs.js b/wolnelektury/static/js/dialogs.js index 0793a7ffd..bf9d94b54 100755 --- a/wolnelektury/static/js/dialogs.js +++ b/wolnelektury/static/js/dialogs.js @@ -27,14 +27,22 @@ hash.w.show(); }, onLoad: function(hash) { - $('form', hash.w).each(function() {this.action += '?ajax=1';}); + $('form', hash.w).each(function() { + if (this.action.search('[\\?&]ajax=1') != -1) + return; + if (this.action.search('\\?') != -1) + this.action += '&ajax=1'; + else this.action += '?ajax=1'; + }); $('form', hash.w).ajaxForm({ dataType: 'json', target: $('.target', $window), success: function(response) { if (response.success) { $('.target', $window).text(response.message); - setTimeout(function() { $window.jqmHide() }, 1000) + setTimeout(function() { $window.jqmHide() }, 1000); + if (response.redirect) + window.location = response.redirect; } else { $('.error', $window).remove(); diff --git a/wolnelektury/templates/404.html b/wolnelektury/templates/404.html index 50a56ea7e..8083d2865 100644 --- a/wolnelektury/templates/404.html +++ b/wolnelektury/templates/404.html @@ -1,27 +1,19 @@ +{% extends "base.html" %} {% load i18n %} - - - -404 - {% trans "Page does not exist" %} - WolneLektury.pl - - - - - - - - -

    {% trans "Page does not exist" %}

    + + +{% block titleextra %}404 - {% trans "Page does not exist" %}{% endblock %} + + +{% block body %} + +

    {% trans "Page not found" %}

    + +

    {% trans "We are sorry, but this page does not exist. Please check if you entered correct address or go to "%} {% trans "main page" %}.

    - - - - \ No newline at end of file + + +{% endblock body %} diff --git a/wolnelektury/templates/base.html b/wolnelektury/templates/base.html index 48f13adb2..04330f232 100644 --- a/wolnelektury/templates/base.html +++ b/wolnelektury/templates/base.html @@ -131,9 +131,11 @@
    +
    {# end main-content #} -
    {# end main-content #} {# template #} diff --git a/wolnelektury/templates/catalogue/book_list.html b/wolnelektury/templates/catalogue/book_list.html index b4336d32b..318f1b446 100644 --- a/wolnelektury/templates/catalogue/book_list.html +++ b/wolnelektury/templates/catalogue/book_list.html @@ -9,9 +9,15 @@ {% block body %}

    {% block book_list_header %}{% trans "Listing of all works" %}{% endblock %}

    -
    {% block book_list_info %}{% endblock %}
    +
    + {% block book_list_info %}{% endblock %} +
    + +
    + -
    + +
    {% trans "Table of Content" %} {% for index, authors in books_nav.items %}
      @@ -24,7 +30,7 @@
    {% endfor %}
    -
    +
    {% block book_list %} {% book_tree orphans books_by_parent %} {% for author, group in books_by_author.items %} diff --git a/wolnelektury/templates/catalogue/book_text.html b/wolnelektury/templates/catalogue/book_text.html index 935cdf392..c1331653c 100644 --- a/wolnelektury/templates/catalogue/book_text.html +++ b/wolnelektury/templates/catalogue/book_text.html @@ -8,7 +8,7 @@ {% block title %}WolneLektury.pl{% endblock %} {% compressed_css "book" %} - {% compressed_js "jquery" %} + {% compressed_js "book" %}