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
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(),
]
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'),
"""
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
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,
+ })
# 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
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)
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
--- /dev/null
+# -*- 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)
--- /dev/null
+# -*- 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')),
+ ],
+ ),
+ ]
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
pass
class Meta:
- ordering = ('sort_key',)
+ ordering = ('sort_key_author', 'sort_key')
verbose_name = _('book')
verbose_name_plural = _('books')
app_label = 'catalogue'
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:
).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)
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:
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
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',
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)
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):
+++ /dev/null
-{% 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 %}
{% 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 %}
{% 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 %} ({{ 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 %} ({{ tag.count }}){% endif %}</a></li>
+ <li><a href="{% catalogue_url list_type choices tag %}">{{ tag }}{% if tag.count %} ({{ 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 %}
<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 %} ({{ 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 %} ({{ item.count}}){% endif %}</a></p>
+ {% endfor %}
{% if initial_blocks %}</div>{% endif %}
-{% endfor %}
-</div>
-<div class="pager"></div>
+ {% endfor %}
+ </div>
</div>
{% endspaceless %}
<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:" %} <span class='artist'>{{ i.mp3.extra_info.artist_name }}</span>,
+ {% trans "director:" %} <span class='director'>{{ i.mp3.extra_info.director_name }}</span>
</div>
</div>
</li>
{% 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 %} ({{ tag.count }}){% endif %}</a></li>
+ <li><a href="{% catalogue_url 'default' choices tag %}">{{ tag }}{% if tag.count %} ({{ tag.count }}){% endif %}</a></li>
{% endfor %}
{% else %}
{% for tag in tags %}
{% 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 %}
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
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 = []
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 = []
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')
@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
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.
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'),
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'),
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'),
)
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})
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]
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:
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):
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))
# ==========
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:
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):
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
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,
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)
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:
# 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')
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()
@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,
('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)
(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)
@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', {
@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,
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,)))
'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)
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))
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
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:
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
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))
pass
class Meta:
- ordering = ('sort_key',)
+ ordering = ('sort_key_author', 'sort_key')
verbose_name = _('picture')
verbose_name_plural = _('pictures')
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):
<div class='clearboth'></div>
-
-
-{% work_list book_list %}
-
-
-
+{% work_list picture_list %}
{% endblock %}
<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>
# 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
# 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):
# 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):
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(),
+ }
# =========
@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,
+ })
@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())
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"),
})
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,
+ })
# 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."""
@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):
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,
+ })
CATALOGUE_COUNTERS_FILE = os.path.join(VAR_DIR, 'catalogue_counters.p')
CATALOGUE_MIN_INITIALS = 60
+
+PICTURE_PAGE_SIZE = 20
--- /dev/null
+<?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>
if (button && short_text) button.html(short_text);
}
return false;
- }
+ };
return toggle;
- }
+ };
if (long_el.html().length <= short_el.html().length)
return;
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="#">></a> ';
+ return '<li><a href="#">›</a></li>';
case 'prev': // <
- return ' <a href="#"><</a> ';
+ return '<li><a href="#">‹</a></li>';
case 'first': // [
- return '<a href="#">«</a> ';
+ return '<li><a href="#">«</a></li>';
case 'last': // ]
- return ' <a href="#">»</a>';
+ return '<li><a href="#">»</a></li>';
}
}
});
@import "main/catalogue";
@import "main/cite";
@import "main/dialogs";
+@import "main/fonts";
@import "main/footer";
@import "main/form";
@import "main/fragment";
@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";
/* 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);
+ }
}
+@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);
}
+ }
}
--- /dev/null
+/* 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;
+}
+@import "../tools";
+
$header_bg: #191919;
$small_logo: .9;
color: #bbb;
position: relative;
- Xtext-align: center;
+ /*text-align: center;*/
@media screen and (min-width: 1024px) {
width: 975px;
margin: auto;
-
}
a {
top: 0;
}
- X#user-info {
+ /*#user-info {
margin-top: 0;
@include size(margin-left, 5px);
@include size(margin-right, 5px);
@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) {
color: #0D7E85;
}
}
- }
+ }*/
form#search-area {
position: relative;
@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;
}
#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;
}
}
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;
}
@mixin mono {
- font-family: "Andale Mono", "Lucida Sans Typewriter", "Courier New";
+ font-family: "Cousine";
}
@mixin hidden-label {
{#% 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>
<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>
# 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
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