Merge branch 'dev'
authorJan Szejko <jan.szejko@gmail.com>
Wed, 13 Apr 2016 08:17:15 +0000 (10:17 +0200)
committerJan Szejko <jan.szejko@gmail.com>
Wed, 13 Apr 2016 08:50:34 +0000 (10:50 +0200)
Conflicts:
src/api/management/commands/mobileinit.py
src/catalogue/templates/catalogue/inline_tag_list.html
src/catalogue/templates/catalogue/plain_list.html
src/catalogue/views.py
src/wolnelektury/static/scss/main/book_box.scss
src/wolnelektury/static/scss/main/header.scss
src/wolnelektury/templates/main_page.html
src/wolnelektury/templates/superbase.html

62 files changed:
fabfile.py
src/api/management/commands/mobileinit.py
src/catalogue/helpers.py
src/catalogue/management/commands/update_popularity.py [new file with mode: 0644]
src/catalogue/migrations/0010_bookpopularity.py [new file with mode: 0644]
src/catalogue/models/__init__.py
src/catalogue/models/book.py
src/catalogue/models/collection.py
src/catalogue/models/tag.py
src/catalogue/templates/catalogue/audiobook_list.html [deleted file]
src/catalogue/templates/catalogue/book_wide.html
src/catalogue/templates/catalogue/inline_tag_list.html
src/catalogue/templates/catalogue/plain_list.html
src/catalogue/templates/catalogue/snippets/jplayer.html
src/catalogue/templates/catalogue/tag_list.html
src/catalogue/templates/catalogue/tagged_object_list.html
src/catalogue/templatetags/catalogue_tags.py
src/catalogue/urls.py
src/catalogue/views.py
src/infopages/views.py
src/newtagging/views.py
src/pdcounter/views.py
src/picture/models.py
src/picture/templates/picture/picture_list_thumb.html
src/picture/templates/picture/picture_wide.html
src/picture/views.py
src/reporting/views.py
src/social/utils.py
src/social/views.py
src/waiter/views.py
src/wolnelektury/settings/custom.py
src/wolnelektury/static/fonts/cousine/v10/0IpceuvDvCegpU9Mz8MQ_g.woff2 [new file with mode: 0644]
src/wolnelektury/static/fonts/cousine/v10/0VdvlOBfHobA4eW-NVYk-VtXRa8TVwTICgirnJhmVJw.woff2 [new file with mode: 0644]
src/wolnelektury/static/fonts/cousine/v10/4OFqQVT4CccEiwAQMKv0eiEAvth_LlrfE80CYdSH47w.woff2 [new file with mode: 0644]
src/wolnelektury/static/fonts/cousine/v10/Bt5Lz7Saa5a5RtsafP9xmfY6323mHUZFJMgTvxaG2iE.woff2 [new file with mode: 0644]
src/wolnelektury/static/fonts/cousine/v10/IYQIfrNvkAhlEkaWqzgTm_Y6323mHUZFJMgTvxaG2iE.woff2 [new file with mode: 0644]
src/wolnelektury/static/fonts/cousine/v10/JHq2r7PZMuzGPtEvdlprZCEAvth_LlrfE80CYdSH47w.woff2 [new file with mode: 0644]
src/wolnelektury/static/fonts/cousine/v10/XkYjaL8YjqL9qW8o0T14ufk_vArhqVIZ0nv9q090hN8.woff2 [new file with mode: 0644]
src/wolnelektury/static/fonts/cousine/v10/aWxnkwXsEJCSg-HKkPTCkBJtnKITppOI_IvcXXDNrsc.woff2 [new file with mode: 0644]
src/wolnelektury/static/fonts/cousine/v10/auteuWVL5GjOhekECAkC_SEAvth_LlrfE80CYdSH47w.woff2 [new file with mode: 0644]
src/wolnelektury/static/fonts/cousine/v10/bS4Fjp2fQa3pRrzTYB6CXhJtnKITppOI_IvcXXDNrsc.woff2 [new file with mode: 0644]
src/wolnelektury/static/fonts/cousine/v10/qjsoqLzZoDyy_opKVvy-uvY6323mHUZFJMgTvxaG2iE.woff2 [new file with mode: 0644]
src/wolnelektury/static/fonts/cousine/v10/svY-0AsqnhB5pNHScPRpHhJtnKITppOI_IvcXXDNrsc.woff2 [new file with mode: 0644]
src/wolnelektury/static/fonts/cousine/v10/y_AZ5Sz-FwL1lux2xLSTZT0LW-43aMEzIO6XUTLjad8.woff2 [new file with mode: 0644]
src/wolnelektury/static/fonts/cousine/v10/y_AZ5Sz-FwL1lux2xLSTZZX5f-9o1vgP2EXwfjgl7AY.woff2 [new file with mode: 0644]
src/wolnelektury/static/fonts/cousine/v10/y_AZ5Sz-FwL1lux2xLSTZa-j2U0lmluP9RWlSytm3ho.woff2 [new file with mode: 0644]
src/wolnelektury/static/fonts/cousine/v10/y_AZ5Sz-FwL1lux2xLSTZegdm0LZdjqr5-oayXSOefg.woff2 [new file with mode: 0644]
src/wolnelektury/static/img/read-white.png [new file with mode: 0644]
src/wolnelektury/static/img/read-white.svg [new file with mode: 0644]
src/wolnelektury/static/img/wiatrak.jpg [new file with mode: 0644]
src/wolnelektury/static/img/wiatrak.png [deleted file]
src/wolnelektury/static/js/base.js
src/wolnelektury/static/scss/main.scss
src/wolnelektury/static/scss/main/base.scss
src/wolnelektury/static/scss/main/book_box.scss
src/wolnelektury/static/scss/main/fonts.scss [new file with mode: 0644]
src/wolnelektury/static/scss/main/header.scss
src/wolnelektury/static/scss/main/menu.scss
src/wolnelektury/static/scss/tools.scss
src/wolnelektury/templates/main_page.html
src/wolnelektury/templates/superbase.html
src/wolnelektury/utils.py

index ff9a4b4..b6f3bbb 100644 (file)
@@ -10,18 +10,26 @@ except ImportError:
 env.project_name = 'wolnelektury'
 
 
-def update_counters():
-    print '>>> updating counters'
-    require('app_path', 'project_name')
-    with cd(get_django_root_path('current')):
-        run('%(ve)s/bin/python manage.py update_counters' % env, pty=True)
+class ManageTask(Task):
+    def __init__(self, name, params='', **kwargs):
+        super(ManageTask, self).__init__(**kwargs)
+        self.name = name
+        self.params = params
 
+    def run(self):
+        require('app_path', 'project_name')
+        with cd(get_django_root_path('current')):
+            run('source %(ve)s/bin/activate && python manage.py %(task)s %(params)s' % {
+                've': env.ve,
+                'task': self.name,
+                'params': self.params,
+            }, pty=True)
 
-def compile_messages():
-    print '>>> compiling messages'
-    require('app_path', 'project_name')
-    with cd(get_django_root_path('current')):
-        run('source %(ve)s/bin/activate && python manage.py localepack -c' % env, pty=True)
+
+class Memcached(Service):
+    def run(self):
+        print '>>> memcached: restart'
+        sudo('service memcached restart', shell=False)
 
 
 @task
@@ -32,12 +40,13 @@ def production():
     env.django_root_path = 'src'
     env.requirements_file = 'requirements/requirements.txt'
     env.pre_collectstatic = [
-        update_counters,
-        compile_messages,
+        ManageTask('update_counters'),
+        ManageTask('localepack', '-c'),
     ]
     env.services = [
         Supervisord('wolnelektury'),
         Supervisord('wolnelektury.celery'),
+        Memcached(),
     ]
 
 
