mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-06-23 15:30:45 -04:00
Start work on UI for new server
This commit is contained in:
parent
c8ad7b203a
commit
0bbc30c07f
Binary file not shown.
@ -7,6 +7,8 @@ __license__ = 'GPL v3'
|
||||
__copyright__ = '2015, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
|
||||
from functools import partial
|
||||
from future_builtins import zip
|
||||
from itertools import cycle
|
||||
|
||||
from calibre import force_unicode
|
||||
from calibre.library.field_metadata import category_icon_map
|
||||
@ -15,6 +17,7 @@ from calibre.ebooks.metadata import title_sort
|
||||
from calibre.ebooks.metadata.book.json_codec import JsonCodec
|
||||
from calibre.srv.errors import HTTPNotFound
|
||||
from calibre.srv.routes import endpoint, json
|
||||
from calibre.srv.session import defaults
|
||||
from calibre.srv.content import get as get_content, icon as get_icon
|
||||
from calibre.srv.utils import http_date, custom_fields_to_display, encode_name, decode_name
|
||||
from calibre.utils.config import prefs, tweaks
|
||||
@ -520,29 +523,20 @@ def books_in(ctx, rd, encoded_category, encoded_item, library_id):
|
||||
return result
|
||||
# }}}
|
||||
|
||||
|
||||
@endpoint('/ajax/search/{library_id=None}', postprocess=json)
|
||||
def search(ctx, rd, library_id):
|
||||
'''
|
||||
Return the books (as list of ids) matching the specified search query.
|
||||
|
||||
Optional: ?num=100&offset=0&sort=title&sort_order=asc&query=
|
||||
'''
|
||||
db = get_db(ctx, library_id)
|
||||
with db.safe_read_lock:
|
||||
query = rd.query.get('query')
|
||||
num, offset = get_pagination(rd.query)
|
||||
sort, sort_order = rd.query.get('sort', 'title'), rd.query.get('sort_order')
|
||||
sort_order = ensure_val(sort_order, 'asc', 'desc')
|
||||
sfield = sanitize_sort_field_name(db.field_metadata, sort)
|
||||
if sfield not in db.field_metadata.sortable_field_keys():
|
||||
# Search {{{
|
||||
def _search(ctx, rd, db, query, num, offset, sort, sort_order):
|
||||
multisort = [(sanitize_sort_field_name(db.field_metadata, s), ensure_val(o, 'asc', 'desc'))
|
||||
for s, o in zip(sort.split(','), cycle(sort_order.split(',')))]
|
||||
skeys = db.field_metadata.sortable_field_keys()
|
||||
for sfield, sorder in multisort:
|
||||
if sfield not in skeys:
|
||||
raise HTTPNotFound('%s is not a valid sort field'%sort)
|
||||
|
||||
if not query:
|
||||
ids = ctx.allowed_book_ids(rd, db)
|
||||
else:
|
||||
ids = ctx.search(rd, db, query)
|
||||
ids = db.multisort(fields=[(sfield, sort_order == 'asc')], ids_to_sort=ids)
|
||||
ids = db.multisort(fields=multisort, ids_to_sort=ids)
|
||||
total_num = len(ids)
|
||||
ids = ids[offset:offset+num]
|
||||
return {
|
||||
@ -552,3 +546,52 @@ def search(ctx, rd, library_id):
|
||||
'query': query,
|
||||
'book_ids':ids
|
||||
}
|
||||
|
||||
@endpoint('/ajax/search/{library_id=None}', postprocess=json)
|
||||
def search(ctx, rd, library_id):
|
||||
'''
|
||||
Return the books (as list of ids) matching the specified search query.
|
||||
|
||||
Optional: ?num=100&offset=0&sort=title&sort_order=asc&query=
|
||||
'''
|
||||
db = get_db(ctx, library_id)
|
||||
query = rd.query.get('query')
|
||||
num, offset = get_pagination(rd.query)
|
||||
with db.safe_read_lock:
|
||||
return _search(ctx, rd, db, query, num, offset, rd.query.get('sort', 'title'), rd.query.get('sort_order', 'asc'))
|
||||
# }}}
|
||||
|
||||
|
||||
@endpoint('/ajax/interface-data/{library_id=None}', postprocess=json)
|
||||
def interface_data(ctx, rd, library_id):
|
||||
'''
|
||||
Return the data needed to create the server main UI
|
||||
|
||||
Optional: ?num=75
|
||||
'''
|
||||
session = rd.session
|
||||
ans = {'session_data': {k:session[k] for k in defaults.iterkeys()}}
|
||||
sorts, orders = [], []
|
||||
for x in ans['session_data']['sort'].split(','):
|
||||
s, o = x.partition(':')[::2]
|
||||
sorts.append(s.strip()), orders.append(o.strip())
|
||||
sort, sort_order = ans['session_data']['sort'].partition(',')[0].partition(':')[::2]
|
||||
try:
|
||||
num = int(rd.query.get('num', 75))
|
||||
except Exception:
|
||||
raise HTTPNotFound('Invalid number of books: %r' % rd.query.get('num'))
|
||||
last_modified = None
|
||||
db = get_db(ctx, library_id)
|
||||
with db.safe_read_lock:
|
||||
ans['search_result'] = _search(ctx, rd, db, '', num, 0, ','.join(sorts), ','.join(orders))
|
||||
ans['field_metadata'] = db.field_metadata
|
||||
# ans['categories'] = ctx.get_categories(rd, db)
|
||||
mdata = ans['metadata'] = {}
|
||||
for book_id in ans['search_result']['book_ids']:
|
||||
data, lm = book_to_json(ctx, rd, db, book_id)
|
||||
last_modified = lm if last_modified is None else max(lm, last_modified)
|
||||
mdata[book_id] = data
|
||||
|
||||
if last_modified is not None:
|
||||
rd.outheaders['Last-Modified'] = http_date(timestampfromdt(last_modified))
|
||||
return ans
|
||||
|
@ -92,8 +92,8 @@ class Context(object):
|
||||
return self.library_broker.get(library_id)
|
||||
|
||||
def allowed_book_ids(self, data, db):
|
||||
# TODO: Implement this based on data.username caching result on the
|
||||
# data object
|
||||
# TODO: Implement this based on data.username for per-user
|
||||
# restrictions. Cache result on the data object
|
||||
with self.lock:
|
||||
ans = data.allowed_book_ids.get(db.server_library_id)
|
||||
if ans is None:
|
||||
|
@ -11,8 +11,9 @@ from threading import Lock
|
||||
from calibre.utils.lru_cache import lru_cache
|
||||
|
||||
defaults = {
|
||||
'sort': 'date:asc',
|
||||
'sort': 'date:desc',
|
||||
'library_id': None,
|
||||
'view_mode': 'cover_grid',
|
||||
}
|
||||
|
||||
class Session(object):
|
||||
|
14
src/pyj/ajax.pyj
Normal file
14
src/pyj/ajax.pyj
Normal file
@ -0,0 +1,14 @@
|
||||
# vim:fileencoding=utf-8
|
||||
# License: GPL v3 Copyright: 2015, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
|
||||
def ajax(path, on_complete, on_progress=None, bypass_cache=True):
|
||||
xhr = XMLHttpRequest()
|
||||
if bypass_cache:
|
||||
path += ('&' if '?' in path else '?') + Date().getTime()
|
||||
if on_progress:
|
||||
xhr.addEventListener('progress', on_progress)
|
||||
xhr.addEventListener('abort', def(ev): on_complete('abort', this, xhr);)
|
||||
xhr.addEventListener('error', def(ev): on_complete('error', this, xhr);)
|
||||
xhr.addEventListener('load', def(ev): on_complete('load', this, xhr);)
|
||||
return xhr
|
||||
|
17
src/pyj/dom.pyj
Normal file
17
src/pyj/dom.pyj
Normal file
@ -0,0 +1,17 @@
|
||||
# vim:fileencoding=utf-8
|
||||
# License: GPL v3 Copyright: 2015, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
|
||||
simple_vendor_prefixes = {
|
||||
'transform': v"['webkit', 'ms']",
|
||||
}
|
||||
|
||||
def set_css(elem, **kw):
|
||||
s = elem.style
|
||||
if s:
|
||||
for prop in kw:
|
||||
name, val = str.replace(prop, '_', '-'), kw[prop]
|
||||
s.setProperty(name, val)
|
||||
prefixes = simple_vendor_prefixes[name]
|
||||
if prefixes:
|
||||
for prefix in prefixes:
|
||||
s.setProperty('-' + prefix + '-' + name, val)
|
@ -1,5 +1,15 @@
|
||||
# vim:fileencoding=utf-8
|
||||
# License: GPL v3 Copyright: 2015, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
|
||||
alert('hello world!')
|
||||
from elementmaker import E
|
||||
|
||||
def create_page_load_progress_bar():
|
||||
E.progress(id='library_load_bar')
|
||||
|
||||
def on_document_loaded():
|
||||
alert(11111111)
|
||||
|
||||
# We wait for all page elements to load, since this is a single page app
|
||||
# with a largely empty starting document, we can use this to preload any resources
|
||||
# we know are going to be needed immediately.
|
||||
window.addEventListener("load", on_document_loaded)
|
||||
|
Loading…
x
Reference in New Issue
Block a user