From 665210e5062818a7035a9a1d6d47937579d72c63 Mon Sep 17 00:00:00 2001 From: Ozzie Isaacs Date: Sat, 3 Apr 2021 14:21:38 +0200 Subject: [PATCH] Fixes for book edit with title sort and author sort --- cps/editbooks.py | 112 +++++++++++--------- cps/helper.py | 1 + cps/isoLanguages.py | 2 +- cps/static/js/table.js | 29 +++-- cps/templates/book_table.html | 8 +- cps/templates/user_table.html | 2 +- cps/translations/de/LC_MESSAGES/messages.po | 2 +- 7 files changed, 88 insertions(+), 68 deletions(-) diff --git a/cps/editbooks.py b/cps/editbooks.py index 2a3f6c64..5fedee50 100644 --- a/cps/editbooks.py +++ b/cps/editbooks.py @@ -27,6 +27,8 @@ import json from shutil import copyfile from uuid import uuid4 +from babel import Locale as LC +from babel.core import UnknownLocaleError from flask import Blueprint, request, flash, redirect, url_for, abort, Markup, Response from flask_babel import gettext as _ from flask_login import current_user, login_required @@ -447,7 +449,7 @@ def edit_book_comments(comments, book): return modif_date -def edit_book_languages(languages, book, upload=False): +def edit_book_languages(languages, book, upload=False, invalid=None): input_languages = languages.split(',') unknown_languages = [] if not upload: @@ -456,7 +458,10 @@ def edit_book_languages(languages, book, upload=False): input_l = isoLanguages.get_valid_language_codes(get_locale(), input_languages, unknown_languages) for l in unknown_languages: log.error('%s is not a valid language', l) - flash(_(u"%(langname)s is not a valid language", langname=l), category="warning") + if isinstance(invalid, list): + invalid.append(l) + else: + flash(_(u"%(langname)s is not a valid language", langname=l), category="warning") # ToDo: Not working correct if upload and len(input_l) == 1: # If the language of the file is excluded from the users view, it's not imported, to allow the user to view @@ -645,18 +650,21 @@ def upload_cover(request, book): return False return None -def handle_title_on_edit(book, to_save): + +def handle_title_on_edit(book, book_title): # handle book title - if book.title != to_save["book_title"].rstrip().strip(): - if to_save["book_title"] == '': - to_save["book_title"] = _(u'Unknown') - book.title = to_save["book_title"].rstrip().strip() + book_title = book_title.rstrip().strip() + if book.title != book_title: + if book_title == '': + book_title = _(u'Unknown') + book.title = book_title return True return False -def handle_author_on_edit(book, to_save): + +def handle_author_on_edit(book, author_name, update_stored=True): # handle author(s) - input_authors = to_save["author_name"].split('&') + input_authors = author_name.split('&') input_authors = list(map(lambda it: it.strip().replace(',', '|'), input_authors)) # Remove duplicates in authors list input_authors = helper.uniq(input_authors) @@ -666,7 +674,7 @@ def handle_author_on_edit(book, to_save): change = modify_database_object(input_authors, book.authors, db.Authors, calibre_db.session, 'author') - # Search for each author if author is in database, if not, authorname and sorted authorname is generated new + # Search for each author if author is in database, if not, author name and sorted author name is generated new # everything then is assembled for sorted author field in database sort_authors_list = list() for inp in input_authors: @@ -677,7 +685,7 @@ def handle_author_on_edit(book, to_save): stored_author = stored_author.sort sort_authors_list.append(helper.get_sorted_author(stored_author)) sort_authors = ' & '.join(sort_authors_list) - if book.author_sort != sort_authors: + if book.author_sort != sort_authors and update_stored: book.author_sort = sort_authors change = True return input_authors, change @@ -718,14 +726,9 @@ def edit_book(book_id): edited_books_id = None # handle book title - if book.title != to_save["book_title"].rstrip().strip(): - if to_save["book_title"] == '': - to_save["book_title"] = _(u'Unknown') - book.title = to_save["book_title"].rstrip().strip() - edited_books_id = book.id - modif_date = True + modif_date |= handle_title_on_edit(book, to_save["book_title"]) - input_authors, change = handle_author_on_edit(book, to_save) + input_authors, change = handle_author_on_edit(book, to_save["author_name"]) if change: edited_books_id = book.id modif_date = True @@ -1039,6 +1042,7 @@ def convert_bookformat(book_id): flash(_(u"There was an error converting this book: %(res)s", res=rtn), category="error") return redirect(url_for('editbook.edit_book', book_id=book_id)) + @editbook.route("/ajax/editbooks/", methods=['POST']) @login_required_if_no_ano @edit_required @@ -1048,71 +1052,79 @@ def edit_list_book(param): ret = "" if param =='series_index': edit_book_series_index(vals['value'], book) - ret = Response(json.dumps({'success':True, 'newValue': book.series_index}), mimetype='application/json') + ret = Response(json.dumps({'success': True, 'newValue': book.series_index}), mimetype='application/json') elif param =='tags': edit_book_tags(vals['value'], book) - ret = Response(json.dumps({'success':True, 'newValue': ', '.join([tag.name for tag in book.tags])}), + ret = Response(json.dumps({'success': True, 'newValue': ', '.join([tag.name for tag in book.tags])}), mimetype='application/json') elif param =='series': edit_book_series(vals['value'], book) - ret = Response(json.dumps({'success':True, 'newValue': ', '.join([serie.name for serie in book.series])}), + ret = Response(json.dumps({'success': True, 'newValue': ', '.join([serie.name for serie in book.series])}), mimetype='application/json') elif param =='publishers': - vals['publisher'] = vals['value'] - edit_book_publisher(vals, book) - ret = Response(json.dumps({'success':True, 'newValue': book.publishers}), + edit_book_publisher(vals['value'], book) + ret = Response(json.dumps({'success': True, + 'newValue': ', '.join([publisher.name for publisher in book.publishers])}), mimetype='application/json') elif param =='languages': - edit_book_languages(vals['value'], book) - # ToDo: Not working - ret = Response(json.dumps({'success':True, 'newValue': ', '.join([lang.name for lang in book.languages])}), - mimetype='application/json') + invalid = list() + edit_book_languages(vals['value'], book, invalid=invalid) + if invalid: + ret = Response(json.dumps({'success': False, + 'msg': 'Invalid languages in request: {}'.format(','.join(invalid))}), + mimetype='application/json') + else: + lang_names = list() + for lang in book.languages: + try: + lang_names.append(LC.parse(lang.lang_code).get_language_name(get_locale())) + except UnknownLocaleError: + lang_names.append(_(isoLanguages.get(part3=lang.lang_code).name)) + ret = Response(json.dumps({'success': True, 'newValue': ', '.join(lang_names)}), + mimetype='application/json') elif param =='author_sort': book.author_sort = vals['value'] - ret = Response(json.dumps({'success':True, 'newValue': book.author_sort}), + ret = Response(json.dumps({'success': True, 'newValue': book.author_sort}), mimetype='application/json') - elif param =='title': - book.title = vals['value'] + elif param == 'title': + sort = book.sort + handle_title_on_edit(book, vals.get('value', "")) helper.update_dir_stucture(book.id, config.config_calibre_dir) - ret = Response(json.dumps({'success':True, 'newValue': book.title}), + ret = Response(json.dumps({'success': True, 'newValue': book.title}), mimetype='application/json') elif param =='sort': book.sort = vals['value'] - ret = Response(json.dumps({'success':True, 'newValue': book.sort}), + ret = Response(json.dumps({'success': True, 'newValue': book.sort}), mimetype='application/json') - # ToDo: edit books elif param =='authors': - input_authors = vals['value'].split('&') - input_authors = list(map(lambda it: it.strip().replace(',', '|'), input_authors)) - modify_database_object(input_authors, book.authors, db.Authors, calibre_db.session, 'author') - sort_authors_list = list() - for inp in input_authors: - stored_author = calibre_db.session.query(db.Authors).filter(db.Authors.name == inp).first() - if not stored_author: - stored_author = helper.get_sorted_author(inp) - else: - stored_author = stored_author.sort - sort_authors_list.append(helper.get_sorted_author(stored_author)) - sort_authors = ' & '.join(sort_authors_list) - if book.author_sort != sort_authors: - book.author_sort = sort_authors + input_authors, __ = handle_author_on_edit(book, vals['value'], vals.get('checkA', None) == "true") helper.update_dir_stucture(book.id, config.config_calibre_dir, input_authors[0]) - ret = Response(json.dumps({'success':True, 'newValue': ', '.join([author.name for author in book.authors])}), + ret = Response(json.dumps({'success': True, + 'newValue': ' & '.join([author.replace('|',',') for author in input_authors])}), mimetype='application/json') book.last_modified = datetime.utcnow() calibre_db.session.commit() + # revert change for sort if automatic fields link is deactivated + if param == 'title' and vals.get('checkT') == "false": + book.sort = sort + calibre_db.session.commit() return ret + @editbook.route("/ajax/sort_value//") @login_required def get_sorted_entry(field, bookid): - if field == 'title' or field == 'authors': + if field in ['title', 'authors', 'sort', 'author_sort']: book = calibre_db.get_filtered_book(bookid) if book: if field == 'title': return json.dumps({'sort': book.sort}) elif field == 'authors': return json.dumps({'author_sort': book.author_sort}) + if field == 'sort': + return json.dumps({'sort': book.title}) + if field == 'author_sort': + return json.dumps({'author_sort': book.author}) return "" diff --git a/cps/helper.py b/cps/helper.py index d7be6dfe..f5e57e02 100644 --- a/cps/helper.py +++ b/cps/helper.py @@ -498,6 +498,7 @@ def generate_random_password(): def uniq(inpt): output = [] + inpt = [ " ".join(inp.split()) for inp in inpt] for x in inpt: if x not in output: output.append(x) diff --git a/cps/isoLanguages.py b/cps/isoLanguages.py index e447a623..35d9f0a7 100644 --- a/cps/isoLanguages.py +++ b/cps/isoLanguages.py @@ -63,7 +63,7 @@ def get_language_codes(locale, language_names, remainder=None): if v in language_names: lang.append(k) language_names.remove(v) - if remainder is not None: + if remainder is not None and language_names: remainder.extend(language_names) return lang diff --git a/cps/static/js/table.js b/cps/static/js/table.js index 0982f374..6eed51f0 100644 --- a/cps/static/js/table.js +++ b/cps/static/js/table.js @@ -95,17 +95,22 @@ $(function() { mode: "inline", emptytext: "", success: function (response, __) { - if(!response.success) return response.msg; + if (!response.success) return response.msg; return {newValue: response.newValue}; + }, + params: function (params) { + params.checkA = $('#autoupdate_authorsort').prop('checked'); + params.checkT = $('#autoupdate_titlesort').prop('checked'); + return params } } }; - } - var validateText = $(this).attr("data-edit-validate"); - if (validateText) { - element.editable.validate = function (value) { - if ($.trim(value) === "") return validateText; - }; + var validateText = $(this).attr("data-edit-validate"); + if (validateText) { + element.editable.validate = function (value) { + if ($.trim(value) === "") return validateText; + }; + } } column.push(element); }); @@ -132,7 +137,8 @@ $(function() { }, // eslint-disable-next-line no-unused-vars onEditableSave: function (field, row, oldvalue, $el) { - if (field === "title" || field === "authors") { + if ($.inArray(field, [ "title", "sort" ]) !== -1 && $('#autoupdate_titlesort').prop('checked') + || $.inArray(field, [ "authors", "author_sort" ]) !== -1 && $('#autoupdate_authorsort').prop('checked')) { $.ajax({ method:"get", dataType: "json", @@ -561,7 +567,10 @@ function TableActions (value, row) { ].join(""); } - +function editEntry(param) +{ + console.log(param); +} /* Function for deleting domain restrictions */ function RestrictionActions (value, row) { return [ @@ -598,7 +607,7 @@ function responseHandler(res) { } function singleUserFormatter(value, row) { - return '' + return '' + this.buttontext + '' } function checkboxFormatter(value, row, index){ diff --git a/cps/templates/book_table.html b/cps/templates/book_table.html index ecd840b5..6a31c235 100644 --- a/cps/templates/book_table.html +++ b/cps/templates/book_table.html @@ -30,8 +30,8 @@
- - + +
@@ -53,7 +53,7 @@ {{ text_table_row('languages', _('Enter Languages'),_('Languages'), false) }} {{ text_table_row('publishers', _('Enter Publishers'),_('Publishers'), false) }} - {% if g.user.role_edit() %} + {% if g.user.role_delete_books() and g.user.role_edit()%} {{_('Delete')}} {% endif %} @@ -94,6 +94,4 @@ - {% endblock %} diff --git a/cps/templates/user_table.html b/cps/templates/user_table.html index 9817faa6..318f873a 100644 --- a/cps/templates/user_table.html +++ b/cps/templates/user_table.html @@ -12,7 +12,7 @@ {% endif %} {% if validate %}data-edit-validate="{{ _('This Field is Required') }}"{% endif %}> {% if button %} -
{{edit_text}}

+
{% endif %} {{ show_text }} diff --git a/cps/translations/de/LC_MESSAGES/messages.po b/cps/translations/de/LC_MESSAGES/messages.po index 29f42ad1..603cd586 100644 --- a/cps/translations/de/LC_MESSAGES/messages.po +++ b/cps/translations/de/LC_MESSAGES/messages.po @@ -1693,7 +1693,7 @@ msgstr "Speicherort der Calibre-Datenbank" #: cps/templates/config_edit.html:29 msgid "To activate serverside filepicker start Calibre-Web with -f option" -msgstr "Calibre-Web mit -f Option starten um die serverseitige Dateiauswahl zu aktivieren" +msgstr "Calibre-Web mit -f Option starten, um die serverseitige Dateiauswahl zu aktivieren" #: cps/templates/config_edit.html:35 msgid "Use Google Drive?"