@@ -50,8 +59,8 @@ def beta():
     env.django_root_path = 'src'
     env.requirements_file = 'requirements/requirements.txt'
     env.pre_collectstatic = [
-        update_counters,
-        compile_messages,
+        ManageTask('update_counters'),
+        ManageTask('localepack', '-c'),
     ]
     env.services = [
         Supervisord('beta'),
index ccbff3e..ccf27bc 100755 (executable)
@@ -97,7 +97,7 @@ CREATE TABLE state (last_checked INTEGER);
 """
 
     db.executescript(schema)
-    db.execute("INSERT INTO state VALUES (:last_checked)", locals())
+    db.execute("INSERT INTO state VALUES (:last_checked)", {'last_checked': last_checked})
     return db
 
 
@@ -133,29 +133,30 @@ categories = {'author': 'autor',
 
 
 def add_book(db, book):
-    title = book.title
     if book.html_file:
         html_file = book.html_file.url
         html_file_size = book.html_file.size
     else:
         html_file = html_file_size = None
-    if book.cover:
-        cover = book.cover.url
-    else:
-        cover = None
-    parent = book.parent_id
-    parent_number = book.parent_number
-    sort_key = book.sort_key
-    size_str = pretty_size(html_file_size)
-    authors = book.author_unicode()
-    db.execute(book_sql, locals())
+    db.execute(book_sql, {
+        'title': book.title,
+        'cover': book.cover.url if book.cover else None,
+        'html_file': html_file,
+        'html_file_size': html_file_size,
+        'parent': book.parent_id,
+        'parent_number': book.parent_number,
+        'sort_key': book.sort_key,
+        'size_str': pretty_size(html_file_size),
+        'authors': book.author_unicode(),
+    })
 
 
 def add_tag(db, tag):
-    category = categories[tag.category]
-    name = tag.name
-    sort_key = tag.sort_key
-
     books = Book.tagged_top_level([tag])
-    book_ids = ','.join(str(b.id) for b in books)
-    db.execute(tag_sql, locals())
+    book_ids = ','.join(str(book_id) for book_id in books.values_list('id', flat=True))
+    db.execute(tag_sql, {
+        'category': categories[tag.category],
+        'name': tag.name,
+        'sort_key': tag.sort_key,
+        'book_ids': book_ids,
+    })
index e476079..97c63d0 100644 (file)
@@ -3,6 +3,9 @@
 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
 #
 from django.conf import settings
+from django.contrib.contenttypes.models import ContentType
+from django.core.cache import cache
+
 from .models import Tag, Book
 from os.path import getmtime
 import cPickle
@@ -36,7 +39,6 @@ def get_top_level_related_tags(tags, categories=None):
 
     related = Tag.objects.filter(pk__in=related_ids)
 
-    # TODO: do we really need that?
     if categories is not None:
         related = related.filter(category__in=categories)
 
@@ -86,3 +88,16 @@ def update_counters():
 
     with open(settings.CATALOGUE_COUNTERS_FILE, 'w') as f:
         cPickle.dump(counters, f)
+
+
+def get_audiobook_tags():
+    audiobook_tag_ids = cache.get('audiobook_tags')
+    if audiobook_tag_ids is None:
+        books_with_audiobook = Book.objects.filter(media__type__in=('mp3', 'ogg'))\
+            .distinct().values_list('pk', flat=True)
+        audiobook_tag_ids = Tag.objects.filter(
+            items__content_type=ContentType.objects.get_for_model(Book),
+            items__object_id__in=list(books_with_audiobook)).distinct().values_list('pk', flat=True)
+        audiobook_tag_ids = list(audiobook_tag_ids)
+        cache.set('audiobook_tags', audiobook_tag_ids)
+    return audiobook_tag_ids
diff --git a/src/catalogue/management/commands/update_popularity.py b/src/catalogue/management/commands/update_popularity.py
new file mode 100644 (file)
index 0000000..df9dedf
--- /dev/null
@@ -0,0 +1,26 @@
+# -*- 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 __future__ import print_function, unicode_literals
+
+from django.core.management.base import BaseCommand
+from django.db.models import Count
+
+from catalogue.models import Book, BookPopularity
+
+
+class Command(BaseCommand):
+    help = 'Update popularity counters.'
+
+    def handle(self, **options):
+        BookPopularity.objects.all().delete()
+        books_with_popularity = Book.objects.filter(tag_relations__tag__category='set').only('id').distinct()\
+            .annotate(pop=Count('tag_relations__tag__user', distinct=True))
+        pop_list = []
+        for book in books_with_popularity:
+            pop_list.append(BookPopularity(book=book, count=book.pop))
+        books_without_popularity = Book.objects.exclude(tag_relations__tag__category='set')
+        for book in books_without_popularity:
+            pop_list.append(BookPopularity(book=book, count=0))
+        BookPopularity.objects.bulk_create(pop_list, batch_size=512)
diff --git a/src/catalogue/migrations/0010_bookpopularity.py b/src/catalogue/migrations/0010_bookpopularity.py
new file mode 100644 (file)
index 0000000..42fd6cb
--- /dev/null
@@ -0,0 +1,22 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('catalogue', '0009_auto_20160127_1019'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='BookPopularity',
+            fields=[
+                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+                ('count', models.IntegerField(default=0)),
+                ('book', models.OneToOneField(related_name='popularity', to='catalogue.Book')),
+            ],
+        ),
+    ]
index 73b5109..7aebc31 100644 (file)
@@ -5,6 +5,6 @@
 from catalogue.models.tag import Tag
 from catalogue.models.bookmedia import BookMedia
 from catalogue.models.fragment import Fragment
-from catalogue.models.book import Book
+from catalogue.models.book import Book, BookPopularity
 from catalogue.models.collection import Collection
 from catalogue.models.source import Source
index d3e2a77..0ed9716 100644 (file)
@@ -92,7 +92,7 @@ class Book(models.Model):
         pass
 
     class Meta:
-        ordering = ('sort_key',)
+        ordering = ('sort_key_author', 'sort_key')
         verbose_name = _('book')
         verbose_name_plural = _('books')
         app_label = 'catalogue'
@@ -576,6 +576,15 @@ class Book(models.Model):
         else:
             return None
 
+    def update_popularity(self):
+        count = self.tags.filter(category='set').values('user').order_by('user').distinct().count()
+        try:
+            pop = self.popularity
+            pop.count = count
+            pop.save()
+        except BookPopularity.DoesNotExist:
+            BookPopularity.objects.create(book=self, count=count)
+
 
 def add_file_fields():
     for format_ in Book.formats:
@@ -595,3 +604,8 @@ def add_file_fields():
         ).contribute_to_class(Book, field_name)
 
 add_file_fields()
+
+
+class BookPopularity(models.Model):
+    book = models.OneToOneField(Book, related_name='popularity')
+    count = models.IntegerField(default=0)
index 0150055..15a4e2a 100644 (file)
@@ -49,7 +49,7 @@ class Collection(models.Model):
 
     def get_books(self):
         from catalogue.models import Book
-        return Book.objects.filter(self.get_query()).order_by('sort_key_author', 'sort_key')
+        return Book.objects.filter(self.get_query())
 
     def flush_includes(self, languages=True):
         if not languages:
index 57935f8..1531973 100644 (file)
@@ -9,6 +9,7 @@ from django.db import models
 from django.db.models import permalink
 from django.dispatch import Signal
 from django.utils.translation import ugettext_lazy as _
+
 from newtagging.models import TagBase
 from ssify import flush_ssi_includes
 
@@ -48,7 +49,9 @@ class Tag(TagBase):
     after_change = Signal(providing_args=['instance', 'languages'])
 
     class UrlDeprecationWarning(DeprecationWarning):
-        pass
+        def __init__(self, tags=None):
+            super(Tag.UrlDeprecationWarning, self).__init__()
+            self.tags = tags
 
     categories_rev = {
         'autor': 'author',
@@ -157,24 +160,24 @@ class Tag(TagBase):
     has_description.boolean = True
 
     @staticmethod
-    def get_tag_list(tags):
-        if isinstance(tags, basestring):
-            if not tags:
+    def get_tag_list(tag_str):
+        if isinstance(tag_str, basestring):
+            if not tag_str:
                 return []
-            real_tags = []
+            tags = []
             ambiguous_slugs = []
             category = None
             deprecated = False
-            tags_splitted = tags.split('/')
+            tags_splitted = tag_str.split('/')
             for name in tags_splitted:
                 if category:
-                    real_tags.append(Tag.objects.get(slug=name, category=category))
+                    tags.append(Tag.objects.get(slug=name, category=category))
                     category = None
                 elif name in Tag.categories_rev:
                     category = Tag.categories_rev[name]
                 else:
                     try:
-                        real_tags.append(Tag.objects.get(slug=name))
+                        tags.append(Tag.objects.get(slug=name))
                         deprecated = True
                     except Tag.MultipleObjectsReturned:
                         ambiguous_slugs.append(name)
@@ -185,16 +188,14 @@ class Tag(TagBase):
             if ambiguous_slugs:
                 # some tags should be qualified
                 e = Tag.MultipleObjectsReturned()
-                e.tags = real_tags
+                e.tags = tags
                 e.ambiguous_slugs = ambiguous_slugs
                 raise e
             if deprecated:
-                e = Tag.UrlDeprecationWarning()
-                e.tags = real_tags
-                raise e
-            return real_tags
+                raise Tag.UrlDeprecationWarning(tags=tags)
+            return tags
         else:
-            return TagBase.get_tag_list(tags)
+            return TagBase.get_tag_list(tag_str)
 
     @property
     def url_chunk(self):
diff --git a/src/catalogue/templates/catalogue/audiobook_list.html b/src/catalogue/templates/catalogue/audiobook_list.html
deleted file mode 100644 (file)
index 7eda46e..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-{% extends "base.html" %}
-{% load i18n %}
-{% load catalogue_tags switch_tag social_tags %}
-{% load ssi_include from ssify %}
-
-{% block titleextra %}{% trans "Audiobooks" %}{% endblock %}
-
-{% block bodyid %}audiobooks{% endblock %}
-
-{% block body %}
-    <h1>{% trans "Audiobooks" %}</h1>
-
-    {% work_list best %}
-    <h2>{% trans "Listing of all audiobooks" %}</h2>
-    {% plain_list books by_author=True %}
-
-    <h2>{% trans "Listing of all DAISY files" %}</h2>
-    {% plain_list daisy by_author=True %}
-
-
-{% endblock %}
index 26ee55d..aa78292 100644 (file)
@@ -1,6 +1,6 @@
 {% extends "catalogue/book_short.html" %}
 {% load i18n %}
-{% load choose_fragment download_audio tag_list custom_pdf_link_li license_icon source_name from catalogue_tags %}
+{% load choose_fragment download_audio custom_pdf_link_li license_icon source_name from catalogue_tags %}
 {% load choose_cite from social_tags %}
 {% load ssi_include from ssify %}
 
index f6d59a7..802fcb1 100755 (executable)
@@ -1,34 +1,27 @@
 {% load i18n %}
 {% load catalogue_tags %}
-       {% if choices %}
-        {% if category_choices %}
-            <ul>
-            {% for tag in category_choices %}
-                <li class="active">{{ tag }} <a href="{% if gallery %}{% catalogue_url_gallery choices -tag %}{% else %}{% catalogue_url choices -tag %}{% endif %}">X</a></li>
-            {% endfor %}
-            </ul>
-        {% endif %}
-        {% if tags %}
+
+{% if choices %}
+    {% if category_choices %}
         <ul>
-        {% for tag in tags %}
-            <li><a href="{% if gallery %}{% catalogue_url_gallery choices tag %}{% else %}{% catalogue_url choices tag %}{% endif %}">{{ tag }}{% if tag.count %}&nbsp;({{ tag.count }}){% endif %}</a></li>
+        {% for tag in category_choices %}
+            <li class="active">{{ tag }} <a href="{% catalogue_url list_type choices -tag %}">X</a></li>
         {% endfor %}
         </ul>
-        {% endif %}
-    {% else %}
-        {% if tags %}
-        <ul>
+    {% endif %}
+{% endif %}
+{% if tags %}
+    <ul>
         {% for tag in tags %}
-            <li><a href="{% if gallery %}{{ tag.get_absolute_gallery_url }}{% else %}{{ tag.get_absolute_url }}{% endif %}">{{ tag }} {% if tag.count %}&nbsp;({{ tag.count }}){% endif %}</a></li>
+            <li><a href="{% catalogue_url list_type choices tag %}">{{ tag }}{% if tag.count %}&nbsp;({{ tag.count }}){% endif %}</a></li>
         {% endfor %}
-        </ul>
-        {% endif %}
-    {% endif %}
-    {% if other %}
-       <ul>
+    </ul>
+{% endif %}
+{% if other %}
+    <ul>
         <li class="header">{% trans "Other" %}:</li>
         {% for tag in other %}
-            <li class="other"><a href="{% if gallery %}{{ tag.get_absolute_gallery_url }}{% else %}{{ tag.get_absolute_url }}{% endif %}">{{ tag }}</a></li>
+            <li class="other"><a href="{% catalogue_url list_type tag %}">{{ tag }}</a></li>
         {% endfor %}
     </ul>
-    {% endif %}
+{% endif %}
index c8cad33..e26bb7f 100644 (file)
@@ -3,19 +3,21 @@
 
 
 <div class='{% if paged %}plain-list-paged {% endif %}plain-list-container white-box'>
-<div class='plain-list' style=''>
-{% for initial, object_list in names %}
-    {% if initial_blocks %}<div class="initial-block">{% endif %}
-    {% if initial %}
+  <div class="pager-center">
+      <div class="pager"></div>
+  </div>
+  <div class='plain-list' style=''>
+    {% for initial, object_list in names %}
+      {% if initial_blocks %}<div class="initial-block">{% endif %}
+      {% if initial %}
         <p class="header">{{ initial }}</p>
-    {% endif %}
-    {% for item in object_list %}
-        <p><a href="{% if book %}{% url 'book_fragments' book.slug item.slug %}{% elif choice %}{% if gallery %}{% catalogue_url_gallery choice item %}{% else %}{% catalogue_url choice item %}{% endif %}{% elif gallery and item.get_absolute_gallery_url %}{{ item.get_absolute_gallery_url }}{% else %}{{ item.get_absolute_url }}{% endif %}">{{ item }}{% if item.count %}&nbsp;({{ item.count}}){% endif %}</a></p>
-    {% endfor %}
+      {% endif %}
+      {% for item in object_list %}
+        <p><a href="{% if book %}{% url 'book_fragments' book.slug item.slug %}{% elif choice %}{% catalogue_url list_type choice item %}{% elif list_type == 'gallery' and item.get_absolute_gallery_url %}{{ item.get_absolute_gallery_url }}{% else %}{{ item.get_absolute_url }}{% endif %}">{{ item }}{% if item.count %}&nbsp;({{ item.count}}){% endif %}</a></p>
+      {% endfor %}
     {% if initial_blocks %}</div>{% endif %}
-{% endfor %}
-</div>
-<div class="pager"></div>
+    {% endfor %}
+  </div>
 </div>
 
 {% endspaceless %}
index e1339ad..397d36e 100644 (file)
@@ -32,8 +32,8 @@
               <li data-mp3='{{ i.mp3.file.url }}' data-ogg='{{ i.ogg.file.url }}'>
                 <div class='play'>{{ i.mp3.name }}
                   <div class='extra-info'>
-                    {% trans "Artist:" %} <span class='artist'>{{ i.mp3.extra_info.artist_name }}</span>,
-                    {% trans "director:" %} <span class='director'>{{ i.mp3.extra_info.director_name }}</span>
+                    {% trans "Artist:" %}&nbsp;<span class='artist'>{{ i.mp3.extra_info.artist_name }}</span>,
+                    {% trans "director:" %}&nbsp;<span class='director'>{{ i.mp3.extra_info.director_name }}</span>
                   </div>
                 </div>
               </li>
index e0fecc0..2a7fd6a 100644 (file)
@@ -3,12 +3,12 @@
 {% load i18n %}
 {% load catalogue_tags %}
 {% if one_tag %}
-    <p>{% trans "See full category" %} <a href="{% catalogue_url one_tag %}">{{ one_tag }}</a></p>
+    <p>{% trans "See full category" %} <a href="{% catalogue_url 'default' one_tag %}">{{ one_tag }}</a></p>
 {% else %}
     <ul>
         {% if choices %}
         {% for tag in tags %}
-            <li><a href="{% catalogue_url choices tag %}">{{ tag }}{% if tag.count %}&nbsp;({{ tag.count }}){% endif %}</a></li>
+            <li><a href="{% catalogue_url 'default' choices tag %}">{{ tag }}{% if tag.count %}&nbsp;({{ tag.count }}){% endif %}</a></li>
         {% endfor %}
         {% else %}
         {% for tag in tags %}
index 9a3455d..7603d7b 100644 (file)
@@ -3,17 +3,13 @@
 {% load catalogue_tags switch_tag social_tags %}
 {% load ssi_include from ssify %}
 
-{% block titleextra %}{% if tags %}{% title_from_tags tags %}{% elif gallery %}{% trans "Gallery" %}{% else %}{% trans "Literature" %}{% endif %}{% endblock %}
+{% block titleextra %}{% if tags %}{% title_from_tags tags %}{% elif list_type == 'gallery' %}{% trans "Gallery" %}{% elif list_type == 'audiobooks' %}{% trans "Audiobooks" %}{% else %}{% trans "Literature" %}{% endif %}{% endblock %}
 
 {% block bodyid %}tagged-object-list{% endblock %}
 
 {% block body %}
 <div class="tabbed-filter">
-    <h1>{% if tags %}
-        {% html_title_from_tags tags %}
-        {% elif gallery %}{% trans "Gallery" %}{% else %}{% trans "Literature" %}
-        {% endif %}
-    </h1>
+    <h1>{% if tags %}{% html_title_from_tags tags %}{% endif %}</h1>
 
     <div class="tabs">
         <a class="tab white-box" data-id="authors">{% trans "Authors" %}</a>
         <a class="tab white-box" data-id="genres">{% trans "Genres" %}</a>
         <a class="tab white-box" data-id="kinds">{% trans "Kinds" %}</a>
         {% if theme_is_set %}
-        <a class="tab white-box" data-id="themes">{% trans "Themes" %}</a>
+            <a class="tab white-box" data-id="themes">{% trans "Themes" %}</a>
         {% endif %}
     </div>
-
 </div>
+
 <div class="tabbed-filter-contents">
     <div id="authors" class="white-box normal-text tab-content">
-        {% inline_tag_list categories.author tags 'author' gallery=gallery %}
+        {% inline_tag_list categories.author tags 'author' list_type=list_type %}
     </div>
     <div id="epochs" class="white-box normal-text tab-content">
-        {% inline_tag_list categories.epoch tags 'epoch' gallery=gallery %}
+        {% inline_tag_list categories.epoch tags 'epoch' list_type=list_type %}
     </div>
     <div id="genres" class="white-box normal-text tab-content">
-        {% inline_tag_list categories.genre tags 'genre' gallery=gallery %}
+        {% inline_tag_list categories.genre tags 'genre' list_type=list_type %}
     </div>
     <div id="kinds" class="white-box normal-text tab-content">
-        {% inline_tag_list categories.kind tags 'kind' gallery=gallery %}
+        {% inline_tag_list categories.kind tags 'kind' list_type=list_type %}
     </div>
     {% if theme_is_set %}
         <div id="themes" class="white-box normal-text tab-content">
-            {% inline_tag_list categories.theme tags 'theme' gallery=gallery %}
+            {% inline_tag_list categories.theme tags 'theme' list_type=list_type %}
         </div>
     {% endif %}
 </div>
 
+{% comment %}
+{% if list_type == 'books' and not tags %}
+    <section>
+        <h1>{% trans "Recent publications" %}</h1>
+        {% for book in last_published %}
+            {% ssi_include 'catalogue_book_mini' pk=book.pk %}
+        {% endfor %}
+        <a class="more" href="{% url 'recent_list' %}">{% trans "More recent publications" %}</a>
+    </section>
+    <section>
+        <h1>{% trans "Most popular books" %}</h1>
+        {% for book in most_popular %}
+            {% ssi_include 'catalogue_book_mini' pk=book.pk %}
+        {% endfor %}
+    </section>
+{% endif %}
+{% endcomment %}
 
-
-
-    {% if theme_is_set %}
-        {% work_list object_list %}
-    {% else %}
+{% if theme_is_set %}
+    {% work_list object_list %}
+{% else %}
     <div id="books-list">
         {% if object_list %}
             {% work_list best %}
             {% if tags %}
                 <h2>{% trans "All matching works" %}</h2>
             {% else %}
-                <h2>{% trans "All works" %}</h2>
+                {% if list_type == 'audiobooks' %}
+                    <h2>{% trans "Listing of all audiobooks" %}</h2>
+                {% else %}
+                    <h2>{% trans "All works" %}</h2>
+                 {% endif %}
+            {% endif %}
+            {% plain_list object_list by_author=True list_type=list_type %}
+            {% if daisy %}
+                <h2>{% trans "DAISY files" %}</h2>
+                {% plain_list daisy by_author=True %}
             {% endif %}
-            {% plain_list object_list by_author=True gallery=gallery %}
         {% else %}
             {% trans "Sorry! Search cirteria did not match any resources." %}
             {% include "info/join_us.html" %}
         {% endif %}
     </div>
 
-
-
-
-{% if categories.theme %}
-    <h2>{% trans "Motifs and themes" %}</h2>
-    {% plain_list categories.theme choice=tags gallery=gallery %}
-{% endif %}
-
-
+    {% if categories.theme and list_type != 'audiobooks' %}
+        <h2>{% trans "Motifs and themes" %}</h2>
+        {% plain_list categories.theme choice=tags list_type=list_type %}
+    {% endif %}
 
 {% endif %}
 
-
-
-    {% for tag in tags %}
+{% for tag in tags %}
     {% if tag.category != 'set' %}
-    <h2>{% trans tag.category as c %}{{ c|capfirst }}: {{ tag }}</h2>
+        <h2>{% trans tag.category as c %}{{ c|capfirst }}: {{ tag }}</h2>
         <div class="white-box">
-        {% if tag.has_description %}
-            {{ tag.description|safe }}
-        {% else %}
-            <em>{% trans "No description." %}</em>
-        {% endif %}
+            {% if tag.has_description %}
+                {{ tag.description|safe }}
+            {% else %}
+                <em>{% trans "No description." %}</em>
+            {% endif %}
         </div>
 
         {% if tag.gazeta_link %}
-        <div class="white-box"><a href="{{ tag.gazeta_link }}">
-            {{ tag }} {% trans "in Lektury.Gazeta.pl" %}
-        </a></div>
+            <div class="white-box"><a href="{{ tag.gazeta_link }}">
+                {{ tag }} {% trans "in Lektury.Gazeta.pl" %}
+            </a></div>
         {% endif %}
         {% if tag.wiki_link %}
-        <div class="white-box">
-        <a href="{{ tag.wiki_link }}">
-            {{ tag }} {% trans "in Wikipedia" %}
-        </a></div>
+            <div class="white-box">
+            <a href="{{ tag.wiki_link }}">
+                {{ tag }} {% trans "in Wikipedia" %}
+            </a></div>
         {% endif %}
         {% if tag.culturepl_link %}
-        <div class="white-box">
-        <a href="{{ tag.culturepl_link }}">
-            {{ tag }} {% trans "in Culture.pl" %}
-        </a></div>
+            <div class="white-box">
+            <a href="{{ tag.culturepl_link }}">
+                {{ tag }} {% trans "in Culture.pl" %}
+            </a></div>
         {% endif %}
     {% endif %}
-    {% endfor %}
-
-
-
-
+{% endfor %}
 
 {% endblock %}
index 5064cb6..6f50ad7 100644 (file)
@@ -15,6 +15,8 @@ from django.utils.cache import add_never_cache_headers
 from django.utils.translation import ugettext as _
 
 from ssify import ssi_variable
+
+from catalogue.helpers import get_audiobook_tags
 from catalogue.models import Book, BookMedia, Fragment, Tag, Source
 from catalogue.constants import LICENSES
 from picture.models import Picture
@@ -242,37 +244,23 @@ def catalogue_url(parser, token):
 
     tags_to_add = []
     tags_to_remove = []
-    for bit in bits[1:]:
+    for bit in bits[2:]:
         if bit[0] == '-':
             tags_to_remove.append(bit[1:])
         else:
             tags_to_add.append(bit)
 
-    return CatalogueURLNode(tags_to_add, tags_to_remove)
-
-
-@register.tag
-def catalogue_url_gallery(parser, token):
-    bits = token.split_contents()
-
-    tags_to_add = []
-    tags_to_remove = []
-    for bit in bits[1:]:
-        if bit[0] == '-':
-            tags_to_remove.append(bit[1:])
-        else:
-            tags_to_add.append(bit)
-
-    return CatalogueURLNode(tags_to_add, tags_to_remove, gallery=True)
+    return CatalogueURLNode(bits[1], tags_to_add, tags_to_remove)
 
 
 class CatalogueURLNode(Node):
-    def __init__(self, tags_to_add, tags_to_remove, gallery=False):
+    def __init__(self, list_type, tags_to_add, tags_to_remove):
         self.tags_to_add = [Variable(tag) for tag in tags_to_add]
         self.tags_to_remove = [Variable(tag) for tag in tags_to_remove]
-        self.gallery = gallery
+        self.list_type_var = Variable(list_type)
 
     def render(self, context):
+        list_type = self.list_type_var.resolve(context)
         tags_to_add = []
         tags_to_remove = []
 
@@ -298,17 +286,24 @@ class CatalogueURLNode(Node):
                 pass
 
         if len(tag_slugs) > 0:
-            if self.gallery:
+            if list_type == 'gallery':
                 return reverse('tagged_object_list_gallery', kwargs={'tags': '/'.join(tag_slugs)})
+            elif list_type == 'audiobooks':
+                return reverse('tagged_object_list_audiobooks', kwargs={'tags': '/'.join(tag_slugs)})
             else:
                 return reverse('tagged_object_list', kwargs={'tags': '/'.join(tag_slugs)})
         else:
-            return reverse('book_list')
+            if list_type == 'gallery':
+                return reverse('gallery')
+            elif list_type == 'audiobooks':
+                return reverse('audiobook_list')
+            else:
+                return reverse('book_list')
 
 
-@register.inclusion_tag('catalogue/tag_list.html')
-def tag_list(tags, choices=None, category=None, gallery=False):
-    print(tags, choices, category)
+@register.inclusion_tag('catalogue/tag_list.html')
+def tag_list(tags, choices=None, category=None, list_type='books'):
+    print(tags, choices, category)
     if choices is None:
         choices = []
 
@@ -319,25 +314,38 @@ def tag_list(tags, choices=None, category=None, gallery=False):
 
     if len(tags) == 1 and category not in [t.category for t in choices]:
         one_tag = tags[0]
+    else:
+        one_tag = None
 
     if category is not None:
         other = Tag.objects.filter(category=category).exclude(pk__in=[t.pk for t in tags])\
             .exclude(pk__in=[t.pk for t in category_choices])
         # Filter out empty tags.
-        ct = ContentType.objects.get_for_model(Picture if gallery else Book)
+        ct = ContentType.objects.get_for_model(Picture if list_type == 'gallery' else Book)
         other = other.filter(items__content_type=ct).distinct()
+        if list_type == 'audiobooks':
+            other = other.filter(id__in=get_audiobook_tags())
+    else:
+        other = []
 
-    return locals()
+    return {
+        'one_tag': one_tag,
+        'choices': choices,
+        'category_choices': category_choices,
+        'tags': tags,
+        'other': other,
+        'list_type': list_type,
+    }
 
 
 @register.inclusion_tag('catalogue/inline_tag_list.html')
-def inline_tag_list(tags, choices=None, category=None, gallery=False):
-    return tag_list(tags, choices, category, gallery)
+def inline_tag_list(tags, choices=None, category=None, list_type='books'):
+    return tag_list(tags, choices, category, list_type)
 
 
 @register.inclusion_tag('catalogue/collection_list.html')
 def collection_list(collections):
-    return locals()
+    return {'collections': collections}
 
 
 @register.inclusion_tag('catalogue/book_info.html')
@@ -351,11 +359,11 @@ def book_info(book):
 @register.inclusion_tag('catalogue/work-list.html', takes_context=True)
 def work_list(context, object_list):
     request = context.get('request')
-    return locals()
+    return {'object_list': object_list, 'request': request}
 
 
 @register.inclusion_tag('catalogue/plain_list.html', takes_context=True)
-def plain_list(context, object_list, with_initials=True, by_author=False, choice=None, book=None, gallery=False,
+def plain_list(context, object_list, with_initials=True, by_author=False, choice=None, book=None, list_type='books',
                paged=True, initial_blocks=False):
     names = [('', [])]
     last_initial = None
@@ -372,7 +380,14 @@ def plain_list(context, object_list, with_initials=True, by_author=False, choice
                 last_initial = initial
                 names.append((obj.author_unicode() if by_author else initial, []))
         names[-1][1].append(obj)
-    return locals()
+    return {
+        'paged': paged,
+        'names': names,
+        'initial_blocks': initial_blocks,
+        'book': book,
+        'list_type': list_type,
+        'choice': choice,
+    }
 
 
 # TODO: These are no longer just books.
index a4627e2..1d4051b 100644 (file)
@@ -14,6 +14,8 @@ SLUG = r'[a-z0-9-]*'
 
 urlpatterns = patterns(
     'picture.views',
+
+    url(r'^obraz/strona/$', 'picture_page', name='picture_page'),
     # pictures - currently pictures are coupled with catalogue, hence the url is here
     url(r'^obraz/$', 'picture_list_thumb', name='picture_list_thumb'),
     url(r'^obraz/(?P<slug>%s).html$' % SLUG, 'picture_viewer', name='picture_viewer'),
@@ -41,12 +43,12 @@ urlpatterns += patterns(
     url(r'^rodzaj/$', 'tag_catalogue', {'category': 'kind'}, name='kind_catalogue'),
     url(r'^motyw/$', 'tag_catalogue', {'category': 'theme'}, name='theme_catalogue'),
 
-    url(r'^galeria/$', 'tagged_object_list', {'gallery': True}, name='gallery'),
+    url(r'^galeria/$', 'gallery', name='gallery'),
     url(r'^kolekcje/$', 'collections', name='catalogue_collections'),
 
-    url(r'^lektury/$', 'tagged_object_list', name='book_list'),
+    url(r'^lektury/$', 'literature', name='book_list'),
     url(r'^lektury/(?P<slug>[a-zA-Z0-9-]+)/$', 'collection', name='collection'),
-    url(r'^audiobooki/$', 'audiobook_list', name='audiobook_list'),
+    url(r'^audiobooki/$', 'audiobooks', name='audiobook_list'),
     url(r'^daisy/$', 'daisy_list', name='daisy_list'),
     url(r'^tags/$', 'tags_starting_with', name='hint'),
     url(r'^jtags/?$', 'json_tags_starting_with', name='jhint'),
@@ -91,7 +93,10 @@ urlpatterns += patterns(
     url(r'^c/(?P<pk>.+)/box\.(?P<lang>.+)\.html', 'collection_box', name='catalogue_collection_box'),
 
     # This should be the last pattern.
-    url(r'^galeria/(?P<tags>[a-zA-Z0-9-/]*)/$', 'tagged_object_list', {'gallery': True},
+    url(r'^galeria/(?P<tags>[a-zA-Z0-9-/]*)/$', 'tagged_object_list', {'list_type': 'gallery'},
         name='tagged_object_list_gallery'),
-    url(r'^(?P<tags>[a-zA-Z0-9-/]*)/$', 'tagged_object_list', name='tagged_object_list'),
+    url(r'^audiobooki/(?P<tags>[a-zA-Z0-9-/]*)/$', 'tagged_object_list', {'list_type': 'audiobooks'},
+        name='tagged_object_list_audiobooks'),
+    url(r'^(?P<tags>[a-zA-Z0-9-/]*)/$', 'tagged_object_list', {'list_type': 'books'},
+        name='tagged_object_list'),
 )
index 6222185..1955137 100644 (file)
@@ -19,69 +19,55 @@ from django.utils import translation
 from django.utils.translation import ugettext as _, ugettext_lazy
 
 from ajaxable.utils import AjaxableFormView
-from pdcounter import models as pdcounter_models
+from pdcounter.models import BookStub, Author
 from pdcounter import views as pdcounter_views
 from picture.models import Picture, PictureArea
-from ssify import ssi_included, ssi_expect, SsiVariable as V
+from ssify import ssi_included, ssi_expect, SsiVariable as Var
 from suggest.forms import PublishingSuggestForm
 from catalogue import constants
 from catalogue import forms
 from catalogue.helpers import get_top_level_related_tags
-from catalogue import models
+from catalogue.models import Book, Collection, Tag, Fragment
 from catalogue.utils import split_tags
 
 staff_required = user_passes_test(lambda user: user.is_staff)
 
 
-def catalogue(request, as_json=False):
-    books = models.Book.objects.filter(parent=None).order_by('sort_key_author', 'sort_key')
-    pictures = Picture.objects.order_by('sort_key_author', 'sort_key')
-    collections = models.Collection.objects.all()
-    return render(request, 'catalogue/catalogue.html', locals())
+def catalogue(request):
+    return render(request, 'catalogue/catalogue.html', {
+        'books': Book.objects.filter(parent=None),
+        'pictures': Picture.objects.all(),
+        'collections': Collection.objects.all(),
+        'active_menu_item': 'all_works',
+    })
 
 
-def book_list(request, filter=None, get_filter=None, template_name='catalogue/book_list.html',
+def book_list(request, filters=None, template_name='catalogue/book_list.html',
               nav_template_name='catalogue/snippets/book_list_nav.html',
-              list_template_name='catalogue/snippets/book_list.html', context=None):
-    """ generates a listing of all books, optionally filtered with a test function """
-    if get_filter:
-        filter = get_filter()
-    books_by_author, orphans, books_by_parent = models.Book.book_list(filter)
+              list_template_name='catalogue/snippets/book_list.html'):
+    """ generates a listing of all books, optionally filtered """
+    books_by_author, orphans, books_by_parent = Book.book_list(filters)
     books_nav = OrderedDict()
     for tag in books_by_author:
         if books_by_author[tag]:
             books_nav.setdefault(tag.sort_key[0], []).append(tag)
-    rendered_nav = render_to_string(nav_template_name, locals())
-    rendered_book_list = render_to_string(list_template_name, locals())
-    return render_to_response(template_name, locals(), context_instance=RequestContext(request))
-
-
-def audiobook_list(request):
-    books = models.Book.objects.filter(media__type__in=('mp3', 'ogg')).distinct().order_by(
-        'sort_key_author', 'sort_key')
-    books = list(books)
-    if len(books) > 3:
-        best = random.sample(books, 3)
-    else:
-        best = books
-
-    daisy = models.Book.objects.filter(media__type='daisy').distinct().order_by('sort_key_author', 'sort_key')
-
-    return render(request, 'catalogue/audiobook_list.html', {
-        'books': books,
-        'best': best,
-        'daisy': daisy,
+    # WTF: dlaczego nie include?
+    return render_to_response(template_name, {
+        'rendered_nav': render_to_string(nav_template_name, {'books_nav': books_nav}),
+        'rendered_book_list': render_to_string(list_template_name, {
+            'books_by_author': books_by_author,
+            'orphans': orphans,
+            'books_by_parent': books_by_parent,
         })
+    }, context_instance=RequestContext(request))
 
 
 def daisy_list(request):
-    return book_list(request, Q(media__type='daisy'),
-                     template_name='catalogue/daisy_list.html',
-                     )
+    return book_list(request, Q(media__type='daisy'), template_name='catalogue/daisy_list.html')
 
 
 def collection(request, slug):
-    coll = get_object_or_404(models.Collection, slug=slug)
+    coll = get_object_or_404(Collection, slug=slug)
     return render(request, 'catalogue/collection.html', {'collection': coll})
 
 
@@ -89,7 +75,7 @@ def differentiate_tags(request, tags, ambiguous_slugs):
     beginning = '/'.join(tag.url_chunk for tag in tags)
     unparsed = '/'.join(ambiguous_slugs[1:])
     options = []
-    for tag in models.Tag.objects.filter(slug=ambiguous_slugs[0]):
+    for tag in Tag.objects.filter(slug=ambiguous_slugs[0]):
         options.append({
             'url_args': '/'.join((beginning, tag.url_chunk, unparsed)).strip('/'),
             'tags': [tag]
@@ -99,26 +85,98 @@ def differentiate_tags(request, tags, ambiguous_slugs):
         context_instance=RequestContext(request))
 
 
-# TODO: Rewrite this hellish piece of code which tries to do everything
-def tagged_object_list(request, tags='', gallery=False):
-    raw_tags = tags
-    # preliminary tests and conditions
+def object_list(request, objects, fragments=None, related_tags=None, tags=None, list_type='books', extra=None):
+    if not tags:
+        tags = []
+    tag_ids = [tag.pk for tag in tags]
+
+    related_tag_lists = []
+    if related_tags:
+        related_tag_lists.append(related_tags)
+    else:
+        related_tag_lists.append(
+            Tag.objects.usage_for_queryset(objects, counts=True).exclude(category='set').exclude(pk__in=tag_ids))
+    if not (extra and extra.get('theme_is_set')):
+        if fragments is None:
+            if list_type == 'gallery':
+                fragments = PictureArea.objects.filter(picture__in=objects)
+            else:
+                fragments = Fragment.objects.filter(book__in=objects)
+        related_tag_lists.append(
+            Tag.objects.usage_for_queryset(fragments, counts=True).filter(category='theme').exclude(pk__in=tag_ids))
+
+    categories = split_tags(*related_tag_lists)
+
+    objects = list(objects)
+    if len(objects) > 3:
+        best = random.sample(objects, 3)
+    else:
+        best = objects
+
+    result = {
+        'object_list': objects,
+        'categories': categories,
+        'list_type': list_type,
+        'tags': tags,
+
+        'formats_form': forms.DownloadFormatsForm(),
+        'best': best,
+        'active_menu_item': list_type,
+    }
+    if extra:
+        result.update(extra)
+    return render_to_response(
+        'catalogue/tagged_object_list.html', result,
+        context_instance=RequestContext(request))
+
+
+def literature(request):
+    books = Book.objects.filter(parent=None)
+
+    # last_published = Book.objects.exclude(cover_thumb='').filter(parent=None).order_by('-created_at')[:20]
+    # most_popular = Book.objects.exclude(cover_thumb='')\
+    #                    .order_by('-popularity__count', 'sort_key_author', 'sort_key')[:20]
+    return object_list(request, books, related_tags=get_top_level_related_tags([]))
+    # extra={
+    #     'last_published': last_published,
+    #     'most_popular': most_popular,
+    # })
+
+
+def gallery(request):
+    return object_list(request, Picture.objects.all(), list_type='gallery')
+
+
+def audiobooks(request):
+    audiobooks = Book.objects.filter(media__type__in=('mp3', 'ogg')).distinct()
+    return object_list(request, audiobooks, list_type='audiobooks', extra={
+        'daisy': Book.objects.filter(media__type='daisy').distinct(),
+    })
+
+
+class ResponseInstead(Exception):
+    def __init__(self, response):
+        super(ResponseInstead, self).__init__()
+        self.response = response
+
+
+def analyse_tags(request, tag_str):
     try:
-        tags = models.Tag.get_tag_list(tags)
-    except models.Tag.DoesNotExist:
+        tags = Tag.get_tag_list(tag_str)
+    except Tag.DoesNotExist:
         # Perhaps the user is asking about an author in Public Domain
         # counter (they are not represented in tags)
-        chunks = tags.split('/')
+        chunks = tag_str.split('/')
         if len(chunks) == 2 and chunks[0] == 'autor':
-            return pdcounter_views.author_detail(request, chunks[1])
+            raise ResponseInstead(pdcounter_views.author_detail(request, chunks[1]))
         else:
             raise Http404
-    except models.Tag.MultipleObjectsReturned, e:
+    except Tag.MultipleObjectsReturned, e:
         # Ask the user to disambiguate
-        return differentiate_tags(request, e.tags, e.ambiguous_slugs)
-    except models.Tag.UrlDeprecationWarning, e:
-        return HttpResponsePermanentRedirect(
-            reverse('tagged_object_list', args=['/'.join(tag.url_chunk for tag in e.tags)]))
+        raise ResponseInstead(differentiate_tags(request, e.tags, e.ambiguous_slugs))
+    except Tag.UrlDeprecationWarning, e:
+        raise ResponseInstead(HttpResponsePermanentRedirect(
+            reverse('tagged_object_list', args=['/'.join(tag.url_chunk for tag in e.tags)])))
 
     try:
         if len(tags) > settings.MAX_TAG_LIST:
@@ -126,131 +184,100 @@ def tagged_object_list(request, tags='', gallery=False):
     except AttributeError:
         pass
 
-    # beginning of digestion
-    theme_is_set = [tag for tag in tags if tag.category == 'theme']
-    shelf_is_set = [tag for tag in tags if tag.category == 'set']
-    only_shelf = shelf_is_set and len(tags) == 1
-    only_my_shelf = only_shelf and request.user.is_authenticated() and request.user == tags[0].user
-    tags_pks = [tag.pk for tag in tags]
-
-    objects = None
-
-    if theme_is_set:
-        # Only fragments (or pirctureareas) here.
-        shelf_tags = [tag for tag in tags if tag.category == 'set']
-        fragment_tags = [tag for tag in tags if tag.category != 'set']
-        if gallery:
-            fragments = PictureArea.tagged.with_all(fragment_tags)
-        else:
-            fragments = models.Fragment.tagged.with_all(fragment_tags)
-
-        if shelf_tags:
-            if gallery:
-                # TODO: Pictures on shelves not supported yet.
-                raise Http404
-            else:
-                books = models.Book.tagged.with_all(shelf_tags).order_by()
-                fragments = fragments.filter(Q(book__in=books) | Q(book__ancestor__in=books))
+    return tags
 
-        categories = split_tags(
-            models.Tag.objects.usage_for_queryset(fragments, counts=True).exclude(pk__in=tags_pks),
-        )
 
-        objects = fragments
+def theme_list(request, tags, list_type):
+    shelf_tags = [tag for tag in tags if tag.category == 'set']
+    fragment_tags = [tag for tag in tags if tag.category != 'set']
+    if list_type == 'gallery':
+        fragments = PictureArea.tagged.with_all(fragment_tags)
     else:
-        if gallery:
-            if shelf_is_set:
-                # TODO: Pictures on shelves not supported yet.
-                raise Http404
-            else:
-                if tags:
-                    objects = Picture.tagged.with_all(tags)
-                else:
-                    objects = Picture.objects.all()
-            areas = PictureArea.objects.filter(picture__in=objects)
-            categories = split_tags(
-                models.Tag.objects.usage_for_queryset(
-                    objects, counts=True).exclude(pk__in=tags_pks),
-                models.Tag.objects.usage_for_queryset(
-                    areas, counts=True).filter(
-                    category__in=('theme', 'thing')).exclude(
-                    pk__in=tags_pks),
-            )
-        else:
-            if tags:
-                all_books = models.Book.tagged.with_all(tags)
-            else:
-                all_books = models.Book.objects.filter(parent=None)
-            if shelf_is_set:
-                objects = all_books
-                related_book_tags = models.Tag.objects.usage_for_queryset(
-                    objects, counts=True).exclude(
-                    category='set').exclude(pk__in=tags_pks)
-            else:
-                if tags:
-                    objects = models.Book.tagged_top_level(tags)
-                else:
-                    objects = all_books
-                # WTF: was outside if, overwriting value assigned if shelf_is_set
-                related_book_tags = get_top_level_related_tags(tags)
-
-            fragments = models.Fragment.objects.filter(book__in=all_books)
-
-            categories = split_tags(
-                related_book_tags,
-                models.Tag.objects.usage_for_queryset(
-                    fragments, counts=True).filter(
-                    category='theme').exclude(pk__in=tags_pks),
-            )
-        objects = objects.order_by('sort_key_author', 'sort_key')
+        fragments = Fragment.tagged.with_all(fragment_tags)
 
-    objects = list(objects)
-    if len(objects) > 3:
-        best = random.sample(objects, 3)
-    else:
-        best = objects
+    if shelf_tags:
+        # TODO: Pictures on shelves not supported yet.
+        books = Book.tagged.with_all(shelf_tags).order_by()
+        fragments = fragments.filter(Q(book__in=books) | Q(book__ancestor__in=books))
 
-    if not gallery and not objects and len(tags) == 1:
+    if not fragments and len(tags) == 1 and list_type == 'books':
         tag = tags[0]
-        if (tag.category in ('theme', 'thing') and PictureArea.tagged.with_any([tag]).exists() or
+        if tag.category == 'theme' and (
+                PictureArea.tagged.with_any([tag]).exists() or
                 Picture.tagged.with_any([tag]).exists()):
-            return redirect('tagged_object_list_gallery', raw_tags, permanent=False)
+            return redirect('tagged_object_list_gallery', '/'.join(tag.url_chunk for tag in tags))
 
-    return render_to_response(
-        'catalogue/tagged_object_list.html',
-        {
-            'object_list': objects,
-            'categories': categories,
-            'only_shelf': only_shelf,
-            'only_my_shelf': only_my_shelf,
-            'formats_form': forms.DownloadFormatsForm(),
-            'tags': tags,
-            'tag_ids': tags_pks,
-            'theme_is_set': theme_is_set,
-            'best': best,
-            'gallery': gallery,
-        },
-        context_instance=RequestContext(request))
+    return object_list(request, fragments, tags=tags, list_type=list_type, extra={
+        'theme_is_set': True,
+        'active_menu_item': 'theme',
+    })
+
+
+def tagged_object_list(request, tags, list_type):
+    try:
+        tags = analyse_tags(request, tags)
+    except ResponseInstead as e:
+        return e.response
+
+    if list_type == 'gallery' and any(tag.category == 'set' for tag in tags):
+        raise Http404
+
+    if any(tag.category in ('theme', 'thing') for tag in tags):
+        return theme_list(request, tags, list_type=list_type)
+
+    if list_type == 'books':
+        books = Book.tagged.with_all(tags)
+
+        if any(tag.category == 'set' for tag in tags):
+            params = {'objects': books}
+        else:
+            params = {
+                'objects': Book.tagged_top_level(tags),
+                'fragments': Fragment.objects.filter(book__in=books),
+                'related_tags': get_top_level_related_tags(tags),
+            }
+    elif list_type == 'gallery':
+        params = {'objects': Picture.tagged.with_all(tags)}
+    elif list_type == 'audiobooks':
+        audiobooks = Book.objects.filter(media__type__in=('mp3', 'ogg')).distinct()
+        params = {
+            'objects': Book.tagged.with_all(tags, audiobooks),
+            'extra': {
+                'daisy': Book.tagged.with_all(tags, audiobooks.filter(media__type='daisy').distinct()),
+            }
+        }
+    else:
+        raise Http404
+
+    return object_list(request, tags=tags, list_type=list_type, **params)
 
 
 def book_fragments(request, slug, theme_slug):
-    book = get_object_or_404(models.Book, slug=slug)
-    theme = get_object_or_404(models.Tag, slug=theme_slug, category='theme')
-    fragments = models.Fragment.tagged.with_all([theme]).filter(
+    book = get_object_or_404(Book, slug=slug)
+    theme = get_object_or_404(Tag, slug=theme_slug, category='theme')
+    fragments = Fragment.tagged.with_all([theme]).filter(
         Q(book=book) | Q(book__ancestor=book))
 
-    return render_to_response('catalogue/book_fragments.html', locals(), context_instance=RequestContext(request))
+    return render_to_response('catalogue/book_fragments.html', {
+        'book': book,
+        'theme': theme,
+        'fragments': fragments,
+        'active_menu_item': 'books',
+    }, context_instance=RequestContext(request))
 
 
 def book_detail(request, slug):
     try:
-        book = models.Book.objects.get(slug=slug)
-    except models.Book.DoesNotExist:
+        book = Book.objects.get(slug=slug)
+    except Book.DoesNotExist:
         return pdcounter_views.book_stub_detail(request, slug)
 
-    tags = book.tags.exclude(category__in=('set', 'theme'))
-    book_children = book.children.all().order_by('parent_number', 'sort_key')
-    return render_to_response('catalogue/book_detail.html', locals(), context_instance=RequestContext(request))
+    return render_to_response('catalogue/book_detail.html', {
+        'book': book,
+        'tags': book.tags.exclude(category__in=('set', 'theme')),
+        'book_children': book.children.all().order_by('parent_number', 'sort_key'),
+        'active_menu_item': 'books',
+    }, context_instance=RequestContext(request))
 
 
 def get_audiobooks(book):
@@ -284,24 +311,28 @@ def get_audiobooks(book):
     return audiobooks, projects, have_oggs
 
 
+# używane w publicznym interfejsie
 def player(request, slug):
-    book = get_object_or_404(models.Book, slug=slug)
+    book = get_object_or_404(Book, slug=slug)
     if not book.has_media('mp3'):
         raise Http404
 
     audiobooks, projects, have_oggs = get_audiobooks(book)
 
-    extra_info = book.extra_info
-
-    return render_to_response('catalogue/player.html', locals(), context_instance=RequestContext(request))
+    return render_to_response('catalogue/player.html', {
+        'book': book,
+        'audiobook': '',
+        'audiobooks': audiobooks,
+        'projects': projects,
+    }, context_instance=RequestContext(request))
 
 
 def book_text(request, slug):
-    book = get_object_or_404(models.Book, slug=slug)
+    book = get_object_or_404(Book, slug=slug)
 
     if not book.has_html_file():
         raise Http404
-    return render_to_response('catalogue/book_text.html', locals(), context_instance=RequestContext(request))
+    return render_to_response('catalogue/book_text.html', {'book': book}, context_instance=RequestContext(request))
 
 
 # ==========
@@ -389,11 +420,11 @@ _apps = (
 def _tags_starting_with(prefix, user=None):
     prefix = prefix.lower()
     # PD counter
-    book_stubs = pdcounter_models.BookStub.objects.filter(_word_starts_with('title', prefix))
-    authors = pdcounter_models.Author.objects.filter(_word_starts_with('name', prefix))
+    book_stubs = BookStub.objects.filter(_word_starts_with('title', prefix))
+    authors = Author.objects.filter(_word_starts_with('name', prefix))
 
-    books = models.Book.objects.filter(_word_starts_with('title', prefix))
-    tags = models.Tag.objects.filter(_word_starts_with('name', prefix))
+    books = Book.objects.filter(_word_starts_with('title', prefix))
+    tags = Tag.objects.filter(_word_starts_with('name', prefix))
     if user and user.is_authenticated():
         tags = tags.filter(~Q(category='set') | Q(user=user))
     else:
@@ -405,7 +436,7 @@ def _tags_starting_with(prefix, user=None):
 
 
 def _get_result_link(match, tag_list):
-    if isinstance(match, models.Tag):
+    if isinstance(match, Tag):
         return reverse('catalogue.views.tagged_object_list',
                        kwargs={'tags': '/'.join(tag.url_chunk for tag in tag_list + [match])})
     elif isinstance(match, App):
@@ -415,7 +446,7 @@ def _get_result_link(match, tag_list):
 
 
 def _get_result_type(match):
-    if isinstance(match, models.Book) or isinstance(match, pdcounter_models.BookStub):
+    if isinstance(match, Book) or isinstance(match, BookStub):
         match_type = 'book'
     else:
         match_type = match.category
@@ -424,11 +455,11 @@ def _get_result_type(match):
 
 def books_starting_with(prefix):
     prefix = prefix.lower()
-    return models.Book.objects.filter(_word_starts_with('title', prefix))
+    return Book.objects.filter(_word_starts_with('title', prefix))
 
 
 def find_best_matches(query, user=None):
-    """ Finds a models.Book, Tag, models.BookStub or Author best matching a query.
+    """ Finds a Book, Tag, BookStub or Author best matching a query.
 
     Returns a with:
       - zero elements when nothing is found,
@@ -445,12 +476,12 @@ def find_best_matches(query, user=None):
     result = tuple(_tags_starting_with(query, user))
     # remove pdcounter stuff
     book_titles = set(match.pretty_title().lower() for match in result
-                      if isinstance(match, models.Book))
+                      if isinstance(match, Book))
     authors = set(match.name.lower() for match in result
-                  if isinstance(match, models.Tag) and match.category == 'author')
+                  if isinstance(match, Tag) and match.category == 'author')
     result = tuple(res for res in result if not (
-                 (isinstance(res, pdcounter_models.BookStub) and res.pretty_title().lower() in book_titles) or
-                 (isinstance(res, pdcounter_models.Author) and res.name.lower() in authors)
+                 (isinstance(res, BookStub) and res.pretty_title().lower() in book_titles) or
+                 (isinstance(res, Author) and res.name.lower() in authors)
              ))
 
     exact_matches = tuple(res for res in result if res.name.lower() == query)
@@ -465,8 +496,8 @@ def search(request):
     prefix = request.GET.get('q', '')
 
     try:
-        tag_list = models.Tag.get_tag_list(tags)
-    except (models.Tag.DoesNotExist, models.Tag.MultipleObjectsReturned, models.Tag.UrlDeprecationWarning):
+        tag_list = Tag.get_tag_list(tags)
+    except (Tag.DoesNotExist, Tag.MultipleObjectsReturned, Tag.UrlDeprecationWarning):
         tag_list = []
 
     try:
@@ -558,22 +589,22 @@ def import_book(request):
 # info views for API
 
 def book_info(request, book_id, lang='pl'):
-    book = get_object_or_404(models.Book, id=book_id)
+    book = get_object_or_404(Book, id=book_id)
     # set language by hand
     translation.activate(lang)
-    return render_to_response('catalogue/book_info.html', locals(), context_instance=RequestContext(request))
+    return render_to_response('catalogue/book_info.html', {'book': book}, context_instance=RequestContext(request))
 
 
 def tag_info(request, tag_id):
-    tag = get_object_or_404(models.Tag, id=tag_id)
+    tag = get_object_or_404(Tag, id=tag_id)
     return HttpResponse(tag.description)
 
 
 def download_zip(request, format, slug=None):
-    if format in models.Book.ebook_formats:
-        url = models.Book.zip_format(format)
+    if format in Book.ebook_formats:
+        url = Book.zip_format(format)
     elif format in ('mp3', 'ogg') and slug is not None:
-        book = get_object_or_404(models.Book, slug=slug)
+        book = get_object_or_404(Book, slug=slug)
         url = book.zip_audiobooks(format)
     else:
         raise Http404('No format specified for zip package')
@@ -596,7 +627,7 @@ class CustomPDFFormView(AjaxableFormView):
         return (obj,), {}
 
     def get_object(self, request, slug, *args, **kwargs):
-        return get_object_or_404(models.Book, slug=slug)
+        return get_object_or_404(Book, slug=slug)
 
     def context_description(self, request, obj):
         return obj.pretty_title()
@@ -609,10 +640,10 @@ class CustomPDFFormView(AjaxableFormView):
 
 @ssi_included
 def book_mini(request, pk, with_link=True):
-    # book = get_object_or_404(models.Book, pk=pk)
+    # book = get_object_or_404(Book, pk=pk)
     try:
-        book = models.Book.objects.only('cover_thumb', 'title', 'language', 'slug').get(pk=pk)
-    except models.Book.DoesNotExist:
+        book = Book.objects.only('cover_thumb', 'title', 'language', 'slug').get(pk=pk)
+    except Book.DoesNotExist:
         raise Http404
     return render(request, 'catalogue/book_mini_box.html', {
         'book': book,
@@ -626,7 +657,7 @@ def book_mini(request, pk, with_link=True):
         ('social_tags.book_shelf_tags', (ipk,)),
     ))(ssi_expect(pk, int)))
 def book_short(request, pk):
-    book = get_object_or_404(models.Book, pk=pk)
+    book = get_object_or_404(Book, pk=pk)
     stage_note, stage_note_url = book.stage_note()
     audiobooks, projects, have_oggs = get_audiobooks(book)
 
@@ -649,10 +680,10 @@ def book_short(request, pk):
     (lambda ipk: (
         ('social_tags.choose_cite', [ipk]),
         ('catalogue_tags.choose_fragment', [ipk], {
-            'unless': V('social_tags.choose_cite', [ipk])}),
+            'unless': Var('social_tags.choose_cite', [ipk])}),
     ))(ssi_expect(pk, int)))
 def book_wide(request, pk):
-    book = get_object_or_404(models.Book, pk=pk)
+    book = get_object_or_404(Book, pk=pk)
     stage_note, stage_note_url = book.stage_note()
     extra_info = book.extra_info
     audiobooks, projects, have_oggs = get_audiobooks(book)
@@ -676,19 +707,19 @@ def book_wide(request, pk):
 
 @ssi_included
 def fragment_short(request, pk):
-    fragment = get_object_or_404(models.Fragment, pk=pk)
+    fragment = get_object_or_404(Fragment, pk=pk)
     return render(request, 'catalogue/fragment_short.html', {'fragment': fragment})
 
 
 @ssi_included
 def fragment_promo(request, pk):
-    fragment = get_object_or_404(models.Fragment, pk=pk)
+    fragment = get_object_or_404(Fragment, pk=pk)
     return render(request, 'catalogue/fragment_promo.html', {'fragment': fragment})
 
 
 @ssi_included
 def tag_box(request, pk):
-    tag = get_object_or_404(models.Tag, pk=pk)
+    tag = get_object_or_404(Tag, pk=pk)
     assert tag.category != 'set'
 
     return render(request, 'catalogue/tag_box.html', {
@@ -698,7 +729,7 @@ def tag_box(request, pk):
 
 @ssi_included
 def collection_box(request, pk):
-    obj = get_object_or_404(models.Collection, pk=pk)
+    obj = get_object_or_404(Collection, pk=pk)
 
     return render(request, 'catalogue/collection_box.html', {
         'obj': obj,
@@ -707,8 +738,8 @@ def collection_box(request, pk):
 
 def tag_catalogue(request, category):
     if category == 'theme':
-        tags = models.Tag.objects.usage_for_model(
-            models.Fragment, counts=True).filter(category='theme')
+        tags = Tag.objects.usage_for_model(
+            Fragment, counts=True).filter(category='theme')
     else:
         tags = list(get_top_level_related_tags((), categories=(category,)))
 
@@ -724,11 +755,12 @@ def tag_catalogue(request, category):
         'best': best,
         'title': constants.CATEGORIES_NAME_PLURAL[category],
         'whole_category': constants.WHOLE_CATEGORY[category],
+        'active_menu_item': 'theme' if category == 'theme' else None,
     })
 
 
 def collections(request):
-    objects = models.Collection.objects.all()
+    objects = Collection.objects.all()
 
     if len(objects) > 3:
         best = random.sample(objects, 3)
index 443fbda..4d1731f 100644 (file)
@@ -19,6 +19,10 @@ def infopage(request, slug):
     try:
         right_column = Template(page.right_column).render(rc)
     except TemplateSyntaxError:
-        left_column = ''
+        right_column = ''
 
-    return render_to_response('infopages/infopage.html', locals(), context_instance=RequestContext(request))
+    return render_to_response('infopages/infopage.html', {
+        'page': page,
+        'left_column': left_column,
+        'right_columns': right_column,
+    }, context_instance=RequestContext(request))
index e94680c..12e528a 100644 (file)
@@ -7,7 +7,7 @@ from django.utils.translation import ugettext as _
 from django.views.generic import ListView
 
 
-def tagged_object_list(request, queryset_or_model=None, tag_model=None, tags=None, related_tags=False,
+def tagged_object_list(request, queryset_or_model, tag_model, tags, related_tags=False,
                        related_tag_counts=True, **kwargs):
     """
     A thin wrapper around
@@ -25,13 +25,6 @@ def tagged_object_list(request, queryset_or_model=None, tag_model=None, tags=Non
     tag will have a ``count`` attribute indicating the number of items
     which have it in addition to the given tag.
     """
-    # Check attributes
-    if queryset_or_model is None:
-        raise AttributeError(_('tagged_object_list must be called with a queryset or a model.'))
-    if tag_model is None:
-        raise AttributeError(_('tagged_object_list must be called with a tag model.'))
-    if tags is None:
-        raise AttributeError(_('tagged_object_list must be called with a tag.'))
 
     tag_instances = tag_model.get_tag_list(tags)
     if tag_instances is None:
index eee6bfc..b8a685e 100644 (file)
@@ -15,10 +15,16 @@ def book_stub_detail(request, slug):
     book = get_object_or_404(models.BookStub, slug=slug)
     if book.pd and not book.in_pd():
         pd_counter = datetime(book.pd, 1, 1)
+    else:
+        pd_counter = None
 
     form = PublishingSuggestForm(initial={"books": u"%s — %s, \n" % (book.author, book.title)})
 
-    return render_to_response('pdcounter/book_stub_detail.html', locals(), context_instance=RequestContext(request))
+    return render_to_response('pdcounter/book_stub_detail.html', {
+        'book': book,
+        'pd_counter': pd_counter,
+        'form': form,
+    }, context_instance=RequestContext(request))
 
 
 @cache.never_cache
@@ -26,7 +32,13 @@ def author_detail(request, slug):
     author = get_object_or_404(models.Author, slug=slug)
     if not author.alive():
         pd_counter = datetime(author.goes_to_pd(), 1, 1)
+    else:
+        pd_counter = None
 
     form = PublishingSuggestForm(initial={"books": author.name + ", \n"})
 
-    return render_to_response('pdcounter/author_detail.html', locals(), context_instance=RequestContext(request))
+    return render_to_response('pdcounter/author_detail.html', {
+        'author': author,
+        'pd_counter': pd_counter,
+        'form': form,
+    }, context_instance=RequestContext(request))
index b39661f..e10d2fa 100644 (file)
@@ -101,7 +101,7 @@ class Picture(models.Model):
         pass
 
     class Meta:
-        ordering = ('sort_key',)
+        ordering = ('sort_key_author', 'sort_key')
 
         verbose_name = _('picture')
         verbose_name_plural = _('pictures')
@@ -127,8 +127,11 @@ class Picture(models.Model):
     def authors(self):
         return self.tags.filter(category='author')
 
+    def tag_unicode(self, category):
+        return ", ".join(self.tags.filter(category=category).values_list('name', flat=True))
+
     def author_unicode(self):
-        return ", ".join(self.authors().values_list('name', flat=True))
+        return self.tag_unicode('author')
 
     @permalink
     def get_absolute_url(self):
index adfc25e..a38efa5 100644 (file)
 
     <div class='clearboth'></div>
 
-
-
-{% work_list book_list %}
-
-
-
+{% work_list picture_list %}
 
 {% endblock %}
index 462e8b0..00260f5 100644 (file)
   <div class="other-tools">
     <h2 class="mono">{% trans "See" %}</h2>
     <ul class="plain">
-      {% if extra_info.source_url %}
-      <li><a href="{{ extra_info.source_url }}">{% trans "Source" %}</a> {% trans "of the picture" %}</li>
+      {% if picture.extra_info.source_url %}
+      <li><a href="{{ picture.extra_info.source_url }}">{% trans "Source" %}</a> {% trans "of the picture" %}</li>
       {% endif %}
       <li><a href="{{ picture.xml_file.url }}">{% trans "Source XML file" %}</a></li>
-      {% if extra_info.about and not hide_about %}
-      <li>{% trans "Picture on" %} <a href="{{ extra_info.about }}">{% trans "Editor's Platform" %}</a></li>
+      {% if picture.extra_info.about and not hide_about %}
+      <li>{% trans "Picture on" %} <a href="{{ picture.extra_info.about }}">{% trans "Editor's Platform" %}</a></li>
       {% endif %}
       {% if picture.wiki_link %}
       <li><a href="{{ picture.wiki_link }}">{% trans "Picture description on Wikipedia" %}</a></li>
index 6b83c35..5de38b2 100644 (file)
@@ -2,6 +2,7 @@
 # This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
 #
+from django.conf import settings
 from django.contrib.auth.decorators import permission_required
 from django.shortcuts import render_to_response, get_object_or_404, render
 from django.template import RequestContext
@@ -26,18 +27,17 @@ from sponsors.models import Sponsor
 #             books_nav.setdefault(tag.sort_key[0], []).append(tag)
 #
 #     return render_to_response(template_name, locals(), context_instance=RequestContext(request))
+from wolnelektury.utils import ajax
 
 
 def picture_list_thumb(request, filter=None, get_filter=None, template_name='picture/picture_list_thumb.html',
                        cache_key=None, context=None):
-    book_list = Picture.objects.all()
+    pictures = Picture.objects.all()
     if filter:
-        book_list = book_list.filter(filter)
+        pictures = pictures.filter(filter)
     if get_filter:
-        book_list = book_list.filter(get_filter())
-    book_list = book_list.order_by('sort_key_author')
-    book_list = list(book_list)
-    return render_to_response(template_name, locals(), context_instance=RequestContext(request))
+        pictures = pictures.filter(get_filter())
+    return render_to_response(template_name, {'book_list': list(pictures)}, context_instance=RequestContext(request))
 
 
 def picture_detail(request, slug):
@@ -49,13 +49,11 @@ def picture_detail(request, slug):
     # for tag in picture.tags.iterator():
     #     categories.setdefault(tag.category, []).append(tag)
 
-    themes = theme_things.get('theme', [])
-    things = theme_things.get('thing', [])
-
-    extra_info = picture.extra_info
-
-    return render_to_response("picture/picture_detail.html", locals(),
-                              context_instance=RequestContext(request))
+    return render_to_response("picture/picture_detail.html", {
+        'picture': picture,
+        'themes': theme_things.get('theme', []),
+        'things': theme_things.get('thing', []),
+    }, context_instance=RequestContext(request))
 
 
 def picture_viewer(request, slug):
@@ -65,8 +63,37 @@ def picture_viewer(request, slug):
         have_sponsors = Sponsor.objects.filter(name=sponsor)
         if have_sponsors.exists():
             sponsors.append(have_sponsors[0])
-    return render_to_response("picture/picture_viewer.html", locals(),
-                              context_instance=RequestContext(request))
+    return render_to_response("picture/picture_viewer.html", {
+        'picture': picture,
+        'sponsors': sponsors,
+    }, context_instance=RequestContext(request))
+
+
+@ajax(method='get')
+def picture_page(request, key=None):
+    pictures = Picture.objects.order_by('-id')
+    if key is not None:
+        pictures = pictures.filter(id__lt=key)
+    pictures = pictures[:settings.PICTURE_PAGE_SIZE]
+    picture_data = [
+        {
+            'id': picture.id,
+            'title': picture.title,
+            'author': picture.author_unicode(),
+            'epoch': picture.tag_unicode('epoch'),
+            'kind': picture.tag_unicode('kind'),
+            'genre': picture.tag_unicode('genre'),
+            'style': picture.extra_info['style'],
+            'image_url': picture.image_file.url,
+            'width': picture.width,
+            'height': picture.height,
+        }
+        for picture in pictures
+    ]
+    return {
+        'pictures': picture_data,
+        'count': Picture.objects.count(),
+    }
 
 
 # =========
@@ -122,8 +149,10 @@ def picture_short(request, pk):
 @ssi_included
 def picturearea_short(request, pk):
     area = get_object_or_404(PictureArea, pk=pk)
-    theme = area.tags.filter(category='theme')
-    theme = theme and theme[0] or None
-    thing = area.tags.filter(category='thing')
-    thing = thing and thing[0] or None
-    return render(request, 'picture/picturearea_short.html', locals())
+    themes = area.tags.filter(category='theme')
+    things = area.tags.filter(category='thing')
+    return render(request, 'picture/picturearea_short.html', {
+        'area': area,
+        'theme': themes[0] if themes else None,
+        'thing': things[0] if things else None,
+    })
index d17d212..bc0dee6 100644 (file)
@@ -16,7 +16,6 @@ from reporting.utils import render_to_pdf, render_to_csv, generated_file_view
 
 @staff_member_required
 def stats_page(request):
-    media = BookMedia.objects.count()
     media_types = BookMedia.objects.values('type').annotate(count=Count('type')).order_by('type')
     for mt in media_types:
         mt['size'] = sum(b.file.size for b in BookMedia.objects.filter(type=mt['type']).iterator())
@@ -27,18 +26,25 @@ def stats_page(request):
         else:
             mt['deprecated'] = '-'
 
-    licenses = set((
+    licenses = set(
         (b.extra_info.get('license'), b.extra_info.get('license_description'))
-        for b in Book.objects.all().iterator() if b.extra_info.get('license')))
+        for b in Book.objects.all().iterator() if b.extra_info.get('license'))
 
-    return render_to_response('reporting/main.html', locals(), context_instance=RequestContext(request))
+    return render_to_response('reporting/main.html', {
+        'media_types': media_types,
+        'licenses': licenses,
+    }, context_instance=RequestContext(request))
 
 
 @generated_file_view('reports/katalog.pdf', 'application/pdf',
                      send_name=lambda: 'wolnelektury_%s.pdf' % date.today(), signals=[Book.published])
 def catalogue_pdf(path):
     books_by_author, orphans, books_by_parent = Book.book_list()
-    render_to_pdf(path, 'reporting/catalogue.texml', locals(), {
+    render_to_pdf(path, 'reporting/catalogue.texml', {
+        'books_by_author': books_by_author,
+        'orphans': orphans,
+        'book_by_parent': books_by_parent,
+    }, {
         "wl-logo.png": os.path.join(settings.STATIC_ROOT, "img/logo-big.png"),
     })
 
@@ -47,4 +53,8 @@ def catalogue_pdf(path):
                      send_name=lambda: 'wolnelektury_%s.csv' % date.today(), signals=[Book.published])
 def catalogue_csv(path):
     books_by_author, orphans, books_by_parent = Book.book_list()
-    render_to_csv(path, 'reporting/catalogue.csv', locals())
+    render_to_csv(path, 'reporting/catalogue.csv', {
+        'books_by_author': books_by_author,
+        'orphans': orphans,
+        'book_by_parent': books_by_parent,
+    })
index c89878a..63a4227 100755 (executable)
@@ -69,6 +69,9 @@ def set_sets(user, work, sets):
     # delete empty tags
     Tag.objects.filter(category='set', user=user, items=None).delete()
 
+    if isinstance(work, Book):
+        work.update_popularity()
+
 
 def cites_for_tags(tags):
     """Returns a QuerySet with all Cites for books with given tags."""
index 49c9b70..15f01bf 100644 (file)
@@ -38,8 +38,9 @@ def like_book(request, slug):
 
 @login_required
 def my_shelf(request):
-    books = Book.tagged.with_any(request.user.tag_set.all())
-    return render(request, 'social/my_shelf.html', locals())
+    return render(request, 'social/my_shelf.html', {
+        'books': Book.tagged.with_any(request.user.tag_set.all())
+    })
 
 
 class ObjectSetsFormView(AjaxableFormView):
index b951cc4..836534e 100644 (file)
@@ -14,11 +14,15 @@ from django.views.decorators.cache import never_cache
 def wait(request, path):
     if WaitedFile.exists(path):
         file_url = join(WAITER_URL, path)
+        waiting = None
     else:
-        file_url = ""
+        file_url = None
         waiting = get_object_or_404(WaitedFile, path=path)
 
     if request.is_ajax():
         return HttpResponse(file_url)
     else:
-        return render(request, "waiter/wait.html", locals())
+        return render(request, "waiter/wait.html", {
+            'waiting': waiting,
+            'file_url': file_url,
+        })
index 09cc641..acea28b 100644 (file)
@@ -26,3 +26,5 @@ LATEST_BLOG_POSTS = "http://nowoczesnapolska.org.pl/feed/?cat=-135"
 CATALOGUE_COUNTERS_FILE = os.path.join(VAR_DIR, 'catalogue_counters.p')
 
 CATALOGUE_MIN_INITIALS = 60
+
+PICTURE_PAGE_SIZE = 20
diff --git a/src/wolnelektury/static/fonts/cousine/v10/0IpceuvDvCegpU9Mz8MQ_g.woff2 b/src/wolnelektury/static/fonts/cousine/v10/0IpceuvDvCegpU9Mz8MQ_g.woff2
new file mode 100644 (file)
index 0000000..f8b755d
Binary files /dev/null and b/src/wolnelektury/static/fonts/cousine/v10/0IpceuvDvCegpU9Mz8MQ_g.woff2 differ
diff --git a/src/wolnelektury/static/fonts/cousine/v10/0VdvlOBfHobA4eW-NVYk-VtXRa8TVwTICgirnJhmVJw.woff2 b/src/wolnelektury/static/fonts/cousine/v10/0VdvlOBfHobA4eW-NVYk-VtXRa8TVwTICgirnJhmVJw.woff2
new file mode 100644 (file)
index 0000000..332cb4d
Binary files /dev/null and b/src/wolnelektury/static/fonts/cousine/v10/0VdvlOBfHobA4eW-NVYk-VtXRa8TVwTICgirnJhmVJw.woff2 differ
diff --git a/src/wolnelektury/static/fonts/cousine/v10/4OFqQVT4CccEiwAQMKv0eiEAvth_LlrfE80CYdSH47w.woff2 b/src/wolnelektury/static/fonts/cousine/v10/4OFqQVT4CccEiwAQMKv0eiEAvth_LlrfE80CYdSH47w.woff2
new file mode 100644 (file)
index 0000000..a28674f
Binary files /dev/null and b/src/wolnelektury/static/fonts/cousine/v10/4OFqQVT4CccEiwAQMKv0eiEAvth_LlrfE80CYdSH47w.woff2 differ
diff --git a/src/wolnelektury/static/fonts/cousine/v10/Bt5Lz7Saa5a5RtsafP9xmfY6323mHUZFJMgTvxaG2iE.woff2 b/src/wolnelektury/static/fonts/cousine/v10/Bt5Lz7Saa5a5RtsafP9xmfY6323mHUZFJMgTvxaG2iE.woff2
new file mode 100644 (file)
index 0000000..0fa7222
Binary files /dev/null and b/src/wolnelektury/static/fonts/cousine/v10/Bt5Lz7Saa5a5RtsafP9xmfY6323mHUZFJMgTvxaG2iE.woff2 differ
diff --git a/src/wolnelektury/static/fonts/cousine/v10/IYQIfrNvkAhlEkaWqzgTm_Y6323mHUZFJMgTvxaG2iE.woff2 b/src/wolnelektury/static/fonts/cousine/v10/IYQIfrNvkAhlEkaWqzgTm_Y6323mHUZFJMgTvxaG2iE.woff2
new file mode 100644 (file)
index 0000000..f6ecbfb
Binary files /dev/null and b/src/wolnelektury/static/fonts/cousine/v10/IYQIfrNvkAhlEkaWqzgTm_Y6323mHUZFJMgTvxaG2iE.woff2 differ
diff --git a/src/wolnelektury/static/fonts/cousine/v10/JHq2r7PZMuzGPtEvdlprZCEAvth_LlrfE80CYdSH47w.woff2 b/src/wolnelektury/static/fonts/cousine/v10/JHq2r7PZMuzGPtEvdlprZCEAvth_LlrfE80CYdSH47w.woff2
new file mode 100644 (file)
index 0000000..6c954b1
Binary files /dev/null and b/src/wolnelektury/static/fonts/cousine/v10/JHq2r7PZMuzGPtEvdlprZCEAvth_LlrfE80CYdSH47w.woff2 differ
diff --git a/src/wolnelektury/static/fonts/cousine/v10/XkYjaL8YjqL9qW8o0T14ufk_vArhqVIZ0nv9q090hN8.woff2 b/src/wolnelektury/static/fonts/cousine/v10/XkYjaL8YjqL9qW8o0T14ufk_vArhqVIZ0nv9q090hN8.woff2
new file mode 100644 (file)
index 0000000..843326b
Binary files /dev/null and b/src/wolnelektury/static/fonts/cousine/v10/XkYjaL8YjqL9qW8o0T14ufk_vArhqVIZ0nv9q090hN8.woff2 differ
diff --git a/src/wolnelektury/static/fonts/cousine/v10/aWxnkwXsEJCSg-HKkPTCkBJtnKITppOI_IvcXXDNrsc.woff2 b/src/wolnelektury/static/fonts/cousine/v10/aWxnkwXsEJCSg-HKkPTCkBJtnKITppOI_IvcXXDNrsc.woff2
new file mode 100644 (file)
index 0000000..a224891
Binary files /dev/null and b/src/wolnelektury/static/fonts/cousine/v10/aWxnkwXsEJCSg-HKkPTCkBJtnKITppOI_IvcXXDNrsc.woff2 differ
diff --git a/src/wolnelektury/static/fonts/cousine/v10/auteuWVL5GjOhekECAkC_SEAvth_LlrfE80CYdSH47w.woff2 b/src/wolnelektury/static/fonts/cousine/v10/auteuWVL5GjOhekECAkC_SEAvth_LlrfE80CYdSH47w.woff2
new file mode 100644 (file)
index 0000000..8b6e83a
Binary files /dev/null and b/src/wolnelektury/static/fonts/cousine/v10/auteuWVL5GjOhekECAkC_SEAvth_LlrfE80CYdSH47w.woff2 differ
diff --git a/src/wolnelektury/static/fonts/cousine/v10/bS4Fjp2fQa3pRrzTYB6CXhJtnKITppOI_IvcXXDNrsc.woff2 b/src/wolnelektury/static/fonts/cousine/v10/bS4Fjp2fQa3pRrzTYB6CXhJtnKITppOI_IvcXXDNrsc.woff2
new file mode 100644 (file)
index 0000000..0e94d0a
Binary files /dev/null and b/src/wolnelektury/static/fonts/cousine/v10/bS4Fjp2fQa3pRrzTYB6CXhJtnKITppOI_IvcXXDNrsc.woff2 differ
diff --git a/src/wolnelektury/static/fonts/cousine/v10/qjsoqLzZoDyy_opKVvy-uvY6323mHUZFJMgTvxaG2iE.woff2 b/src/wolnelektury/static/fonts/cousine/v10/qjsoqLzZoDyy_opKVvy-uvY6323mHUZFJMgTvxaG2iE.woff2
new file mode 100644 (file)
index 0000000..7169258
Binary files /dev/null and b/src/wolnelektury/static/fonts/cousine/v10/qjsoqLzZoDyy_opKVvy-uvY6323mHUZFJMgTvxaG2iE.woff2 differ
diff --git a/src/wolnelektury/static/fonts/cousine/v10/svY-0AsqnhB5pNHScPRpHhJtnKITppOI_IvcXXDNrsc.woff2 b/src/wolnelektury/static/fonts/cousine/v10/svY-0AsqnhB5pNHScPRpHhJtnKITppOI_IvcXXDNrsc.woff2
new file mode 100644 (file)
index 0000000..edf7a5a
Binary files /dev/null and b/src/wolnelektury/static/fonts/cousine/v10/svY-0AsqnhB5pNHScPRpHhJtnKITppOI_IvcXXDNrsc.woff2 differ
diff --git a/src/wolnelektury/static/fonts/cousine/v10/y_AZ5Sz-FwL1lux2xLSTZT0LW-43aMEzIO6XUTLjad8.woff2 b/src/wolnelektury/static/fonts/cousine/v10/y_AZ5Sz-FwL1lux2xLSTZT0LW-43aMEzIO6XUTLjad8.woff2
new file mode 100644 (file)
index 0000000..c1c1d84
Binary files /dev/null and b/src/wolnelektury/static/fonts/cousine/v10/y_AZ5Sz-FwL1lux2xLSTZT0LW-43aMEzIO6XUTLjad8.woff2 differ
diff --git a/src/wolnelektury/static/fonts/cousine/v10/y_AZ5Sz-FwL1lux2xLSTZZX5f-9o1vgP2EXwfjgl7AY.woff2 b/src/wolnelektury/static/fonts/cousine/v10/y_AZ5Sz-FwL1lux2xLSTZZX5f-9o1vgP2EXwfjgl7AY.woff2
new file mode 100644 (file)
index 0000000..96d5907
Binary files /dev/null and b/src/wolnelektury/static/fonts/cousine/v10/y_AZ5Sz-FwL1lux2xLSTZZX5f-9o1vgP2EXwfjgl7AY.woff2 differ
diff --git a/src/wolnelektury/static/fonts/cousine/v10/y_AZ5Sz-FwL1lux2xLSTZa-j2U0lmluP9RWlSytm3ho.woff2 b/src/wolnelektury/static/fonts/cousine/v10/y_AZ5Sz-FwL1lux2xLSTZa-j2U0lmluP9RWlSytm3ho.woff2
new file mode 100644 (file)
index 0000000..6a3ad1a
Binary files /dev/null and b/src/wolnelektury/static/fonts/cousine/v10/y_AZ5Sz-FwL1lux2xLSTZa-j2U0lmluP9RWlSytm3ho.woff2 differ
diff --git a/src/wolnelektury/static/fonts/cousine/v10/y_AZ5Sz-FwL1lux2xLSTZegdm0LZdjqr5-oayXSOefg.woff2 b/src/wolnelektury/static/fonts/cousine/v10/y_AZ5Sz-FwL1lux2xLSTZegdm0LZdjqr5-oayXSOefg.woff2
new file mode 100644 (file)
index 0000000..3f07baa
Binary files /dev/null and b/src/wolnelektury/static/fonts/cousine/v10/y_AZ5Sz-FwL1lux2xLSTZegdm0LZdjqr5-oayXSOefg.woff2 differ
diff --git a/src/wolnelektury/static/img/read-white.png b/src/wolnelektury/static/img/read-white.png
new file mode 100644 (file)
index 0000000..7db8916
Binary files /dev/null and b/src/wolnelektury/static/img/read-white.png differ
diff --git a/src/wolnelektury/static/img/read-white.svg b/src/wolnelektury/static/img/read-white.svg
new file mode 100644 (file)
index 0000000..294d8c7
--- /dev/null
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   version="1.1"
+   width="13.0625"
+   height="9.0397387"
+   id="svg6288"
+   inkscape:version="0.48.4 r9939"
+   sodipodi:docname="read-white.svg"
+   inkscape:export-filename="/home/janek/src/wolnelektury/src/wolnelektury/static/img/read-white.png"
+   inkscape:export-xdpi="90"
+   inkscape:export-ydpi="90">
+  <sodipodi:namedview
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1"
+     objecttolerance="10"
+     gridtolerance="10"
+     guidetolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:window-width="1366"
+     inkscape:window-height="692"
+     id="namedview8"
+     showgrid="false"
+     inkscape:zoom="11.271642"
+     inkscape:cx="6.426905"
+     inkscape:cy="10.355676"
+     inkscape:window-x="0"
+     inkscape:window-y="24"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="svg6288"
+     fit-margin-top="0"
+     fit-margin-left="0"
+     fit-margin-right="0"
+     fit-margin-bottom="0" />
+  <defs
+     id="defs6290" />
+  <metadata
+     id="metadata6293">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     transform="translate(-258.99199,-652.55746)"
+     id="layer1"
+     style="fill:#ffffff">
+    <g
+       transform="translate(-138.76939,118.84837)"
+       id="layer1-3"
+       style="fill:#ffffff;fill-opacity:1">
+      <path
+         id="path3144"
+         style="fill:#ffffff;fill-opacity:1;stroke:none"
+         d="m 404.26138,536.71758 c -0.82987,0 -1.5,0.67013 -1.5,1.5 0,0.82986 0.67013,1.5 1.5,1.5 0.82986,0 1.5,-0.67014 1.5,-1.5 0,-0.82987 -0.67014,-1.5 -1.5,-1.5 z m 0.0312,-0.96875 c 1.37294,0 2.5,1.12096 2.5,2.5 0,1.37904 -1.12706,2.46875 -2.5,2.46875 -1.37294,0 -2.46875,-1.08971 -2.46875,-2.46875 0,-1.37904 1.09581,-2.5 2.46875,-2.5 z m -0.0312,-1.03125 c -2.5018,0 -3.50211,1.55979 -5.46875,3.53125 2.03294,2.05986 2.96695,3.4375 5.46875,3.4375 2.5018,0 3.46707,-1.31135 5.5,-3.4375 -2.03293,-2.05985 -2.9982,-3.53125 -5.5,-3.53125 z m -0.46875,-1 c 0.15292,-0.0191 0.31239,0 0.46875,0 2.5018,0 4.52957,2.4714 6.5625,4.53125 -2.03293,2.12615 -4.0607,4.5 -6.5625,4.5 -2.5018,0 -4.46706,-2.44014 -6.5,-4.5 1.84373,-1.86896 3.73739,-4.2453 6.03125,-4.53125 z"
+         inkscape:connector-curvature="0" />
+    </g>
+  </g>
+</svg>
diff --git a/src/wolnelektury/static/img/wiatrak.jpg b/src/wolnelektury/static/img/wiatrak.jpg
new file mode 100644 (file)
index 0000000..e96a0c1
Binary files /dev/null and b/src/wolnelektury/static/img/wiatrak.jpg differ
diff --git a/src/wolnelektury/static/img/wiatrak.png b/src/wolnelektury/static/img/wiatrak.png
deleted file mode 100644 (file)
index e96a0c1..0000000
Binary files a/src/wolnelektury/static/img/wiatrak.png and /dev/null differ
index 5283468..eee31f9 100644 (file)
@@ -22,9 +22,9 @@
                         if (button && short_text) button.html(short_text);
                     }
                     return false;
-                }
+                };
                 return toggle;
-            }
+            };
             if (long_el.html().length <= short_el.html().length)
                 return;
 
@@ -209,15 +209,15 @@ $('#themes-list-toggle').click(function(event) {
                 onFormat: function (type) {
                     switch (type) {
                         case 'block': // n and c
-                            return ' <a href="#"' + (this.value == this.page ? ' class="current"' : '') + '>' + this.value + '</a> ';
+                            return ' <li><a href="#"' + (this.value == this.page ? ' class="current"' : '') + '>' + this.value + '</a></li>';
                         case 'next': // >
-                            return ' <a href="#">&gt;</a> ';
+                            return '<li><a href="#">&rsaquo;</a></li>';
                         case 'prev': // <
-                            return ' <a href="#">&lt;</a> ';
+                            return '<li><a href="#">&lsaquo;</a></li>';
                         case 'first': // [
-                            return '<a href="#">«</a> ';
+                            return '<li><a href="#">&laquo;</a></li>';
                         case 'last': // ]
-                            return ' <a href="#">»</a>';
+                            return '<li><a href="#">&raquo;</a></li>';
                     }
                 }
             });
index bab1fb4..9f0701f 100644 (file)
@@ -8,6 +8,7 @@
 @import "main/catalogue";
 @import "main/cite";
 @import "main/dialogs";
+@import "main/fonts";
 @import "main/footer";
 @import "main/form";
 @import "main/fragment";
@@ -17,6 +18,8 @@
 @import "main/main_page";
 @import "main/menu";
 @import "main/picture_box";
+/* import "main/picture_slider";*/
+/*TODO:ogarnac nazwe/lokalizcje scss dla gallery slider*/
 @import "main/search";
 @import "main/tag";
 
index 210fd44..48c63fe 100755 (executable)
 /* Basic layout */
 html, body {
-    margin: 0;
-    padding: 0;
+  margin: 0;
+  padding: 0;
 }
 
-
 .clearboth {
-    clear: both;
+  clear: both;
 }
 
-
-
 /* Basic colors and fonts */
 body {
-    font-family: Georgia;
-    background: #f7f7f7;
-    color: black;
+  font-family: Georgia;
+  background: #f7f7f7;
+  color: black;
 
-    @include size(font-size, 13px);
+  @include size(font-size, 13px);
 }
 
 a {
-    color: #0d7e85;
-    text-decoration: none;
+  color: #0d7e85;
+  text-decoration: none;
 
-    img {
-        border: 0;
-    }
+  img {
+    border: 0;
+  }
 }
 
 h1 {
-    @include size(font-size, 35px);
-    font-weight: normal;
-    @include size(margin-top, 14px);
+  @include size(font-size, 35px);
+  font-weight: normal;
+  @include size(margin-top, 14px);
 
-    a {
-        color: inherit;
-    }
+  a {
+    color: inherit;
+  }
 }
 
 h2 {
-    @include size(font-size, 20px);
-    font-weight: normal;
+  @include size(font-size, 20px);
+  font-weight: normal;
 }
 
 h3 {
-    @include size(font-size, 15px);
-    font-weight: normal;
+  @include size(font-size, 15px);
+  font-weight: normal;
 }
 
-
 .normal-text {
-    line-height: 1.3em;
-    @include size(margin, 0 5px);
+  line-height: 1.3em;
+  @include size(margin, 0 5px);
 
-    @media screen and (min-width: 62.5em) {
-        margin: 0;
-    }
+  @media screen and (min-width: 62.5em) {
+    margin: 0;
+  }
 }
 
 .white-box {
-    @include size(padding, 10px);
-    @include white-box;
+  @include size(padding, 10px);
+  @include white-box;
 }
 
-
 ul.plain {
-    list-style:none;
-    margin: 0;
-    padding: 0;
+  list-style: none;
+  margin: 0;
+  padding: 0;
 }
 
-
 .theme-list-link {
-    @include mono;
-    @include size(font-size, 11px);
+  @include mono;
+  @include size(font-size, 11px);
 
-    &:after {
-        @include size(padding-left, 11px);
-        content: url("/static/img/arrow-teal.png");
-        vertical-align: middle;
-    }
+  &:after {
+    @include size(padding-left, 11px);
+    content: url("/static/img/arrow-teal.png");
+    vertical-align: middle;
+  }
 }
 
-
 .left-column, .right-column {
-    @include size(max-width, 600px);
+  @include size(max-width, 600px);
 }
+
 @media screen and (min-width: 62.5em) {
-    .left-column {
-        @include size(width, 470px);
-        float: left;
-    }
-    .right-column {
-        @include size(width, 470px);
-        float:right;
-    }
+  .left-column {
+    @include size(width, 470px);
+    float: left;
+  }
+  .right-column {
+    @include size(width, 470px);
+    float: right;
+  }
 }
 
 .pagination {
-    display: block;
-    @include size(font-size, 12px);
-    @include size(padding, 6px);
-    text-align:center;
+  display: block;
+  @include size(font-size, 12px);
+  @include size(padding, 6px);
+  text-align: center;
 }
 
 .simple-hidden-initially {
-    display: none;
+  display: none;
 }
 
-
 .plain-list-container {
-    margin: 2em 0;
-    .plain-list {
-        column-count: 2;
-        -moz-column-count: 2;
-        -webkit-column-count: 2;
-
-        @media screen and (min-width: 768px) {
-            column-count: 4;
-            -moz-column-count: 4;
-            -webkit-column-count: 4;
-        }
-        @media screen and (min-width: 1024px) {
-            column-count: 5;
-            -moz-column-count: 5;
-            -webkit-column-count: 5;
-        }
+  margin: 2em 0;
+  .plain-list {
+    column-count: 2;
+    -moz-column-count: 2;
+    -webkit-column-count: 2;
 
-        p {
-            margin-top: 0;
+    @media screen and (min-width: 768px) {
+      column-count: 4;
+      -moz-column-count: 4;
+      -webkit-column-count: 4;
+    }
+    @media screen and (min-width: 1024px) {
+      column-count: 5;
+      -moz-column-count: 5;
+      -webkit-column-count: 5;
+    }
 
-            &.header {
-                -webkit-column-break-after: avoid;
-                break-after: avoid;
-            }
-        }
+    p {
+      margin-top: 0;
 
-        .initial-block {
-            display: inline-block;
-        }
+      &.header {
+        -webkit-column-break-after: avoid;
+        break-after: avoid;
+      }
     }
 
-    .pager {
-        font-size: 18px;
-        margin-top: .5em;
-        text-align: center;
-
-        .current {
-            font-weight: bold;
-            color: black;
+    .initial-block {
+      display: inline-block;
+    }
+  }
+
+  .pager-center {
+    //width: 100%;
+    text-align: center;
+  }
+
+  .pager {
+    display: inline-block;
+    padding-left: 0;
+    margin: 20px 0;
+    border-radius: 4px;
+    > li {
+      display: inline;
+
+      > a, > span {
+        position: relative;
+        float: left;
+        padding: 6px 12px;
+        margin-left: -1px;
+        line-height: 1.42857143;
+        color: #337ab7;
+        text-decoration: none;
+        background-color: #fff;
+        border: 1px solid #ddd;
+        &:hover, &:focus {
+          color: #23527c;
+          background-color: #eee;
+          border-color: #ddd;
+        }
+      }
+      &:first-child > a,
+      &:first-child > span {
+        margin-left: 0;
+        border-top-left-radius: 4px;
+        border-bottom-left-radius: 4px;
+      }
+      &:last-child > a,
+      &:last-child > span {
+        border-top-right-radius: 4px;
+        border-bottom-right-radius: 4px;
+      }
+    }
+    .current {
+      &, &:hover, &:focus {
+        z-index: 2;
+        color: #fff;
+        cursor: default;
+        background-color: #337ab7;
+        border-color: #337ab7;
+      }
+    }
+    .disabled {
+      > a, span {
+        &, &:hover, &:focus {
+          color: #777;
+          cursor: not-allowed;
+          background-color: #fff;
+          border-color: #ddd;
         }
+      }
     }
+  }
 }
 
-
 .tag-box {
-    display: block;
-
-    @media screen and (min-width: 768px) {
-        display: inline-block;
-        vertical-align: top;
-        width: 48%;
-        margin-right: 1%;
-        @include size(margin-bottom, 10px);
-    }
+  display: block;
+
+  @media screen and (min-width: 768px) {
+    display: inline-block;
+    vertical-align: top;
+    width: 48%;
+    margin-right: 1%;
+    @include size(margin-bottom, 10px);
+  }
 }
index e8a83f7..592f8ef 100755 (executable)
+@import "../tools";
+@import "const";
+
 @mixin inner-box {
-    display: block;
-    color: black;
-    @include size(margin, 1px);
-    @include size(padding, 8px 10px);
-    @include white-box;
+  display: block;
+  color: black;
+  @include size(margin, 1px);
+  @include size(padding, 8px 10px);
+  @include white-box;
 }
 
-
 .cover-area {
-    float: left;
-    @include size(margin-right, 15px);
-    @include size(margin-bottom, 5px);
+  float: left;
+  @include size(margin-right, 15px);
+  @include size(margin-bottom, 5px);
 
-    @include min-screen($S_BOOK_SHORT_FULL) {
-        position: absolute;
-        top: 0;
-        left: 0;
-        margin-right: 0;
-    }
+  @include min-screen($S_BOOK_SHORT_FULL) {
+    position: absolute;
+    top: 0;
+    left: 0;
+    margin-right: 0;
+  }
 
-    img.cover {
-        @include size(height, 193px / 2);
-        @include size(width, 139px / 2);
+  img.cover {
+    @include size(height, 193px / 2);
+    @include size(width, 139px / 2);
 
-        @include min-screen($S_BOOK_SHORT_MEDIUM) {
-            @include size(height, 193px);
-            @include size(width, 139px);
-        }
+    @include min-screen($S_BOOK_SHORT_MEDIUM) {
+      @include size(height, 193px);
+      @include size(width, 139px);
     }
+  }
 }
 
 .book-mini-box {
-    /* Original design fits 6 boxes horizontally in 975px (162.5px each),
-     * but we really want to fit 2 boxes on a 320px mobile screen. */
+  /* Original design fits 6 boxes horizontally in 975px (162.5px each),
+   * but we really want to fit 2 boxes on a 320px mobile screen. */
 
-    @include size(width, 160px);
-    display: inline-block;
-    vertical-align: top;
+  @include size(width, 160px);
+  display: inline-block;
+  vertical-align: top;
 
-    @include min-screen(350px) {
-        @include size(width, 162.5px);
-    }
-
-    .book-mini-box-inner {
-        @include inner-box;
-        @include size(height, 271px);
-        @include size(margin, 1px);
-        @include size(padding, 8px 9px);
-        overflow: hidden;
+  @include min-screen(350px) {
+    @include size(width, 162.5px);
+  }
 
-        @include min-screen(350px) {
-            @include size(padding, 8px 10px);
-        }
+  .book-mini-box-inner {
+    @include inner-box;
+    @include size(height, 271px);
+    @include size(margin, 1px);
+    @include size(padding, 8px 9px);
+    overflow: hidden;
 
-        a {
-            display: block;
-        }
-    }
-    img.cover {
-        @include size(height, 193px);
-        @include size(width, 139px);
-        @include size(margin-bottom, 18px);
-    }
-    .language {
-        color: #aaa;
-        float: right;
-        @include mono;
-        @include size(font-size, 10px);
-        text-transform: uppercase;
-        position: relative;
-        @include size(top, -20px);
-    }
-    .desc {
-        margin-left: 0;
-    }
-    .author {
-        @include size(font-size, 11px);
-        @include mono;
-        color: #6d7877;
-        display: block;
-        overflow: hidden;
-        text-overflow: ellipsis;
-        white-space: nowrap;
+    @include min-screen(350px) {
+      @include size(padding, 8px 10px);
     }
-    .title {
-        @include size(font-size, 14px);
-        color: #242424;
-        white-space: normal;
+
+    a {
+      display: block;
     }
+  }
+  img.cover {
+    @include size(height, 193px);
+    @include size(width, 139px);
+    @include size(margin-bottom, 18px);
+  }
+  .language {
+    color: #aaa;
+    float: right;
+    @include mono;
+    @include size(font-size, 10px);
+    text-transform: uppercase;
+    position: relative;
+    @include size(top, -20px);
+  }
+  .desc {
+    margin-left: 0;
+  }
+  .author {
+    @include size(font-size, 11px);
+    @include mono;
+    color: #6d7877;
+    display: block;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+  }
+  .title {
+    @include size(font-size, 14px);
+    color: #242424;
+    white-space: normal;
+  }
 }
 
-
-
 .work-list {
-    margin: 0;
-    padding: 0;
-    list-style: none;
+  margin: 0;
+  padding: 0;
+  list-style: none;
 
-    .Book-item {
-        /*@include min-screen($S_BOOK_SHORT_FULL) {
-            display: inline-block;
-        }
-        vertical-align: top;*/
+  .Book-item {
+    /*@include min-screen($S_BOOK_SHORT_FULL) {
+        display: inline-block;
     }
+    vertical-align: top;*/
+  }
 }
 
+#books-list .plain-list-container .plain-list {
+  p {
+    line-height: 1.2em;
+    margin-bottom: 20px;
+    &.header {
+      font-weight: bold;
+    }
+  }
+}
 
 .book-box {
-    margin: 0;
-    vertical-align: top;
+  margin: 0;
+  vertical-align: top;
 
-    /* */
-    a {
-        color: #0D7E85;
-    }
+  /* */
+  a {
+    color: #0D7E85;
+  }
 
-    .book-box-inner {
-        position: relative;
-        @include inner-box;
-        @include size(min-height, 197.5px);
-    }
+  .book-box-inner {
+    position: relative;
+    @include inner-box;
+    @include size(min-height, 197.5px);
+  }
 }
 
-
 .audiobook-box {
-    .book-left-column {
-        @media screen and (min-width: 1024px) {
-            display: inline-block;
-            @include size(width, 590px);
-        }
+  .book-left-column {
+    @media screen and (min-width: 1024px) {
+      display: inline-block;
+      @include size(width, 590px);
     }
+  }
 
-    .audiobook-right-column {
-        @media screen and (min-width: 1024px) {
-            float: right;
-            @include size(width, 360px);
-        }
+  .audiobook-right-column {
+    @media screen and (min-width: 1024px) {
+      float: right;
+      @include size(width, 360px);
     }
+  }
 
-    .jp-type-playlist {
-        margin-top: 24px;
+  .jp-type-playlist {
+    margin-top: 24px;
 
-        @media screen and (min-width: 1024px) {
-            float: right;
-            margin-top: 48px;
-        }
+    @media screen and (min-width: 1024px) {
+      float: right;
+      margin-top: 48px;
     }
+  }
 }
 
-
 .book-wide-box {
-    margin: 0;
-    vertical-align: top;
+  margin: 0;
+  vertical-align: top;
 
-    @media screen and (min-width: 62.5em) {
-        @include size(width, 975px);
+  @media screen and (min-width: 62.5em) {
+    @include size(width, 975px);
+  }
+
+  .book-box-inner {
+    position: relative;
+    @include size(min-height, 244px);
+    @include inner-box;
+    @include size(margin-left, 0);
+    @include size(margin-right, 0);
+
+    .book-left-column { /* FIXME */
+      @media screen and (min-width: 62.5em) {
+        float: left;
+        @include size(width, 536px);
+      }
     }
 
-    .book-box-inner {
-        position: relative;
-        @include size(min-height, 244px);
-        @include inner-box;
-        @include size(margin-left, 0);
-        @include size(margin-right, 0);
-
-        .book-left-column { /* FIXME */
-            @media screen and (min-width: 62.5em) {
-                float: left;
-                @include size(width, 536px);
-            }
-        }
+    .license-icon {
+      display: block;
+      @include size(margin-top, 5px);
+    }
 
-        .license-icon {
-            display: block;
-            @include size(margin-top, 5px);
+    @include min-screen($S_BOOK_SHORT_FULL) {
+      // Show full title on the work's page.
+
+      .book-box-body {
+        height: auto;
+        @include size(min-height, 170px);
+
+        .book-box-head .title {
+          height: auto;
+          @include size(min-height, 57.6px);
         }
+      }
+    }
 
-        @include min-screen($S_BOOK_SHORT_FULL) {
-             // Show full title on the work's page.
+    .book-box-head,
+    .tags,
+    .book-box-tools {
+      @media screen and (min-width: 62.5em) {
+        @include size(width, 382px);
+      }
+    }
 
-            .book-box-body {
-                height: auto;
-                @include size(min-height, 170px);
+    #theme-list-wrapper {
+      @include min-screen($S_BOOK_SHORT_MEDIUM) {
+        @include size(margin-left, 154px);
+        @include size(width, 300px);
+      }
+      margin-bottom: 0;
 
-                .book-box-head .title {
-                    height: auto;
-                    @include size(min-height, 57.6px);
-                }
-            }
-        }
+      p {
+        @include size(margin-top, 10px);
+        @include size(margin-bottom, 10px);
+      }
+    }
 
-        .book-box-head,
-        .tags,
-        .book-box-tools {
-            @media screen and (min-width: 62.5em) {
-                @include size(width, 382px);
-            }
+    .right-column {
+      @include size(margin-top, 16px);
+      // Eat the padding
+      @include size(margin-left, -10px);
+      @include size(margin-right, -10px);
+      max-width: none;
+
+      @media screen and (min-width: 62.5em) {
+        @include size(width, 415px);
+        // Eat the padding
+        @include size(margin-top, -8px);
+      }
+
+      .other-tools,
+      .other-download {
+        @include size(font-size, 11px);
+        clear: left;
+        @include size(margin-top, 25px);
+        line-height: 1.75em;
+        @include size(margin-left, 15px);
+
+        h2 {
+          margin: 0;
+          @include size(font-size, 11px);
+          @include mono;
         }
 
-        #theme-list-wrapper {
-            @include min-screen($S_BOOK_SHORT_MEDIUM) {
-                @include size(margin-left, 154px);
-                @include size(width, 300px);
-            }
-            margin-bottom: 0;
-
-            p {
-                @include size(margin-top, 10px);
-                @include size(margin-bottom, 10px);
-            }
+        @include min-screen($S_BOOK_SHORT_FULL) {
+          float: left;
+          clear: none;
+          @include size(width, 145px);
+          @include size(margin-top, 50px);
+          @include size(margin-right, 0);
+          @include size(margin-bottom, 0);
+          @include size(margin-left, 5px);
         }
 
-        .right-column {
-            @include size(margin-top, 16px);
-            // Eat the padding
-            @include size(margin-left, -10px);
-            @include size(margin-right, -10px);
-            max-width: none;
-
-            @media screen and (min-width: 62.5em) {
-                @include size(width, 415px);
-                // Eat the padding
-                @include size(margin-top, -8px);
-            }
-
-            .other-tools,
-            .other-download {
-                @include size(font-size, 11px);
-                clear: left;
-                @include size(margin-top, 25px);
-                line-height: 1.75em;
-                @include size(margin-left, 15px);
-
-                h2 {
-                    margin: 0;
-                    @include size(font-size, 11px);
-                    @include mono;
-                }
-
-                @include min-screen($S_BOOK_SHORT_FULL) {
-                    float: left;
-                    clear: none;
-                    @include size(width, 145px);
-                    @include size(margin-top, 50px);
-                    @include size(margin-right, 0);
-                    @include size(margin-bottom, 0);
-                    @include size(margin-left, 5px);
-                }
-
-                @include min-screen(1000px) {
-                    @include size(margin-top, 50px);
-                    line-height: 1.2em;
-                }
-            }
-            .other-download {
-                @include min-screen($S_BOOK_SHORT_FULL) {
-                    @include size(margin-left, 15px);
-                    @include size(width, 220px);
-                }
-            }
+        @include min-screen(1000px) {
+          @include size(margin-top, 50px);
+          line-height: 1.2em;
         }
+      }
+      .other-download {
+        @include min-screen($S_BOOK_SHORT_FULL) {
+          @include size(margin-left, 15px);
+          @include size(width, 220px);
+        }
+      }
     }
+  }
 
-    .jp-type-playlist {
-        margin-top: 24px;
-        margin-left: 0.625rem;
+  .jp-type-playlist {
+    margin-top: 24px;
+    margin-left: 0.625rem;
 
-        @media screen and (min-width: 1024px) {
-            float: right;
-            margin-right: 0.625rem;
-        }
+    @media screen and (min-width: 1024px) {
+      float: right;
+      margin-right: 0.625rem;
     }
+  }
 
 }
 
 @media screen and (min-width: 50em) {
-    .picture.book-wide-box .right-column {
-        float: none;
-        @include size(width, 415px);
-        top: 0;
-        @include size(margin-left, 550px);
-        margin-top: 0em;
-    }
+  .picture.book-wide-box .right-column {
+    float: none;
+    @include size(width, 415px);
+    top: 0;
+    @include size(margin-left, 550px);
+    margin-top: 0;
+  }
 }
 
-
 .book-box-body {
-    @include size(margin-bottom, 10px);
-    position: relative;
+  @include size(margin-bottom, 10px);
+  position: relative;
 
-    .book-box-head,
-    .tags,
-    .book-box-tools {
-        @include min-screen($S_BOOK_SHORT_FULL) {
-            margin-left: 154px;
-        }
+  .book-box-head,
+  .tags,
+  .book-box-tools {
+    @include min-screen($S_BOOK_SHORT_FULL) {
+      margin-left: 154px;
     }
+  }
 
-    .book-box-head {
-        @include size(padding-top, 14px);
-        @include size(margin-bottom, 10px);
+  .book-box-head {
+    @include size(padding-top, 14px);
+    @include size(margin-bottom, 10px);
 
-        @include min-screen($S_BOOK_SHORT_FULL) {
-            @include size(min-height, 70px);
-        }
+    @include min-screen($S_BOOK_SHORT_FULL) {
+      @include size(min-height, 70px);
+    }
 
-        a {
-            color: black;
-        }
-        .author {
-            @include size(font-size, 11px);
-            @include mono;
-            @include size(line-height, 13.2px);
-            @include size(max-height, 26.4px);
-            overflow: hidden;
-
-            @include min-screen($S_BOOK_SHORT_FULL) {
-                @include size(max-width, 264px);
-            }
-        }
-        .title {
-            @include size(font-size, 24px);
-            line-height: 1.2em;
-            @include size(margin-top, 7.2px);
-            @include size(margin-bottom, 12px);
-
-            @include min-screen($S_BOOK_SHORT_FULL) {
-                margin-bottom: 0;
-                @include size(height, 57.6px);
-                overflow: hidden;
-            }
-        }
+    a {
+      color: black;
+    }
+    .author {
+      @include size(font-size, 11px);
+      @include mono;
+      @include size(line-height, 13.2px);
+      @include size(max-height, 26.4px);
+      overflow: hidden;
+
+      @include min-screen($S_BOOK_SHORT_FULL) {
+        @include size(max-width, 264px);
+      }
+    }
+    .title {
+      @include size(font-size, 24px);
+      line-height: 1.2em;
+      @include size(margin-top, 7.2px);
+      @include size(margin-bottom, 12px);
+
+      @include min-screen($S_BOOK_SHORT_FULL) {
+        margin-bottom: 0;
+        @include size(height, 57.6px);
+        overflow: hidden;
+      }
     }
+  }
 
-    .tags {
-        @include size(font-size, 11px);
-        line-height: 1.2em;
-        margin-bottom: 5px;
+  .tags {
+    @include size(font-size, 11px);
+    line-height: 1.2em;
+    margin-bottom: 5px;
 
-        @include min-screen($S_BOOK_SHORT_FULL) {
-            @include size(max-height, 57.6px);
-            overflow: hidden;
-        }
+    @include min-screen($S_BOOK_SHORT_FULL) {
+      @include size(max-height, 57.6px);
+      overflow: hidden;
+    }
 
-        .category {
-            display: block;
-            @include size(margin-top, 6px);
-            @include size(margin-bottom, 6px);
+    .category {
+      display: block;
+      @include size(margin-top, 6px);
+      @include size(margin-bottom, 6px);
 
-            @include min-screen($S_BOOK_SHORT_FULL) {
-                display: inline;
-            }
+      @include min-screen($S_BOOK_SHORT_FULL) {
+        display: inline;
+      }
 
-            .mono {
-                @include mono;
-            }
+      .mono {
+        @include mono;
+      }
 
-            .book-box-tag {
-                @include size(margin-left, 4.4px);
-                @include size(margin-right, 5.5px);
-            }
-        }
+      .book-box-tag {
+        @include size(margin-left, 4.4px);
+        @include size(margin-right, 5.5px);
+      }
     }
+  }
 
 }
 
+.book-box-tools {
+  @include size(font-size, 11px);
+  margin: 0;
+  padding: 0;
+  list-style: none;
 
+  clear: left;
 
-.book-box-tools {
-    @include size(font-size, 11px);
-    margin: 0;
-    padding: 0;
-    list-style: none;
+  @include min-screen($S_BOOK_SHORT_MEDIUM) {
+    clear: none;
+    @include size(margin-left, 139px + 15px);
+  }
 
-    clear: left;
+  li {
+    @include mono;
+    margin-top: 0;
+  }
 
-    @include min-screen($S_BOOK_SHORT_MEDIUM) {
-        clear: none;
-        @include size(margin-left, 139px + 15px);
+  .book-box-read {
+    a {
+      @include mono;
+      display: inline-block;
+      background: #0D7E85;
+      color: white;
+      @include size(width, 200px);
+      text-align: center;
+      @include size(margin-bottom, 5px);
     }
 
-    li {
-        @include mono;
-        margin-top: 0;
+    a:before {
+      content: url("/static/img/read-white.png");
+      @include size(font-size, 25px);
+      @include size(margin-right, 3.71px);
+      vertical-align: middle;
+      font-weight: normal;
+      // ugly quick fix
+      position: relative;
+      bottom: 2px;
     }
+  }
 
-    .book-box-read {
-        a {
-            @include mono;
-        }
+  .book-box-download {
+    position: relative;
 
-        a:before {
-            content: url("/static/img/read.png");
-            @include size(font-size, 25px);
-            @include size(margin-right, 3.71px);
-            vertical-align: middle;
-            font-weight: normal;
-        }
-    }
+    a {
+      @mixin downarrow {
+        color: #0D7E85;
 
-    .book-box-download {
-        position: relative;
-
-        a {
-            @mixin downarrow {
-                color: #0D7E85;
-
-                &:before {
-                    content: url("/static/img/download.png");
-                    @include size(font-size, 25px);
-                    @include size(margin-right, 3.71px);
-                    vertical-align: middle;
-                    font-weight: normal;
-                    display: inline;
-                }
-            }
-
-            &.downarrow {
-                @include downarrow;
-            }
+        &:before {
+          content: url("/static/img/download.png");
+          @include size(font-size, 25px);
+          @include size(margin-right, 3.71px);
+          vertical-align: middle;
+          font-weight: normal;
+          display: inline;
         }
+      }
 
-        .book-box-formats {
-            display: inline-block;
-            max-width: 220px;
-            vertical-align: top;
-            padding-top: 6px;
-
-                a {
-                    display: inline-block;
-                    @include size(padding, .2em 1em);
-                }
-        }
+      &.downarrow {
+        @include downarrow;
+      }
     }
-}
-
 
+    .book-box-formats {
+      display: inline-block;
+      max-width: 220px;
+      vertical-align: top;
+      padding-top: 6px;
 
+      a {
+        display: inline-block;
+        @include size(padding, .2em 1em);
+      }
+    }
+  }
+}
 
 .star {
-    @include size(font-size, 22.5px);
-    @include size(margin-right, 11.25px);
-    position: absolute;
-    right: 0;
-    z-index: 10;
-
-    button {
-        cursor: pointer;
-        &::-moz-focus-inner {
-            padding: 0;
-            border: 0;
-        }
-    }
-    .if-unlike button {
-        font-size: 1em;
-        font-family: inherit;
-        border: 0;
-        background: none;
-        margin: 0;
-        padding: 0;
-        color: #757575;
-    }
-    .if-like a {
-        display:block;
-        text-align:right;
-        padding: 0;
+  @include size(font-size, 22.5px);
+  @include size(margin-right, 11.25px);
+  position: absolute;
+  right: 0;
+  z-index: 10;
+
+  button {
+    cursor: pointer;
+    &::-moz-focus-inner {
+      padding: 0;
+      border: 0
     }
+  }
+  .if-unlike button {
+    font-size: 1em;
+    font-family: inherit;
+    border: 0;
+    background: none;
+    margin: 0;
+    padding: 0;
+    color: #757575;
+  }
+  .if-like a {
+    display: block;
+    text-align: right;
+    padding: 0;
+  }
 }
+
 .like .if-unlike {
-    display: none;
+  display: none;
 }
+
 .unlike .if-like {
-    display: none;
+  display: none;
 }
 
-
 #book-detail .see-also,
 #picture-detail .see-also {
-    h1 {
-        @include size(height, 32px);
-        margin: 0;
-        @include size(padding-top, 19px);
-        @include size(padding-left, 10px);
+  h1 {
+    @include size(height, 32px);
+    margin: 0;
+    @include size(padding-top, 19px);
+    @include size(padding-left, 10px);
 
-        @include size(font-size, 11px);
-        @include mono;
-        font-weight: normal;
+    @include size(font-size, 11px);
+    @include mono;
+    font-weight: normal;
 
-        @media screen and (min-width: 33em) {
-            @include size(padding-left, 19px);
-        }
+    @media screen and (min-width: 33em) {
+      @include size(padding-left, 19px);
     }
+  }
 }
diff --git a/src/wolnelektury/static/scss/main/fonts.scss b/src/wolnelektury/static/scss/main/fonts.scss
new file mode 100644 (file)
index 0000000..20abbb6
--- /dev/null
@@ -0,0 +1,143 @@
+/* cyrillic-ext */
+@font-face {
+  font-family: 'Cousine';
+  font-style: normal;
+  font-weight: 400;
+  src: local('Cousine'), url(/static/fonts/cousine/v10/Bt5Lz7Saa5a5RtsafP9xmfY6323mHUZFJMgTvxaG2iE.woff2) format('woff2');
+  unicode-range: U+0460-052F, U+20B4, U+2DE0-2DFF, U+A640-A69F;
+}
+
+/* cyrillic */
+@font-face {
+  font-family: 'Cousine';
+  font-style: normal;
+  font-weight: 400;
+  src: local('Cousine'), url(/static/fonts/cousine/v10/IYQIfrNvkAhlEkaWqzgTm_Y6323mHUZFJMgTvxaG2iE.woff2) format('woff2');
+  unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
+}
+
+/* latin-ext */
+@font-face {
+  font-family: 'Cousine';
+  font-style: normal;
+  font-weight: 400;
+  src: local('Cousine'), url(/static/fonts/cousine/v10/qjsoqLzZoDyy_opKVvy-uvY6323mHUZFJMgTvxaG2iE.woff2) format('woff2');
+  unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF;
+}
+
+/* latin */
+@font-face {
+  font-family: 'Cousine';
+  font-style: normal;
+  font-weight: 400;
+  src: local('Cousine'), url(/static/fonts/cousine/v10/0IpceuvDvCegpU9Mz8MQ_g.woff2) format('woff2');
+  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000;
+}
+
+/* cyrillic-ext */
+@font-face {
+  font-family: 'Cousine';
+  font-style: normal;
+  font-weight: 700;
+  src: local('Cousine Bold'), local('Cousine-Bold'), url(/static/fonts/cousine/v10/4OFqQVT4CccEiwAQMKv0eiEAvth_LlrfE80CYdSH47w.woff2) format('woff2');
+  unicode-range: U+0460-052F, U+20B4, U+2DE0-2DFF, U+A640-A69F;
+}
+
+/* cyrillic */
+@font-face {
+  font-family: 'Cousine';
+  font-style: normal;
+  font-weight: 700;
+  src: local('Cousine Bold'), local('Cousine-Bold'), url(/static/fonts/cousine/v10/auteuWVL5GjOhekECAkC_SEAvth_LlrfE80CYdSH47w.woff2) format('woff2');
+  unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
+}
+
+/* latin-ext */
+@font-face {
+  font-family: 'Cousine';
+  font-style: normal;
+  font-weight: 700;
+  src: local('Cousine Bold'), local('Cousine-Bold'), url(/static/fonts/cousine/v10/JHq2r7PZMuzGPtEvdlprZCEAvth_LlrfE80CYdSH47w.woff2) format('woff2');
+  unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF;
+}
+
+/* latin */
+@font-face {
+  font-family: 'Cousine';
+  font-style: normal;
+  font-weight: 700;
+  src: local('Cousine Bold'), local('Cousine-Bold'), url(/static/fonts/cousine/v10/XkYjaL8YjqL9qW8o0T14ufk_vArhqVIZ0nv9q090hN8.woff2) format('woff2');
+  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000;
+}
+
+/* cyrillic-ext */
+@font-face {
+  font-family: 'Cousine';
+  font-style: italic;
+  font-weight: 400;
+  src: local('Cousine Italic'), local('Cousine-Italic'), url(/static/fonts/cousine/v10/svY-0AsqnhB5pNHScPRpHhJtnKITppOI_IvcXXDNrsc.woff2) format('woff2');
+  unicode-range: U+0460-052F, U+20B4, U+2DE0-2DFF, U+A640-A69F;
+}
+
+/* cyrillic */
+@font-face {
+  font-family: 'Cousine';
+  font-style: italic;
+  font-weight: 400;
+  src: local('Cousine Italic'), local('Cousine-Italic'), url(/static/fonts/cousine/v10/bS4Fjp2fQa3pRrzTYB6CXhJtnKITppOI_IvcXXDNrsc.woff2) format('woff2');
+  unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
+}
+
+/* latin-ext */
+@font-face {
+  font-family: 'Cousine';
+  font-style: italic;
+  font-weight: 400;
+  src: local('Cousine Italic'), local('Cousine-Italic'), url(/static/fonts/cousine/v10/aWxnkwXsEJCSg-HKkPTCkBJtnKITppOI_IvcXXDNrsc.woff2) format('woff2');
+  unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF;
+}
+
+/* latin */
+@font-face {
+  font-family: 'Cousine';
+  font-style: italic;
+  font-weight: 400;
+  src: local('Cousine Italic'), local('Cousine-Italic'), url(/static/fonts/cousine/v10/0VdvlOBfHobA4eW-NVYk-VtXRa8TVwTICgirnJhmVJw.woff2) format('woff2');
+  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000;
+}
+
+/* cyrillic-ext */
+@font-face {
+  font-family: 'Cousine';
+  font-style: italic;
+  font-weight: 700;
+  src: local('Cousine Bold Italic'), local('Cousine-BoldItalic'), url(/static/fonts/cousine/v10/y_AZ5Sz-FwL1lux2xLSTZa-j2U0lmluP9RWlSytm3ho.woff2) format('woff2');
+  unicode-range: U+0460-052F, U+20B4, U+2DE0-2DFF, U+A640-A69F;
+}
+
+/* cyrillic */
+@font-face {
+  font-family: 'Cousine';
+  font-style: italic;
+  font-weight: 700;
+  src: local('Cousine Bold Italic'), local('Cousine-BoldItalic'), url(/static/fonts/cousine/v10/y_AZ5Sz-FwL1lux2xLSTZZX5f-9o1vgP2EXwfjgl7AY.woff2) format('woff2');
+  unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
+}
+
+/* latin-ext */
+@font-face {
+  font-family: 'Cousine';
+  font-style: italic;
+  font-weight: 700;
+  src: local('Cousine Bold Italic'), local('Cousine-BoldItalic'), url(/static/fonts/cousine/v10/y_AZ5Sz-FwL1lux2xLSTZT0LW-43aMEzIO6XUTLjad8.woff2) format('woff2');
+  unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF;
+}
+
+/* latin */
+@font-face {
+  font-family: 'Cousine';
+  font-style: italic;
+  font-weight: 700;
+  src: local('Cousine Bold Italic'), local('Cousine-BoldItalic'), url(/static/fonts/cousine/v10/y_AZ5Sz-FwL1lux2xLSTZegdm0LZdjqr5-oayXSOefg.woff2) format('woff2');
+  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000;
+}
index 251df9f..14516b8 100755 (executable)
@@ -1,3 +1,5 @@
+@import "../tools";
+
 $header_bg: #191919;
 $small_logo: .9;
 
@@ -23,12 +25,11 @@ header#main {
   color: #bbb;
   position: relative;
 
-  Xtext-align: center;
+  /*text-align: center;*/
 
   @media screen and (min-width: 1024px) {
     width: 975px;
     margin: auto;
-
   }
 
   a {
@@ -153,7 +154,7 @@ header#main {
     top: 0;
   }
 
-  X#user-info {
+  /*#user-info {
     margin-top: 0;
     @include size(margin-left, 5px);
     @include size(margin-right, 5px);
@@ -175,7 +176,7 @@ header#main {
       @include size(margin-right, 0);
     }
 
-    /* We want submenu on far left on small screens. */
+    !* We want submenu on far left on small screens. *!
     .hidden-box-wrapper {
       position: static;
       @media screen and (min-width: 24em) {
@@ -201,7 +202,7 @@ header#main {
         color: #0D7E85;
       }
     }
-  }
+  }*/
 
   form#search-area {
     position: relative;
@@ -239,24 +240,24 @@ header#main {
         @include size(border-radius, 5px);
         @include box-shadow(0 0 6.5px #444444 inset);
 
-        font-family: Georgia;
+        font-family: Georgia, serif;
         @include size(font-size, 13px);
         background-color: white;
         color: black;
 
         /* styling search placeholder */
         &::placeholder {
-          font-family: Georgia;
+          font-family: Georgia, serif;
           font-style: italic;
           color: #767676;
         }
         &::-webkit-input-placeholder {
-          font-family: Georgia;
+          font-family: Georgia, serif;
           font-style: italic;
           color: #767676;
         }
         &::-moz-placeholder {
-          font-family: Georgia;
+          font-family: Georgia, serif;
           font-style: italic;
           color: #767676;
         }
@@ -289,31 +290,6 @@ header#main {
 
   #header-wrapper {
     position: relative;
-
-    /* Upper-half both sides dark background */
-    &:before {
-      content: " ";
-      display: block;
-      z-index: -1;
-      position: absolute;
-      top: 0;
-      @include size(bottom, 45px);
-      left: 0;
-      width: 100%;
-      background-color: $header_bg;
-    }
-
-    /* Left-side dark background */
-    &:after {
-      content: " ";
-      display: block;
-      z-index: -1;
-      position: absolute;
-      top: 0;
-      bottom: 0;
-      left: 0;
-      width: 50%;
-      background-color: $header_bg;
-    }
+    background-color: $header_bg;
   }
 }
index c2f708a..174a896 100755 (executable)
@@ -129,12 +129,16 @@ body.menu-on {
             display: inline-block;
             @include size(width, 20%);
 
+            &.active {
+                background-color: white;
+            }
+
             a {
                 text-align: center;
                 display: block;
                 @include size(line-height, 13px);
                 @include size(padding, 18px 0 15px);
-                @include size(border-bottom, 3px solid #e2e2e2);
+                //@include size(border-bottom, 3px solid #e2e2e2);
                 color: #0c7076;
                 @include size(font-size, 11px);
                 @include mono;
index 76869c4..018a86d 100644 (file)
@@ -53,7 +53,7 @@ $default-em-size: 16px;
 }
 
 @mixin mono {
-    font-family: "Andale Mono", "Lucida Sans Typewriter", "Courier New";
+    font-family: "Cousine";
 }
 
 @mixin hidden-label {
index 840514f..1f9cbf2 100755 (executable)
           {#% ssi_include 'catalogue_book_mini' pk=b.pk %#}
         {% endfor %}
       </div>
-      <div class="note white-box normal-text" style="font-size: 18px">
-        i wiele innych książek, wierszy, obrazów, audiobooków…
-      </div>
+      <a href="{% url "book_list" %}">
+        <div class="note white-box normal-text" style="font-size: 18px">
+          i wiele innych książek, wierszy, obrazów, audiobooków…
+        </div>
+      </a>
     </div>
   </section>
 
index af9df97..d3abd31 100644 (file)
     <head>
         <meta charset="utf-8">
         <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
-        <meta name="application-name" content="Wolne Lektury"/>
-        <meta property="og:site_name" content="Wolne Lektury"/>
-        <meta property="og:title" content="{% block ogtitle %}{{ page_title }}{% endblock %}"/>
-        <meta property="og:type" content="{% block ogtype %}website{% endblock %}"/>
-        <meta property="og:image" content="{% block ogimage %}{{ FULL_STATIC_URL }}img/wiatrak.png{% endblock %}"/>
-        <meta name="description"
-              content="{% block metadescription %}Darmowe, opracowane, pełne teksty lektur, e-booki, audiobooki i pliki DAISY na wolnej licencji.{% endblock %}"/>
+        <meta name="application-name" content="Wolne Lektury" />
+        <meta property="og:site_name" content="Wolne Lektury" />
+        <meta property="og:title" content="{% block ogtitle %}{{ page_title }}{% endblock %}" />
+        <meta property="og:type" content="{% block ogtype %}website{% endblock %}" />
+        <meta property="og:image" content="{% block ogimage %}{{ FULL_STATIC_URL }}img/wiatrak.jpg{% endblock %}" />
+        <meta name="description" content="{% block metadescription %}Darmowe, opracowane, pełne teksty lektur, e-booki, audiobooki i pliki DAISY na wolnej licencji.{% endblock %}" />
         {% block ogextra %}{% endblock %}
 
         <title>
                         </ul>
 
 
-                        <ul id="main-menu">
-                            <li><a href="{% url 'book_list' %}">{% trans "Literature" %}</a></li>
-                            <li><a href="{% url 'theme_catalogue' %}">{% trans "Themes" %}</a></li>
-                            <li><a href="{% url 'audiobook_list' %}">{% trans "Audiobooks" %}</a></li>
-                            <li><a href="{% url 'gallery' %}">{% trans "Gallery" %}</a></li>
-                            <li><a href="{% url 'catalogue' %}">Wszystkie utwory</a></li>
-                        </ul>
+            <ul id="main-menu">
+                <li{% if active_menu_item == 'books' %} class="active"{% endif %}>
+                    <a href="{% url 'book_list' %}">{% trans "Literature" %}</a>
+                </li>
+                <li{% if active_menu_item == 'theme' %} class="active"{% endif %}>
+                    <a href="{% url 'theme_catalogue' %}">{% trans "Themes" %}</a>
+                </li>
+                <li{% if active_menu_item == 'audiobooks' %} class="active"{% endif %}>
+                    <a href="{% url 'audiobook_list' %}">{% trans "Audiobooks" %}</a>
+                </li>
+                <li{% if active_menu_item == 'gallery' %} class="active"{% endif %}>
+                    <a href="{% url 'gallery' %}">{% trans "Gallery" %}</a>
+                </li>
+                <li{% if active_menu_item == 'all_works' %} class="active"{% endif %}>
+                    <a href="{% url 'catalogue' %}">{% trans "All works" %}</a>
+                </li>
+            </ul>
 
 
                     </nav>
index e8aa7fb..2ce231b 100644 (file)
@@ -2,8 +2,16 @@
 # This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
 #
+import json
 import os
+from functools import wraps
+
 import pytz
+from inspect import getargspec
+
+from django.http import HttpResponse
+from django.template import RequestContext
+from django.template.loader import render_to_string
 from django.utils import timezone
 from django.conf import settings
 
@@ -23,3 +31,78 @@ def utc_for_js(dt):
 def makedirs(path):
     if not os.path.isdir(path):
         os.makedirs(path)
+
+
+def stringify_keys(dictionary):
+    return dict((keyword.encode('ascii'), value)
+                for keyword, value in dictionary.iteritems())
+
+
+def json_encode(obj, sort_keys=True, ensure_ascii=False):
+    return json.dumps(obj, sort_keys=sort_keys, ensure_ascii=ensure_ascii)
+
+
+def json_decode(obj):
+    return json.loads(obj)
+
+
+def json_decode_fallback(value):
+    try:
+        return json_decode(value)
+    except ValueError:
+        return value
+
+
+class AjaxError(Exception):
+    pass
+
+
+def ajax(login_required=False, method=None, template=None, permission_required=None):
+    def decorator(fun):
+        @wraps(fun)
+        def ajax_view(request):
+            kwargs = {}
+            request_params = None
+            if method == 'post':
+                request_params = request.POST
+            elif method == 'get':
+                request_params = request.GET
+            fun_params, xx, fun_kwargs, defaults = getargspec(fun)
+            if defaults:
+                required_params = fun_params[1:-len(defaults)]
+            else:
+                required_params = fun_params[1:]
+            missing_params = set(required_params) - set(request_params)
+            if missing_params:
+                res = {
+                    'result': 'missing params',
+                    'missing': ', '.join(missing_params),
+                }
+            else:
+                if request_params:
+                    request_params = dict(
+                        (key, json_decode_fallback(value))
+                        for key, value in request_params.iteritems()
+                        if fun_kwargs or key in fun_params)
+                    kwargs.update(stringify_keys(request_params))
+                res = None
+                if login_required and not request.user.is_authenticated():
+                    res = {'result': 'logout'}
+                if (permission_required and
+                        not request.user.has_perm(permission_required)):
+                    res = {'result': 'access denied'}
+            if not res:
+                try:
+                    res = fun(request, **kwargs)
+                    if res and template:
+                        res = {'html': render_to_string(template, res, RequestContext(request))}
+                except AjaxError as e:
+                    res = {'result': e.args[0]}
+            if 'result' not in res:
+                res['result'] = 'ok'
+            return HttpResponse(json_encode(res), content_type='application/json; charset=utf-8',
+                                status=200 if res['result'] == 'ok' else 400)
+
+        return ajax_view
+
+    return decorator