From 7ed3cc1134a1b62f53bf246dc8fe0edb05d2d24a Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 23 Oct 2023 08:17:36 +0530 Subject: [PATCH] Allow show/edit of note with item name in addition to item id --- src/calibre/srv/code.py | 18 ++++++++++- src/calibre/srv/content.py | 38 +++++++++++++++++++---- src/pyj/book_list/router.pyj | 7 +++-- src/pyj/book_list/show_note.pyj | 55 ++++++++++++++++++++++++++++----- 4 files changed, 102 insertions(+), 16 deletions(-) diff --git a/src/calibre/srv/code.py b/src/calibre/srv/code.py index 9d48d91ca6..d5327da8ab 100644 --- a/src/calibre/srv/code.py +++ b/src/calibre/srv/code.py @@ -444,5 +444,21 @@ def field_names(ctx, rd, field): ans = all_lang_names() else: db, library_id = get_library_data(ctx, rd)[:2] - ans = tuple(sorted(db.all_field_names(field), key=numeric_sort_key)) + try: + ans = tuple(sorted(db.all_field_names(field), key=numeric_sort_key)) + except ValueError: + raise HTTPNotFound(f'{field} is not a one-one or many-one field') return ans + + +@endpoint('/interface-data/field-id-map/{field}', postprocess=json) +def field_id_map(ctx, rd, field): + ''' + Get a map of all ids:names for the specified field + Optional: ?library_id= + ''' + db, library_id = get_library_data(ctx, rd)[:2] + try: + return db.get_id_map(field) + except ValueError: + raise HTTPNotFound(f'{field} is not a one-one or many-one field') diff --git a/src/calibre/srv/content.py b/src/calibre/srv/content.py index cd44c22e17..1424a1f926 100644 --- a/src/calibre/srv/content.py +++ b/src/calibre/srv/content.py @@ -365,11 +365,7 @@ def resource_hash_to_url(ctx, scheme, digest, library_id): return ctx.url_for('/get-note-resource', **kw) -@endpoint('/get-note/{field}/{item_id}/{library_id=None}', types={'item_id': int}) -def get_note(ctx, rd, field, item_id, library_id): - db = get_db(ctx, rd, library_id) - if db is None: - raise HTTPNotFound(f'Library {library_id} not found') +def _get_note(ctx, rd, db, field, item_id, library_id): note_data = db.notes_data_for(field, item_id) if not note_data: raise HTTPNotFound(f'Note for {field!r}:{item_id!r} not found') @@ -386,13 +382,40 @@ def get_note(ctx, rd, field, item_id, library_id): s, d = m.group(1).split('/', 1) return resource_hash_to_url(ctx, s, d, library_id) note_data['doc'] = pat.sub(sub, html) - rd.outheaders['Content-Type'] = 'text/html; charset=UTF-8' rd.outheaders['Last-Modified'] = http_date(note_data['mtime']) return note_data['doc'] +@endpoint('/get-note/{field}/{item_id}/{library_id=None}', types={'item_id': int}) +def get_note(ctx, rd, field, item_id, library_id): + ''' + Get the note as text/html for the specified field and item id. + ''' + db = get_db(ctx, rd, library_id) + if db is None: + raise HTTPNotFound(f'Library {library_id} not found') + html = _get_note(ctx, rd, db, field, item_id, library_id) + rd.outheaders['Content-Type'] = 'text/html; charset=UTF-8' + return html + + +@endpoint('/get-note-from-item-val/{field}/{item}/{library_id=None}', postprocess=json) +def get_note_from_val(ctx, rd, field, item, library_id): + db = get_db(ctx, rd, library_id) + if db is None: + raise HTTPNotFound(f'Library {library_id} not found') + item_id = db.get_item_id(field, item) + if not item_id: + raise HTTPNotFound(f'Item {field!r}:{item!r} not found') + html = _get_note(ctx, rd, db, field, item_id, library_id) + return {'item_id': item_id, 'html': html} + + @endpoint('/get-note-resource/{scheme}/{digest}/{library_id=None}') def get_note_resource(ctx, rd, scheme, digest, library_id): + ''' + Get the data for a resource in a field note, such as an image. + ''' db = get_db(ctx, rd, library_id) if db is None: raise HTTPNotFound(f'Library {library_id} not found') @@ -409,6 +432,9 @@ def get_note_resource(ctx, rd, scheme, digest, library_id): @endpoint('/set-note/{field}/{item_id}/{library_id=None}', needs_db_write=True, methods={'POST'}, types={'item_id': int}) def set_note(ctx, rd, field, item_id, library_id): + ''' + Set the note for a field as HTML + text + resources. + ''' db = get_db(ctx, rd, library_id) if db is None: raise HTTPNotFound(f'Library {library_id} not found') diff --git a/src/pyj/book_list/router.pyj b/src/pyj/book_list/router.pyj index 4db58fcb18..a33cce8db5 100644 --- a/src/pyj/book_list/router.pyj +++ b/src/pyj/book_list/router.pyj @@ -90,7 +90,10 @@ def open_book_url(book_id, fmt, extra_query): def show_note_url(book_id, field, item_id, item_value, library_id=None, close_action='back'): lid = library_id or current_library_id() ans = absolute_path('') - q = {'book_id':book_id, 'field': field, 'item':item_value, 'item_id': item_id, 'panel': 'show_note', 'close_action': close_action} + q = { + 'book_id':book_id + '', 'field': field, 'item':item_value + '', + 'item_id': item_id + '', 'panel': 'show_note', 'close_action': close_action + } if lid: q.library_id = lid return ans + encode_query(q, '#') @@ -98,7 +101,7 @@ def show_note_url(book_id, field, item_id, item_value, library_id=None, close_ac def show_note(book_id, field, item_id, item_value, replace=False, library_id=None, close_action='back', panel='show_note'): lid = library_id or current_library_id() - q = {'book_id':book_id, 'field': field, 'item':item_value, 'item_id': item_id, 'panel': panel} + q = {'book_id':book_id + '', 'field': field, 'item':item_value + '', 'item_id': item_id + '', 'panel': panel} if panel is 'show_note': q.close_action = close_action if lid: diff --git a/src/pyj/book_list/show_note.pyj b/src/pyj/book_list/show_note.pyj index 9780465ac3..7807142307 100644 --- a/src/pyj/book_list/show_note.pyj +++ b/src/pyj/book_list/show_note.pyj @@ -57,6 +57,27 @@ current_note_markup = None current_container_id = '' +def on_item_val_based_notes_fetched(load_type, xhr, ev): + nonlocal current_note_markup + container = document.getElementById(this) + if not container: + return + q = parse_url_params() + current_note_markup = None + if load_type is 'load': + data = JSON.parse(xhr.responseText) + q.html = data.html + q.item_id = str(data.item_id) + current_note_markup = q + show_note(q.book_id, q.field, q.item_id, q.item, replace=True, library_id=q.library_id or None, close_action=q.close_action, panel=q.panel) + return + old = container.querySelector('div.loading') + p = old.parentNode + p.removeChild(old) + container.appendChild(E.div()) + safe_set_inner_html(container.lastChild, xhr.error_html) + + def on_notes_fetched(load_type, xhr, ev): nonlocal current_note_markup q = parse_url_params() @@ -68,12 +89,13 @@ def on_notes_fetched(load_type, xhr, ev): current_note_markup = None html = xhr.error_html container = document.getElementById(this) + if not container: + return old = container.querySelector('div.loading') p = old.parentNode p.removeChild(old) if q.panel is 'show_note': - iframe = make_iframe(html) - p.appendChild(iframe) + create_note_display(container) elif q.panel is 'edit_note': if current_note_markup: create_editor(container.lastChild, current_note_markup.html) @@ -122,6 +144,21 @@ def get_close_action(): return close_action, close_icon +def create_note_display(container): + add_button(container, 'edit', action=edit_note, tooltip=_('Edit this note [E]')) + html = current_note_markup.html + container.lastChild.appendChild(make_iframe(html)) + + +def reroute_with_item_id(container, q): + container.lastChild.appendChild(E.div(_('Loading') + '…', style='margin: 0.5rem', class_='loading')) + url = 'get-note-from-item-val/' + encodeURIComponent(q.field) + '/' + encodeURIComponent(q.item) + if q.library_id: + url += '/' + encodeURIComponent(q.library_id) + ajax(url, on_item_val_based_notes_fetched.bind(container.id), bypass_cache=False).send() + return + + def init_display_note(container_id): nonlocal current_container_id current_container_id = container_id @@ -129,11 +166,11 @@ def init_display_note(container_id): close_action, close_icon = get_close_action() q = parse_url_params() create_top_bar(container, title=q.item, action=close_action, icon=close_icon) - add_button(container, 'edit', action=edit_note, tooltip=_('Edit this note [E]')) setup_container(container, close_action) + if q.item_id is '0' or q.item_id is 0 or not q.item_id: + return reroute_with_item_id(container, q) if current_note_markup and current_note_markup.library_id is q.library_id and current_note_markup.field is q.field and current_note_markup.item_id is q.item_id: - html = current_note_markup.html - container.lastChild.appendChild(make_iframe(html)) + create_note_display(container) else: container.lastChild.appendChild(E.div(_('Loading') + '…', style='margin: 0.5rem', class_='loading')) url = 'get-note/' + encodeURIComponent(q.field) + '/' + encodeURIComponent(q.item_id) @@ -176,11 +213,12 @@ def init_edit(container_id): nonlocal current_container_id current_container_id = container_id container = document.getElementById(container_id) - close_action, close_icon = get_close_action() q = parse_url_params() + close_action, close_icon = get_close_action() create_top_bar(container, title=_('Edit notes for:') + ' ' + q.item, action=close_action, icon=close_icon, tooltip=_('Discard any changes')) - add_button(container, 'check', action=save.bind(None, container_id), tooltip=_('Save the changes')) setup_container(container, close_action) + if q.item_id is '0' or q.item_id is 0 or not q.item_id: + return reroute_with_item_id(container, q) if current_note_markup and current_note_markup.library_id is q.library_id and current_note_markup.field is q.field and current_note_markup.item_id is q.item_id: create_editor(container.lastChild, current_note_markup.html) else: @@ -192,6 +230,9 @@ def init_edit(container_id): def create_editor(container, html): + bar_container = document.getElementById(current_container_id) + if bar_container: + add_button(bar_container, 'check', action=save.bind(None, bar_container.id), tooltip=_('Save the changes')) c = container.appendChild(E.div(style='flex-grow:10')) editor = create_comments_editor(c, {'insert_image_files': True}) set_comments_html(c, html)