Fixes for book edit with title sort and author sort
This commit is contained in:
parent
91b9370a21
commit
665210e506
108
cps/editbooks.py
108
cps/editbooks.py
|
@ -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,6 +458,9 @@ 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)
|
||||||
|
if isinstance(invalid, list):
|
||||||
|
invalid.append(l)
|
||||||
|
else:
|
||||||
flash(_(u"%(langname)s is not a valid language", langname=l), category="warning")
|
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:
|
||||||
|
@ -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:
|
||||||
|
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')
|
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 ""
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -95,18 +95,23 @@ $(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){
|
||||||
|
|
|
@ -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 %}
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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?"
|
||||||
|
|
Loading…
Reference in New Issue
Block a user