from django.db import models
from django.db.models import permalink, Q
import django.dispatch
-from django.core.cache import cache
+from django.core.cache import get_cache
from django.core.files.storage import DefaultStorage
from django.utils.translation import ugettext_lazy as _
from django.contrib.auth.models import User
from django.utils.translation import get_language
from django.core.urlresolvers import reverse
from django.db.models.signals import post_save, m2m_changed, pre_delete
+import jsonfield
from django.conf import settings
import re
from os import path
-
import search
# Those are hard-coded here so that makemessages sees them.
('book', _('book')),
)
-# not quite, but Django wants you to set a timeout
-CACHE_FOREVER = 2419200 # 28 days
+
+permanent_cache = get_cache('permanent')
class TagSubcategoryManager(models.Manager):
formats = ebook_formats + ['html', 'xml']
parent = models.ForeignKey('self', blank=True, null=True, related_name='children')
+
+ _related_info = jsonfield.JSONField(blank=True, null=True, editable=False)
+
objects = models.Manager()
tagged = managers.ModelTaggedItemManager(Tag)
tags = managers.TagDescriptor(Tag)
if self.id is None:
return
- cache_key = "Book.short_html/%d/%s"
- for lang, langname in settings.LANGUAGES:
- cache.delete(cache_key % (self.id, lang))
- cache.delete("Book.mini_box/%d" % (self.id, ))
+ type(self).objects.filter(pk=self.pk).update(_related_info=None)
# Fragment.short_html relies on book's tags, so reset it here too
for fragm in self.fragments.all():
fragm.reset_short_html()
- def short_html(self):
- if self.id:
- cache_key = "Book.short_html/%d/%s" % (self.id, get_language())
- short_html = cache.get(cache_key)
- else:
- short_html = None
-
- if short_html is not None:
- return mark_safe(short_html)
- else:
- tags = self.tags.filter(category__in=('author', 'kind', 'genre', 'epoch'))
- tags = split_tags(tags)
-
- formats = {}
- # files generated during publication
- for ebook_format in self.ebook_formats:
- if self.has_media(ebook_format):
- formats[ebook_format] = self.get_media(ebook_format)
-
-
- short_html = unicode(render_to_string('catalogue/book_short.html',
- {'book': self, 'tags': tags, 'formats': formats}))
-
- if self.id:
- cache.set(cache_key, short_html, CACHE_FOREVER)
- return mark_safe(short_html)
-
- def mini_box(self):
- if self.id:
- cache_key = "Book.mini_box/%d" % (self.id, )
- short_html = cache.get(cache_key)
- else:
- short_html = None
-
- if short_html is None:
- authors = self.tags.filter(category='author')
-
- short_html = unicode(render_to_string('catalogue/book_mini_box.html',
- {'book': self, 'authors': authors, 'STATIC_URL': settings.STATIC_URL}))
-
- if self.id:
- cache.set(cache_key, short_html, CACHE_FOREVER)
- return mark_safe(short_html)
-
def has_description(self):
return len(self.description) > 0
has_description.short_description = _('description')
book.build_mobi()
if not settings.NO_SEARCH_INDEX and search_index:
- index_book.delay(book.id, book_info)
+ book.search_index()
+ #index_book.delay(book.id, book_info)
book_descendants = list(book.children.all())
descendants_tags = set()
cls.published.send(sender=book)
return book
+ def related_info(self):
+ """Keeps info about related objects (tags, media) in cache field."""
+ if self._related_info is not None:
+ return self._related_info
+ else:
+ rel = {'tags': {}, 'media': {}}
+ tags = self.tags.filter(category__in=(
+ 'author', 'kind', 'genre', 'epoch'))
+ tags = split_tags(tags)
+ for category in tags:
+ rel['tags'][category] = [
+ (t.name, t.get_absolute_url()) for t in tags[category]]
+ for media_format in BookMedia.formats:
+ rel['media'][media_format] = self.has_media(media_format)
+ if self.pk:
+ type(self).objects.filter(pk=self.pk).update(_related_info=rel)
+ return rel
+
def reset_tag_counter(self):
if self.id is None:
return
cache_key = "Book.tag_counter/%d" % self.id
- cache.delete(cache_key)
+ permanent_cache.delete(cache_key)
if self.parent:
self.parent.reset_tag_counter()
def tag_counter(self):
if self.id:
cache_key = "Book.tag_counter/%d" % self.id
- tags = cache.get(cache_key)
+ tags = permanent_cache.get(cache_key)
else:
tags = None
tags[tag.pk] = 1
if self.id:
- cache.set(cache_key, tags, CACHE_FOREVER)
+ permanent_cache.set(cache_key, tags)
return tags
def reset_theme_counter(self):
return
cache_key = "Book.theme_counter/%d" % self.id
- cache.delete(cache_key)
+ permanent_cache.delete(cache_key)
if self.parent:
self.parent.reset_theme_counter()
def theme_counter(self):
if self.id:
cache_key = "Book.theme_counter/%d" % self.id
- tags = cache.get(cache_key)
+ tags = permanent_cache.get(cache_key)
else:
tags = None
tags[tag.pk] = tags.get(tag.pk, 0) + 1
if self.id:
- cache.set(cache_key, tags, CACHE_FOREVER)
+ permanent_cache.set(cache_key, tags)
return tags
def pretty_title(self, html_links=False):
cache_key = "Fragment.short_html/%d/%s"
for lang, langname in settings.LANGUAGES:
- cache.delete(cache_key % (self.id, lang))
+ permanent_cache.delete(cache_key % (self.id, lang))
def short_html(self):
if self.id:
cache_key = "Fragment.short_html/%d/%s" % (self.id, get_language())
- short_html = cache.get(cache_key)
+ short_html = permanent_cache.get(cache_key)
else:
short_html = None
short_html = unicode(render_to_string('catalogue/fragment_short.html',
{'fragment': self}))
if self.id:
- cache.set(cache_key, short_html, CACHE_FOREVER)
+ permanent_cache.set(cache_key, short_html)
return mark_safe(short_html)
+class Collection(models.Model):
+ """A collection of books, which might be defined before publishing them."""
+ title = models.CharField(_('title'), max_length=120, db_index=True)
+ slug = models.SlugField(_('slug'), max_length=120, primary_key=True)
+ description = models.TextField(_('description'), null=True, blank=True)
+
+ models.SlugField(_('slug'), max_length=120, unique=True, db_index=True)
+ book_slugs = models.TextField(_('book slugs'))
+
+ class Meta:
+ ordering = ('title',)
+ verbose_name = _('collection')
+ verbose_name_plural = _('collections')
+
+ def __unicode__(self):
+ return self.title
+
+
###########
#
# SIGNALS
-.book-wide-box, .book-mini-box, .book-box {
+.book-mini-box, .Book-item {
display: inline-block;
+}
+
+.book-wide-box, .book-box {
margin: 0;
vertical-align: top;
}
.book-wide-box {
width: 98.5em;
+
+ /** This is a fullpage box, it must be aligned with the top menu.
+ This corresponds to a .1em margin below **/
margin-left: -0.1em;
}
+ /*
+ * A mini-box wraps it's contents (image + label) in an <a>
+ * other boxes have an inner box as a wrapper.
+ */
+
+ .book-box-inner {
+ /* min, so it can grow */
+ min-height: 19.75em;
+ margin: .5em;
+ }
+
.book-mini-box a, .book-box-inner {
display: block;
color: black;
border: 1px solid #ddd;
- height: 20em;
+ /* height: 20em; */
padding: .8em 1em;
margin: .1em;
background: #fff;
margin: .1em;
overflow: hidden;
}
- .book-box-inner {
- height: 19.75em;
- margin: .5em;
- }
+
.book-wide-box .book-box-inner {
- height: 24.4em;
+ /* min, so it can grow */
+ min-height: 24.4em;
}
+ /*.book-wide-box.search-result .book-box-inner, .book-wide-box.search-result blockquote {
+ height: auto !important;
+ }*/
+
.book-mini-box img, .book-box img, .book-wide-box img {
width: 13.9em;
height: 19.3em;
.book-box-body {
height: 17em;
overflow: hidden;
+ position: relative;
}
.book-wide-box .book-box-body {
margin-left: 14em;
}
-.book-box-tools a.downarrow:before {
+.book-box-read a:before {
content: "\2609";
font-family: WL-Nav;
font-size: 2.25em;
margin-right: .15em;
vertical-align: middle;
+ font-weight: normal;
+}
+
+.book-box-download a:before {
+ content: "\21E9";
+ font-family: WL-Nav;
+ font-size: 2.25em;
+ margin-right: .15em;
+ vertical-align: middle;
+ font-weight: normal;
}
.book-box-audiobook a:before {
font-size: 2.25em;
margin-right: .15em;
vertical-align: middle;
+ font-weight: normal;
}
ul.book-box-tools {
vertical-align: center;
}
- .book-wide-box blockquote div {
+ .book-wide-box blockquote div.cite-text {
padding: 0.888em;
}
+ .book-wide-box blockquote p.cite-more {
+ display: inline;
+ font-size: 0.611em;
+ float: right;
+ }
+
ul.inline-items, ul.inline-items li {
margin: 0;
padding: 0;
display: inline-block;
}
- .book-wide-box #other-tools {
+ .book-wide-box .other-tools {
float: left;
width: 14.5em;
margin: 6em 0 0 1.5em;
}
- .book-wide-box #other-download {
+ .book-wide-box .other-download {
float: left;
width: 22.5em;
- margin: 6em 1.5em 0em 1.5em
+ margin: 6em 1.5em 0em 1.5em;
}
-
-
+
-}
+.star {
+ font-size: 2.25em;
+ margin-right: .5em;
+ position: absolute;
+ right: 0;
+}
+.star button::-moz-focus-inner {
+ padding: 0;
+ border: 0
+}
+.if-unlike button {
+ font-size: 1em;
+ font-family: inherit;
+ border: 0;
+ background: none;
+ margin: 0;
+ padding: 0;
+}
+
+.if-like a {
+ display:block;
+ text-align:right;
+ padding: 0;
+}
+
+.like .if-unlike {
+ display: none;
+}
+
+.unlike .if-like {
+ display: none;
+}
+
+ .snippets .snippet-text {
+ font-size: 1.2em;
+ margin: 1.083em 0em;
++}