mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
...
This commit is contained in:
parent
8f82dc3713
commit
7e6c93504c
@ -259,4 +259,15 @@ h2.library_name {
|
|||||||
|
|
||||||
/* }}} */
|
/* }}} */
|
||||||
|
|
||||||
|
/* Booklist {{{ */
|
||||||
|
|
||||||
|
#booklist .page {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading img {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* }}} */
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ __docformat__ = 'restructuredtext en'
|
|||||||
|
|
||||||
import operator, os, json
|
import operator, os, json
|
||||||
from urllib import quote
|
from urllib import quote
|
||||||
from binascii import hexlify
|
from binascii import hexlify, unhexlify
|
||||||
|
|
||||||
import cherrypy
|
import cherrypy
|
||||||
|
|
||||||
@ -15,64 +15,33 @@ from calibre.constants import filesystem_encoding
|
|||||||
from calibre import isbytestring, force_unicode, prepare_string_for_xml as xml
|
from calibre import isbytestring, force_unicode, prepare_string_for_xml as xml
|
||||||
from calibre.utils.ordered_dict import OrderedDict
|
from calibre.utils.ordered_dict import OrderedDict
|
||||||
|
|
||||||
def paginate(offsets, content, base_url, up_url=None): # {{{
|
def render_book_list(ids):
|
||||||
'Create markup for pagination'
|
pages = []
|
||||||
|
while ids:
|
||||||
if '?' not in base_url:
|
page = list(ids[:25])
|
||||||
base_url += '?'
|
pages.append(page)
|
||||||
|
ids = ids[25:]
|
||||||
if base_url[-1] != '?':
|
page_template = u'''\
|
||||||
base_url += '&'
|
<div class="page" id="page{0}">
|
||||||
|
<div class="load_data" title="{1}"></div>
|
||||||
def navlink(decoration, name, cls, offset):
|
<div class="loading"><img src="/static/loading.gif" /> {2}</div>
|
||||||
label = xml(name)
|
<div class="loaded"></div>
|
||||||
if cls in ('next', 'last'):
|
|
||||||
label += ' ' + decoration
|
|
||||||
else:
|
|
||||||
label = decoration + ' ' + label
|
|
||||||
return (u'<a class="{cls}" href="{base_url}&offset={offset}" title={name}>'
|
|
||||||
u'{label}</a>').format(cls=cls, decoration=decoration,
|
|
||||||
name=xml(name, True), offset=offset,
|
|
||||||
base_url=xml(base_url, True), label=label)
|
|
||||||
left = ''
|
|
||||||
if offsets.offset > 0 and offsets.previous_offset > 0:
|
|
||||||
left += navlink(u'\u219e', _('First'), 'first', 0)
|
|
||||||
if offsets.offset > 0:
|
|
||||||
left += ' ' + navlink('←', _('Previous'), 'previous',
|
|
||||||
offsets.previous_offset)
|
|
||||||
|
|
||||||
middle = ''
|
|
||||||
if up_url:
|
|
||||||
middle = '<a href="{0}" title="{1}">[{1} ↑]</a>'.format(xml(up_url, True),
|
|
||||||
xml(_('Up')))
|
|
||||||
|
|
||||||
right = ''
|
|
||||||
if offsets.next_offset > -1:
|
|
||||||
right += navlink('&rarr', _('Next'), 'next', offsets.next_offset)
|
|
||||||
if offsets.last_offset > offsets.next_offset and offsets.last_offset > 0:
|
|
||||||
right += ' ' + navlink(u'\u21A0', _('Last'), 'last', offsets.last_offset)
|
|
||||||
|
|
||||||
navbar = u'''
|
|
||||||
<table class="navbar">
|
|
||||||
<tr>
|
|
||||||
<td class="left">{left}</td>
|
|
||||||
<td class="middle">{middle}</td>
|
|
||||||
<td class="right">{right}</td>
|
|
||||||
</tr>
|
|
||||||
<table>
|
|
||||||
'''.format(left=left, right=right, middle=middle)
|
|
||||||
|
|
||||||
templ = u'''
|
|
||||||
<div class="page">
|
|
||||||
{navbar}
|
|
||||||
<div class="page-contents">
|
|
||||||
{content}
|
|
||||||
</div>
|
|
||||||
{navbar}
|
|
||||||
</div>
|
</div>
|
||||||
'''
|
'''
|
||||||
return templ.format(navbar=navbar, content=content)
|
rpages = []
|
||||||
# }}}
|
for i, pg in enumerate(pages):
|
||||||
|
ld = xml(json.dumps(pg), True)
|
||||||
|
rpages.append(page_template.format(i, ld,
|
||||||
|
xml(_('Loading, please wait')) + '…'))
|
||||||
|
rpages = u'\n\n'.join(rpages)
|
||||||
|
|
||||||
|
templ = u'''\
|
||||||
|
<h3>{0}</h3>
|
||||||
|
<div id="booklist">
|
||||||
|
{pages}
|
||||||
|
</div>
|
||||||
|
'''
|
||||||
|
return templ.format(_('Browsing %d books')%len(ids), pages=rpages)
|
||||||
|
|
||||||
def utf8(x): # {{{
|
def utf8(x): # {{{
|
||||||
if isinstance(x, unicode):
|
if isinstance(x, unicode):
|
||||||
@ -171,10 +140,15 @@ class BrowseServer(object):
|
|||||||
connect('browse_category_group',
|
connect('browse_category_group',
|
||||||
base_href+'/category_group/{category}/{group}',
|
base_href+'/category_group/{category}/{group}',
|
||||||
self.browse_category_group)
|
self.browse_category_group)
|
||||||
connect('browse_list', base_href+'/list/{query}', self.browse_list)
|
connect('browse_matches',
|
||||||
|
base_href+'/matches/{category}/{cid}',
|
||||||
|
self.browse_matches)
|
||||||
|
connect('browse_booklist_page',
|
||||||
|
base_href+'/booklist_page',
|
||||||
|
self.browse_booklist_page)
|
||||||
|
|
||||||
connect('browse_search', base_href+'/search/{query}',
|
connect('browse_search', base_href+'/search/{query}',
|
||||||
self.browse_search)
|
self.browse_search)
|
||||||
connect('browse_book', base_href+'/book/{uuid}', self.browse_book)
|
|
||||||
|
|
||||||
def browse_template(self, sort, category=True):
|
def browse_template(self, sort, category=True):
|
||||||
|
|
||||||
@ -267,12 +241,12 @@ class BrowseServer(object):
|
|||||||
|
|
||||||
def browse_category(self, category, sort):
|
def browse_category(self, category, sort):
|
||||||
categories = self.categories_cache()
|
categories = self.categories_cache()
|
||||||
|
if category not in categories:
|
||||||
|
raise cherrypy.HTTPError(404, 'category not found')
|
||||||
category_meta = self.db.field_metadata
|
category_meta = self.db.field_metadata
|
||||||
category_name = category_meta[category]['name']
|
category_name = category_meta[category]['name']
|
||||||
datatype = category_meta[category]['datatype']
|
datatype = category_meta[category]['datatype']
|
||||||
|
|
||||||
if category not in categories:
|
|
||||||
raise cherrypy.HTTPError(404, 'category not found')
|
|
||||||
|
|
||||||
items = categories[category]
|
items = categories[category]
|
||||||
sort = self.browse_sort_categories(items, sort)
|
sort = self.browse_sort_categories(items, sort)
|
||||||
@ -331,11 +305,12 @@ class BrowseServer(object):
|
|||||||
if sort not in ('rating', 'name', 'popularity'):
|
if sort not in ('rating', 'name', 'popularity'):
|
||||||
sort = 'name'
|
sort = 'name'
|
||||||
categories = self.categories_cache()
|
categories = self.categories_cache()
|
||||||
|
if category not in categories:
|
||||||
|
raise cherrypy.HTTPError(404, 'category not found')
|
||||||
|
|
||||||
category_meta = self.db.field_metadata
|
category_meta = self.db.field_metadata
|
||||||
datatype = category_meta[category]['datatype']
|
datatype = category_meta[category]['datatype']
|
||||||
|
|
||||||
if category not in categories:
|
|
||||||
raise cherrypy.HTTPError(404, 'category not found')
|
|
||||||
if not group:
|
if not group:
|
||||||
raise cherrypy.HTTPError(404, 'invalid group')
|
raise cherrypy.HTTPError(404, 'invalid group')
|
||||||
|
|
||||||
@ -360,6 +335,8 @@ class BrowseServer(object):
|
|||||||
'Entry point for top-level, categories and sub-categories'
|
'Entry point for top-level, categories and sub-categories'
|
||||||
if category == None:
|
if category == None:
|
||||||
ans = self.browse_toplevel()
|
ans = self.browse_toplevel()
|
||||||
|
elif category == 'newest':
|
||||||
|
raise cherrypy.InternalRedirect('/browse/matches/newest/dummy')
|
||||||
else:
|
else:
|
||||||
ans = self.browse_category(category, category_sort)
|
ans = self.browse_category(category, category_sort)
|
||||||
|
|
||||||
@ -368,8 +345,63 @@ class BrowseServer(object):
|
|||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
# Book Lists {{{
|
# Book Lists {{{
|
||||||
def browse_list(self, query=None, offset=0, sort=None):
|
|
||||||
raise NotImplementedError()
|
def browse_sort_book_list(self, items, sort):
|
||||||
|
fm = self.db.field_metadata
|
||||||
|
keys = frozenset(fm.sortable_field_keys())
|
||||||
|
if sort not in keys:
|
||||||
|
sort = 'title'
|
||||||
|
self.sort(items, 'title', True)
|
||||||
|
if sort != 'title':
|
||||||
|
ascending = fm[sort]['datatype'] not in ('rating', 'datetime')
|
||||||
|
self.sort(items, sort, ascending)
|
||||||
|
return sort
|
||||||
|
|
||||||
|
@Endpoint(sort_type='list')
|
||||||
|
def browse_matches(self, category=None, cid=None, list_sort=None):
|
||||||
|
if not cid:
|
||||||
|
raise cherrypy.HTTPError(404, 'invalid category id: %r'%cid)
|
||||||
|
categories = self.categories_cache()
|
||||||
|
|
||||||
|
if category not in categories and category != 'newest':
|
||||||
|
raise cherrypy.HTTPError(404, 'category not found')
|
||||||
|
try:
|
||||||
|
category_name = self.db.field_metadata[category]['name']
|
||||||
|
except:
|
||||||
|
if category != 'newest':
|
||||||
|
raise
|
||||||
|
category_name = _('Newest')
|
||||||
|
|
||||||
|
if category == 'search':
|
||||||
|
which = unhexlify(cid)
|
||||||
|
try:
|
||||||
|
ids = self.search_cache('search:"%s"'%which)
|
||||||
|
except:
|
||||||
|
raise cherrypy.HTTPError(404, 'Search: %r not understood'%which)
|
||||||
|
elif category == 'newest':
|
||||||
|
ids = list(self.db.data.iterallids())
|
||||||
|
else:
|
||||||
|
ids = self.db.get_books_for_category(category, cid)
|
||||||
|
|
||||||
|
items = [self.db.data._data[x] for x in ids]
|
||||||
|
if category == 'newest':
|
||||||
|
list_sort = 'timestamp'
|
||||||
|
sort = self.browse_sort_book_list(items, list_sort)
|
||||||
|
ids = [x[0] for x in items]
|
||||||
|
html = render_book_list(ids)
|
||||||
|
return self.browse_template(sort).format(
|
||||||
|
title=_('Books in') + " " +category_name,
|
||||||
|
script='booklist();', main=html)
|
||||||
|
|
||||||
|
@Endpoint(mimetype='application/json; charset=utf-8', sort_type='list')
|
||||||
|
def browse_booklist_page(self, ids=None, list_sort=None):
|
||||||
|
if ids is None:
|
||||||
|
ids = json.dumps('[]')
|
||||||
|
try:
|
||||||
|
ids = json.loads(ids)
|
||||||
|
except:
|
||||||
|
raise cherrypy.HTTPError(404, 'invalid ids')
|
||||||
|
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
# Search {{{
|
# Search {{{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user