mirror of
				https://github.com/kovidgoyal/calibre.git
				synced 2025-11-03 19:17:02 -05: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>'
 | 
					__copyright__ = '2015, Kovid Goyal <kovid at kovidgoyal.net>'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from functools import partial
 | 
					from functools import partial
 | 
				
			||||||
 | 
					from future_builtins import zip
 | 
				
			||||||
 | 
					from itertools import cycle
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from calibre import force_unicode
 | 
					from calibre import force_unicode
 | 
				
			||||||
from calibre.library.field_metadata import category_icon_map
 | 
					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.ebooks.metadata.book.json_codec import JsonCodec
 | 
				
			||||||
from calibre.srv.errors import HTTPNotFound
 | 
					from calibre.srv.errors import HTTPNotFound
 | 
				
			||||||
from calibre.srv.routes import endpoint, json
 | 
					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.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.srv.utils import http_date, custom_fields_to_display, encode_name, decode_name
 | 
				
			||||||
from calibre.utils.config import prefs, tweaks
 | 
					from calibre.utils.config import prefs, tweaks
 | 
				
			||||||
@ -520,6 +523,29 @@ def books_in(ctx, rd, encoded_category, encoded_item, library_id):
 | 
				
			|||||||
        return result
 | 
					        return result
 | 
				
			||||||
# }}}
 | 
					# }}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# 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=multisort, ids_to_sort=ids)
 | 
				
			||||||
 | 
					    total_num = len(ids)
 | 
				
			||||||
 | 
					    ids = ids[offset:offset+num]
 | 
				
			||||||
 | 
					    return {
 | 
				
			||||||
 | 
					            'total_num': total_num, 'sort_order':sort_order,
 | 
				
			||||||
 | 
					            'offset':offset, 'num':len(ids), 'sort':sort,
 | 
				
			||||||
 | 
					            'base_url':ctx.url_for(search, library_id=db.server_library_id),
 | 
				
			||||||
 | 
					            'query': query,
 | 
				
			||||||
 | 
					            'book_ids':ids
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@endpoint('/ajax/search/{library_id=None}', postprocess=json)
 | 
					@endpoint('/ajax/search/{library_id=None}', postprocess=json)
 | 
				
			||||||
def search(ctx, rd, library_id):
 | 
					def search(ctx, rd, library_id):
 | 
				
			||||||
@ -529,26 +555,43 @@ def search(ctx, rd, library_id):
 | 
				
			|||||||
    Optional: ?num=100&offset=0&sort=title&sort_order=asc&query=
 | 
					    Optional: ?num=100&offset=0&sort=title&sort_order=asc&query=
 | 
				
			||||||
    '''
 | 
					    '''
 | 
				
			||||||
    db = get_db(ctx, library_id)
 | 
					    db = get_db(ctx, library_id)
 | 
				
			||||||
 | 
					    query = rd.query.get('query')
 | 
				
			||||||
 | 
					    num, offset = get_pagination(rd.query)
 | 
				
			||||||
    with db.safe_read_lock:
 | 
					    with db.safe_read_lock:
 | 
				
			||||||
        query = rd.query.get('query')
 | 
					        return _search(ctx, rd, db, query, num, offset, rd.query.get('sort', 'title'), rd.query.get('sort_order', 'asc'))
 | 
				
			||||||
        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():
 | 
					 | 
				
			||||||
            raise HTTPNotFound('%s is not a valid sort field'%sort)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if not query:
 | 
					
 | 
				
			||||||
            ids = ctx.allowed_book_ids(rd, db)
 | 
					@endpoint('/ajax/interface-data/{library_id=None}', postprocess=json)
 | 
				
			||||||
        else:
 | 
					def interface_data(ctx, rd, library_id):
 | 
				
			||||||
            ids = ctx.search(rd, db, query)
 | 
					    '''
 | 
				
			||||||
        ids = db.multisort(fields=[(sfield, sort_order == 'asc')], ids_to_sort=ids)
 | 
					    Return the data needed to create the server main UI
 | 
				
			||||||
        total_num = len(ids)
 | 
					
 | 
				
			||||||
        ids = ids[offset:offset+num]
 | 
					    Optional: ?num=75
 | 
				
			||||||
        return {
 | 
					    '''
 | 
				
			||||||
                'total_num': total_num, 'sort_order':sort_order,
 | 
					    session = rd.session
 | 
				
			||||||
                'offset':offset, 'num':len(ids), 'sort':sort,
 | 
					    ans = {'session_data': {k:session[k] for k in defaults.iterkeys()}}
 | 
				
			||||||
                'base_url':ctx.url_for(search, library_id=db.server_library_id),
 | 
					    sorts, orders = [], []
 | 
				
			||||||
                'query': query,
 | 
					    for x in ans['session_data']['sort'].split(','):
 | 
				
			||||||
                'book_ids':ids
 | 
					        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)
 | 
					        return self.library_broker.get(library_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def allowed_book_ids(self, data, db):
 | 
					    def allowed_book_ids(self, data, db):
 | 
				
			||||||
        # TODO: Implement this based on data.username caching result on the
 | 
					        # TODO: Implement this based on data.username for per-user
 | 
				
			||||||
        # data object
 | 
					        # restrictions. Cache result on the data object
 | 
				
			||||||
        with self.lock:
 | 
					        with self.lock:
 | 
				
			||||||
            ans = data.allowed_book_ids.get(db.server_library_id)
 | 
					            ans = data.allowed_book_ids.get(db.server_library_id)
 | 
				
			||||||
            if ans is None:
 | 
					            if ans is None:
 | 
				
			||||||
 | 
				
			|||||||
@ -11,8 +11,9 @@ from threading import Lock
 | 
				
			|||||||
from calibre.utils.lru_cache import lru_cache
 | 
					from calibre.utils.lru_cache import lru_cache
 | 
				
			||||||
 | 
					
 | 
				
			||||||
defaults = {
 | 
					defaults = {
 | 
				
			||||||
    'sort': 'date:asc',
 | 
					    'sort': 'date:desc',
 | 
				
			||||||
    'library_id': None,
 | 
					    'library_id': None,
 | 
				
			||||||
 | 
					    'view_mode': 'cover_grid',
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Session(object):
 | 
					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
 | 
					# vim:fileencoding=utf-8
 | 
				
			||||||
# License: GPL v3 Copyright: 2015, Kovid Goyal <kovid at kovidgoyal.net>
 | 
					# 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