Fixes for book edit with title sort and author sort

This commit is contained in:
Ozzie Isaacs 2021-04-03 14:21:38 +02:00
parent 91b9370a21
commit 665210e506
7 changed files with 88 additions and 68 deletions

View File

@ -27,6 +27,8 @@ import json
from shutil import copyfile from shutil import copyfile
from uuid import uuid4 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 import Blueprint, request, flash, redirect, url_for, abort, Markup, Response
from flask_babel import gettext as _ from flask_babel import gettext as _
from flask_login import current_user, login_required from flask_login import current_user, login_required
@ -447,7 +449,7 @@ def edit_book_comments(comments, book):
return modif_date 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(',') input_languages = languages.split(',')
unknown_languages = [] unknown_languages = []
if not upload: 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) input_l = isoLanguages.get_valid_language_codes(get_locale(), input_languages, unknown_languages)
for l in unknown_languages: for l in unknown_languages:
log.error('%s is not a valid language', l) 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 # ToDo: Not working correct
if upload and len(input_l) == 1: 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 # 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 False
return None return None
def handle_title_on_edit(book, to_save):
def handle_title_on_edit(book, book_title):
# handle book title # handle book title
if book.title != to_save["book_title"].rstrip().strip(): book_title = book_title.rstrip().strip()
if to_save["book_title"] == '': if book.title != book_title:
to_save["book_title"] = _(u'Unknown') if book_title == '':
book.title = to_save["book_title"].rstrip().strip() book_title = _(u'Unknown')
book.title = book_title
return True return True
return False return False
def handle_author_on_edit(book, to_save):
def handle_author_on_edit(book, author_name, update_stored=True):
# handle author(s) # 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)) input_authors = list(map(lambda it: it.strip().replace(',', '|'), input_authors))
# Remove duplicates in authors list # Remove duplicates in authors list
input_authors = helper.uniq(input_authors) 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') 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 # everything then is assembled for sorted author field in database
sort_authors_list = list() sort_authors_list = list()
for inp in input_authors: for inp in input_authors:
@ -677,7 +685,7 @@ def handle_author_on_edit(book, to_save):
stored_author = stored_author.sort stored_author = stored_author.sort
sort_authors_list.append(helper.get_sorted_author(stored_author)) sort_authors_list.append(helper.get_sorted_author(stored_author))
sort_authors = ' & '.join(sort_authors_list) 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 book.author_sort = sort_authors
change = True change = True
return input_authors, change return input_authors, change
@ -718,14 +726,9 @@ def edit_book(book_id):
edited_books_id = None edited_books_id = None
# handle book title # handle book title
if book.title != to_save["book_title"].rstrip().strip(): modif_date |= handle_title_on_edit(book, to_save["book_title"])
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
input_authors, change = handle_author_on_edit(book, to_save) input_authors, change = handle_author_on_edit(book, to_save["author_name"])
if change: if change:
edited_books_id = book.id edited_books_id = book.id
modif_date = True 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") 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)) return redirect(url_for('editbook.edit_book', book_id=book_id))
@editbook.route("/ajax/editbooks/<param>", methods=['POST']) @editbook.route("/ajax/editbooks/<param>", methods=['POST'])
@login_required_if_no_ano @login_required_if_no_ano
@edit_required @edit_required
@ -1048,71 +1052,79 @@ def edit_list_book(param):
ret = "" ret = ""
if param =='series_index': if param =='series_index':
edit_book_series_index(vals['value'], book) 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': elif param =='tags':
edit_book_tags(vals['value'], book) 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') mimetype='application/json')
elif param =='series': elif param =='series':
edit_book_series(vals['value'], book) 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') mimetype='application/json')
elif param =='publishers': elif param =='publishers':
vals['publisher'] = vals['value'] edit_book_publisher(vals['value'], book)
edit_book_publisher(vals, book) ret = Response(json.dumps({'success': True,
ret = Response(json.dumps({'success':True, 'newValue': book.publishers}), 'newValue': ', '.join([publisher.name for publisher in book.publishers])}),
mimetype='application/json') mimetype='application/json')
elif param =='languages': elif param =='languages':
edit_book_languages(vals['value'], book) invalid = list()
# ToDo: Not working edit_book_languages(vals['value'], book, invalid=invalid)
ret = Response(json.dumps({'success':True, 'newValue': ', '.join([lang.name for lang in book.languages])}), if invalid:
mimetype='application/json') 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': elif param =='author_sort':
book.author_sort = vals['value'] 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') mimetype='application/json')
elif param =='title': elif param == 'title':
book.title = vals['value'] sort = book.sort
handle_title_on_edit(book, vals.get('value', ""))
helper.update_dir_stucture(book.id, config.config_calibre_dir) 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') mimetype='application/json')
elif param =='sort': elif param =='sort':
book.sort = vals['value'] 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') mimetype='application/json')
# ToDo: edit books
elif param =='authors': elif param =='authors':
input_authors = vals['value'].split('&') input_authors, __ = handle_author_on_edit(book, vals['value'], vals.get('checkA', None) == "true")
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
helper.update_dir_stucture(book.id, config.config_calibre_dir, input_authors[0]) 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') mimetype='application/json')
book.last_modified = datetime.utcnow() book.last_modified = datetime.utcnow()
calibre_db.session.commit() 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 return ret
@editbook.route("/ajax/sort_value/<field>/<int:bookid>") @editbook.route("/ajax/sort_value/<field>/<int:bookid>")
@login_required @login_required
def get_sorted_entry(field, bookid): 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) book = calibre_db.get_filtered_book(bookid)
if book: if book:
if field == 'title': if field == 'title':
return json.dumps({'sort': book.sort}) return json.dumps({'sort': book.sort})
elif field == 'authors': elif field == 'authors':
return json.dumps({'author_sort': book.author_sort}) 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 "" return ""

View File

@ -498,6 +498,7 @@ def generate_random_password():
def uniq(inpt): def uniq(inpt):
output = [] output = []
inpt = [ " ".join(inp.split()) for inp in inpt]
for x in inpt: for x in inpt:
if x not in output: if x not in output:
output.append(x) output.append(x)

View File

@ -63,7 +63,7 @@ def get_language_codes(locale, language_names, remainder=None):
if v in language_names: if v in language_names:
lang.append(k) lang.append(k)
language_names.remove(v) language_names.remove(v)
if remainder is not None: if remainder is not None and language_names:
remainder.extend(language_names) remainder.extend(language_names)
return lang return lang

View File

@ -95,17 +95,22 @@ $(function() {
mode: "inline", mode: "inline",
emptytext: "<span class='glyphicon glyphicon-plus'></span>", emptytext: "<span class='glyphicon glyphicon-plus'></span>",
success: function (response, __) { success: function (response, __) {
if(!response.success) return response.msg; if (!response.success) return response.msg;
return {newValue: response.newValue}; 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");
var validateText = $(this).attr("data-edit-validate"); if (validateText) {
if (validateText) { element.editable.validate = function (value) {
element.editable.validate = function (value) { if ($.trim(value) === "") return validateText;
if ($.trim(value) === "") return validateText; };
}; }
} }
column.push(element); column.push(element);
}); });
@ -132,7 +137,8 @@ $(function() {
}, },
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
onEditableSave: function (field, row, oldvalue, $el) { 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({ $.ajax({
method:"get", method:"get",
dataType: "json", dataType: "json",
@ -561,7 +567,10 @@ function TableActions (value, row) {
].join(""); ].join("");
} }
function editEntry(param)
{
console.log(param);
}
/* Function for deleting domain restrictions */ /* Function for deleting domain restrictions */
function RestrictionActions (value, row) { function RestrictionActions (value, row) {
return [ return [
@ -598,7 +607,7 @@ function responseHandler(res) {
} }
function singleUserFormatter(value, row) { function singleUserFormatter(value, row) {
return '<button type="button" className="btn btn-default"><a href="/admin/user/' + row.id + '">' + this.buttontext + '</a></button>' return '<a class="btn btn-default" href="/admin/user/' + row.id + '">' + this.buttontext + '</a>'
} }
function checkboxFormatter(value, row, index){ function checkboxFormatter(value, row, index){

View File

@ -30,8 +30,8 @@
<label for="autoupdate_titlesort">{{_('Update Title Sort automatically')}}</label> <label for="autoupdate_titlesort">{{_('Update Title Sort automatically')}}</label>
</div> </div>
<div class="row"> <div class="row">
<input type="checkbox" id="autoupdate_autorsort" name="autoupdate_autorsort" checked> <input type="checkbox" id="autoupdate_authorsort" name="autoupdate_authorsort" checked>
<label for="autoupdate_autorsort">{{_('Update Author Sort automatically')}}</label> <label for="autoupdate_authorsort">{{_('Update Author Sort automatically')}}</label>
</div> </div>
</div> </div>
@ -53,7 +53,7 @@
{{ text_table_row('languages', _('Enter Languages'),_('Languages'), false) }} {{ text_table_row('languages', _('Enter Languages'),_('Languages'), false) }}
<!--th data-field="pubdate" data-type="date" data-visible="{{visiblility.get('pubdate')}}" data-viewformat="dd.mm.yyyy" id="pubdate" data-sortable="true">{{_('Publishing Date')}}</th--> <!--th data-field="pubdate" data-type="date" data-visible="{{visiblility.get('pubdate')}}" data-viewformat="dd.mm.yyyy" id="pubdate" data-sortable="true">{{_('Publishing Date')}}</th-->
{{ text_table_row('publishers', _('Enter Publishers'),_('Publishers'), 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()%}
<th data-align="right" data-formatter="EbookActions" data-switchable="false">{{_('Delete')}}</th> <th data-align="right" data-formatter="EbookActions" data-switchable="false">{{_('Delete')}}</th>
{% endif %} {% endif %}
</tr> </tr>
@ -94,6 +94,4 @@
<script src="{{ url_for('static', filename='js/libs/bootstrap-table/bootstrap-table-editable.min.js') }}"></script> <script src="{{ url_for('static', filename='js/libs/bootstrap-table/bootstrap-table-editable.min.js') }}"></script>
<script src="{{ url_for('static', filename='js/libs/bootstrap-table/bootstrap-editable.min.js') }}"></script> <script src="{{ url_for('static', filename='js/libs/bootstrap-table/bootstrap-editable.min.js') }}"></script>
<script src="{{ url_for('static', filename='js/table.js') }}"></script> <script src="{{ url_for('static', filename='js/table.js') }}"></script>
<script>
</script>
{% endblock %} {% endblock %}

View File

@ -12,7 +12,7 @@
{% endif %} {% endif %}
{% if validate %}data-edit-validate="{{ _('This Field is Required') }}"{% endif %}> {% if validate %}data-edit-validate="{{ _('This Field is Required') }}"{% endif %}>
{% if button %} {% if button %}
<div><div data-id="{{id}}" data-toggle="modal" data-target="#restrictModal" class="btn btn-default button_head disabled" aria-disabled="true">{{edit_text}}</div></div><br> <!--div><button data-id="{{id}}" data-toggle="modal" data-target="#restrictModal" class="btn btn-default button_head disabled" aria-disabled="true">{{edit_text}}</button></div--><br>
{% endif %} {% endif %}
{{ show_text }} {{ show_text }}
</th> </th>

View File

@ -1693,7 +1693,7 @@ msgstr "Speicherort der Calibre-Datenbank"
#: cps/templates/config_edit.html:29 #: cps/templates/config_edit.html:29
msgid "To activate serverside filepicker start Calibre-Web with -f option" 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 #: cps/templates/config_edit.html:35
msgid "Use Google Drive?" msgid "Use Google Drive?"