Update mass edit
Refactored delete User function Updated testresults
This commit is contained in:
421
cps/editbooks.py
421
cps/editbooks.py
@@ -73,17 +73,18 @@ def edit_required(f):
|
||||
return inner
|
||||
|
||||
|
||||
@editbook.route("/ajax/delete/<int:book_id>", methods=["POST"])
|
||||
@editbook.route("/ajax/deletebook", methods=["POST"])
|
||||
@user_login_required
|
||||
def delete_book_from_details(book_id):
|
||||
return delete_book_from_table(book_id, "", True) # , mimetype='application/json')
|
||||
def delete_books_ajax():
|
||||
book_ids = request.get_json().get("bookid")
|
||||
return check_delete_book(book_ids, "", True)
|
||||
|
||||
|
||||
@editbook.route("/delete/<int:book_id>", defaults={'book_format': ""}, methods=["POST"])
|
||||
@editbook.route("/delete/<int:book_id>/<string:book_format>", methods=["POST"])
|
||||
@user_login_required
|
||||
def delete_book_ajax(book_id, book_format):
|
||||
return delete_book_from_table(book_id, book_format, False, request.form.to_dict().get('location', ""))
|
||||
def delete_book(book_id, book_format):
|
||||
return check_delete_book(book_id, book_format, False, request.form.to_dict().get('location', ""))
|
||||
|
||||
|
||||
@editbook.route("/admin/book/<int:book_id>", methods=['GET'])
|
||||
@@ -213,7 +214,7 @@ def table_get_custom_enum(c_id):
|
||||
@login_required_if_no_ano
|
||||
@edit_required
|
||||
def edit_list_book(param):
|
||||
vals = request.form.to_dict()
|
||||
vals = request.get_json() # form.to_dict(flat=False)
|
||||
return edit_book_param(param, vals)
|
||||
|
||||
@editbook.route("/ajax/editselectedbooks", methods=['POST'])
|
||||
@@ -233,42 +234,39 @@ def edit_selected_books():
|
||||
comments = d.get('comments')
|
||||
checkA = d.get('checkA')
|
||||
|
||||
if len(selections) != 0:
|
||||
for book_id in selections:
|
||||
vals = {
|
||||
"pk": book_id,
|
||||
"value": None,
|
||||
"checkA": checkA,
|
||||
}
|
||||
if title:
|
||||
vals['value'] = title
|
||||
edit_book_param('title', vals)
|
||||
if title_sort:
|
||||
vals['value'] = title_sort
|
||||
edit_book_param('sort', vals)
|
||||
if author_sort:
|
||||
vals['value'] = author_sort
|
||||
edit_book_param('author_sort', vals)
|
||||
if authors:
|
||||
vals['value'] = authors
|
||||
edit_book_param('authors', vals)
|
||||
if categories:
|
||||
vals['value'] = categories
|
||||
edit_book_param('tags', vals)
|
||||
if series:
|
||||
vals['value'] = series
|
||||
edit_book_param('series', vals)
|
||||
if languages:
|
||||
vals['value'] = languages
|
||||
edit_book_param('languages', vals)
|
||||
if publishers:
|
||||
vals['value'] = publishers
|
||||
edit_book_param('publishers', vals)
|
||||
if comments:
|
||||
vals['value'] = comments
|
||||
edit_book_param('comments', vals)
|
||||
return json.dumps({'success': True})
|
||||
return ""
|
||||
vals = {
|
||||
"pk": selections,
|
||||
"value": None,
|
||||
"checkA": checkA,
|
||||
}
|
||||
if title:
|
||||
vals['value'] = title
|
||||
edit_book_param('title', vals)
|
||||
if title_sort:
|
||||
vals['value'] = title_sort
|
||||
edit_book_param('sort', vals)
|
||||
if author_sort:
|
||||
vals['value'] = author_sort
|
||||
edit_book_param('author_sort', vals)
|
||||
if authors:
|
||||
vals['value'] = authors
|
||||
edit_book_param('authors', vals)
|
||||
if categories:
|
||||
vals['value'] = categories
|
||||
edit_book_param('tags', vals)
|
||||
if series:
|
||||
vals['value'] = series
|
||||
edit_book_param('series', vals)
|
||||
if languages:
|
||||
vals['value'] = languages
|
||||
edit_book_param('languages', vals)
|
||||
if publishers:
|
||||
vals['value'] = publishers
|
||||
edit_book_param('publishers', vals)
|
||||
if comments:
|
||||
vals['value'] = comments
|
||||
edit_book_param('comments', vals)
|
||||
return json.dumps({'success': True})
|
||||
|
||||
# Separated from /editbooks so that /editselectedbooks can also use this
|
||||
#
|
||||
@@ -284,94 +282,96 @@ def edit_selected_books():
|
||||
@login_required_if_no_ano
|
||||
@edit_required
|
||||
def edit_book_param(param, vals):
|
||||
book = calibre_db.get_book(vals['pk'])
|
||||
calibre_db.create_functions(config)
|
||||
sort_param = ""
|
||||
ret = ""
|
||||
try:
|
||||
if param == 'series_index':
|
||||
edit_book_series_index(vals['value'], book)
|
||||
ret = make_response(jsonify(success=True, newValue=book.series_index))
|
||||
elif param == 'tags':
|
||||
edit_book_tags(vals['value'], book)
|
||||
ret = make_response(jsonify(success=True, newValue=', '.join([tag.name for tag in book.tags])))
|
||||
elif param == 'series':
|
||||
edit_book_series(vals['value'], book)
|
||||
ret = make_response(jsonify(success=True, newValue=', '.join([serie.name for serie in book.series])))
|
||||
elif param == 'publishers':
|
||||
edit_book_publisher(vals['value'], book)
|
||||
ret = make_response(jsonify(success=True,
|
||||
newValue=', '.join([publisher.name for publisher in book.publishers])))
|
||||
elif param == 'languages':
|
||||
invalid = list()
|
||||
edit_book_languages(vals['value'], book, invalid=invalid)
|
||||
if invalid:
|
||||
ret = make_response(jsonify(success=False,
|
||||
msg='Invalid languages in request: {}'.format(','.join(invalid))))
|
||||
else:
|
||||
lang_names = list()
|
||||
for lang in book.languages:
|
||||
lang_names.append(isoLanguages.get_language_name(get_locale(), lang.lang_code))
|
||||
ret = make_response(jsonify(success=True, newValue=', '.join(lang_names)))
|
||||
elif param == 'author_sort':
|
||||
book.author_sort = vals['value']
|
||||
ret = make_response(jsonify(success=True, newValue=book.author_sort))
|
||||
elif param == 'title':
|
||||
sort_param = book.sort
|
||||
if handle_title_on_edit(book, vals.get('value', "")):
|
||||
rename_error = helper.update_dir_structure(book.id, config.get_book_path())
|
||||
if not rename_error:
|
||||
ret = make_response(jsonify(success=True, newValue=book.title))
|
||||
elements = vals.get('pk',[])
|
||||
ret = {}
|
||||
for elem in elements:
|
||||
book = calibre_db.get_book(elem)
|
||||
calibre_db.create_functions(config)
|
||||
sort_param = ""
|
||||
try:
|
||||
if param == 'series_index':
|
||||
edit_book_series_index(vals['value'], book)
|
||||
ret = jsonify(success=True, newValue=book.series_index)
|
||||
elif param == 'tags':
|
||||
edit_book_tags(vals['value'], book)
|
||||
ret = jsonify(success=True, newValue=', '.join([tag.name for tag in book.tags]))
|
||||
elif param == 'series':
|
||||
edit_book_series(vals['value'], book)
|
||||
ret = jsonify(success=True, newValue=', '.join([serie.name for serie in book.series]))
|
||||
elif param == 'publishers':
|
||||
edit_book_publisher(vals['value'], book)
|
||||
ret = jsonify(success=True,
|
||||
newValue=', '.join([publisher.name for publisher in book.publishers]))
|
||||
elif param == 'languages':
|
||||
invalid = list()
|
||||
edit_book_languages(vals['value'], book, invalid=invalid)
|
||||
if invalid:
|
||||
ret = jsonify(success=False, msg='Invalid languages in request: {}'.format(','.join(invalid)))
|
||||
else:
|
||||
ret = make_response(jsonify(success=False, msg=rename_error))
|
||||
elif param == 'sort':
|
||||
book.sort = vals['value']
|
||||
ret = make_response(jsonify(success=True,newValue=book.sort))
|
||||
elif param == 'comments':
|
||||
edit_book_comments(vals['value'], book)
|
||||
ret = make_response(jsonify(success=True, newValue=book.comments[0].text))
|
||||
elif param == 'authors':
|
||||
input_authors, __ = handle_author_on_edit(book, vals['value'], vals.get('checkA', None) == "true")
|
||||
rename_error = helper.update_dir_structure(book.id, config.get_book_path(), input_authors[0])
|
||||
if not rename_error:
|
||||
ret = make_response(jsonify(
|
||||
success=True,
|
||||
newValue=' & '.join([author.replace('|', ',') for author in input_authors])))
|
||||
lang_names = list()
|
||||
for lang in book.languages:
|
||||
lang_names.append(isoLanguages.get_language_name(get_locale(), lang.lang_code))
|
||||
ret = jsonify(success=True, newValue=', '.join(lang_names))
|
||||
elif param == 'author_sort':
|
||||
book.author_sort = vals['value']
|
||||
ret = jsonify(success=True, newValue=book.author_sort)
|
||||
elif param == 'title':
|
||||
sort_param = book.sort
|
||||
if handle_title_on_edit(book, vals.get('value', "")):
|
||||
rename_error = helper.update_dir_structure(book.id, config.get_book_path())
|
||||
if not rename_error:
|
||||
ret = jsonify(success=True, newValue=book.title)
|
||||
else:
|
||||
ret = jsonify(success=False, msg=rename_error)
|
||||
elif param == 'sort':
|
||||
book.sort = vals['value']
|
||||
ret = jsonify(success=True,newValue=book.sort)
|
||||
elif param == 'comments':
|
||||
edit_book_comments(vals['value'], book)
|
||||
ret = jsonify(success=True, newValue=book.comments[0].text)
|
||||
elif param == 'authors':
|
||||
input_authors, __ = handle_author_on_edit(book, vals['value'], vals.get('checkA', None) == True)
|
||||
rename_error = helper.update_dir_structure(book.id, config.get_book_path(), input_authors[0])
|
||||
if not rename_error:
|
||||
ret = jsonify(
|
||||
success=True,
|
||||
newValue=' & '.join([author.replace('|', ',') for author in input_authors]))
|
||||
else:
|
||||
ret = jsonify(success=False, msg=rename_error)
|
||||
elif param == 'is_archived':
|
||||
is_archived = change_archived_books(book.id, vals['value'] == "True",
|
||||
message="Book {} archive bit set to: {}".format(book.id,
|
||||
vals['value']))
|
||||
if is_archived:
|
||||
kobo_sync_status.remove_synced_book(book.id)
|
||||
continue
|
||||
elif param == 'read_status':
|
||||
error = helper.edit_book_read_status(book.id, vals['value'] == "True")
|
||||
if error:
|
||||
return error, 400
|
||||
continue
|
||||
elif param.startswith("custom_column_"):
|
||||
new_val = dict()
|
||||
new_val[param] = vals['value']
|
||||
edit_single_cc_data(book.id, book, param[14:], new_val)
|
||||
# ToDo: Very hacky find better solution
|
||||
if vals['value'] in ["True", "False"]:
|
||||
ret = {}
|
||||
else:
|
||||
ret = jsonify(success=True, newValue=vals['value'])
|
||||
else:
|
||||
ret = make_response(jsonify(success=False, msg=rename_error))
|
||||
elif param == 'is_archived':
|
||||
is_archived = change_archived_books(book.id, vals['value'] == "True",
|
||||
message="Book {} archive bit set to: {}".format(book.id, vals['value']))
|
||||
if is_archived:
|
||||
kobo_sync_status.remove_synced_book(book.id)
|
||||
return ""
|
||||
elif param == 'read_status':
|
||||
ret = helper.edit_book_read_status(book.id, vals['value'] == "True")
|
||||
if ret:
|
||||
return ret, 400
|
||||
elif param.startswith("custom_column_"):
|
||||
new_val = dict()
|
||||
new_val[param] = vals['value']
|
||||
edit_single_cc_data(book.id, book, param[14:], new_val)
|
||||
# ToDo: Very hacky find better solution
|
||||
if vals['value'] in ["True", "False"]:
|
||||
ret = ""
|
||||
else:
|
||||
ret = make_response(jsonify(success=True, newValue=vals['value']))
|
||||
else:
|
||||
return _("Parameter not found"), 400
|
||||
book.last_modified = datetime.now(timezone.utc)
|
||||
return _("Parameter not found"), 400
|
||||
book.last_modified = datetime.now(timezone.utc)
|
||||
|
||||
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_param
|
||||
calibre_db.session.commit()
|
||||
except (OperationalError, IntegrityError, StaleDataError) as e:
|
||||
calibre_db.session.rollback()
|
||||
log.error_or_exception("Database error: {}".format(e))
|
||||
ret = make_response(jsonify(success=False,
|
||||
msg='Database error: {}'.format(e.orig if hasattr(e, "orig") else e)))
|
||||
# revert change for sort if automatic fields link is deactivated
|
||||
if param == 'title' and vals.get('checkT') == False:
|
||||
book.sort = sort_param
|
||||
calibre_db.session.commit()
|
||||
except (OperationalError, IntegrityError, StaleDataError, AttributeError) as e:
|
||||
calibre_db.session.rollback()
|
||||
log.error_or_exception("Database error: {}".format(e))
|
||||
ret = jsonify(success=False, msg='Database error: {}'.format(e.orig if hasattr(e, "orig") else e))
|
||||
return ret
|
||||
|
||||
|
||||
@@ -433,16 +433,6 @@ def archive_selected_books():
|
||||
return json.dumps({'success': True})
|
||||
return ""
|
||||
|
||||
@editbook.route("/ajax/deleteselectedbooks", methods=['POST'])
|
||||
@user_login_required
|
||||
@edit_required
|
||||
def delete_selected_books():
|
||||
vals = request.get_json().get('selections')
|
||||
if vals:
|
||||
for book_id in vals:
|
||||
delete_book_from_table(book_id, "", True)
|
||||
return json.dumps({'success': True})
|
||||
return ""
|
||||
|
||||
@editbook.route("/ajax/readselectedbooks", methods=['POST'])
|
||||
@user_login_required
|
||||
@@ -498,7 +488,7 @@ def merge_list_book():
|
||||
element.format,
|
||||
element.uncompressed_size,
|
||||
to_name))
|
||||
delete_book_from_table(from_book.id, "", True)
|
||||
check_delete_book([from_book.id], "", True)
|
||||
return make_response(jsonify(success=True))
|
||||
return ""
|
||||
|
||||
@@ -968,86 +958,109 @@ def delete_whole_book(book_id, book):
|
||||
calibre_db.session.query(db.Books).filter(db.Books.id == book_id).delete()
|
||||
|
||||
|
||||
def render_delete_book_result(book_format, json_response, warning, book_id, location=""):
|
||||
def render_delete_book_result(book_format, book_id, location=""):
|
||||
if book_format:
|
||||
if json_response:
|
||||
return jsonify([warning, {"location": url_for("edit-book.show_edit_book", book_id=book_id),
|
||||
"type": "success",
|
||||
"format": book_format,
|
||||
"message": _('Book Format Successfully Deleted')}])
|
||||
else:
|
||||
flash(_('Book Format Successfully Deleted'), category="success")
|
||||
return redirect(url_for('edit-book.show_edit_book', book_id=book_id))
|
||||
flash(_('Book Format Successfully Deleted'), category="success")
|
||||
return redirect(url_for('edit-book.show_edit_book', book_id=book_id))
|
||||
else:
|
||||
if json_response:
|
||||
return jsonify([warning, {"location": get_redirect_location(location, "web.index"),
|
||||
"type": "success",
|
||||
"format": book_format,
|
||||
"message": _('Book Successfully Deleted')}])
|
||||
else:
|
||||
flash(_('Book Successfully Deleted'), category="success")
|
||||
return redirect(get_redirect_location(location, "web.index"))
|
||||
flash(_('Book Successfully Deleted'), category="success")
|
||||
return redirect(get_redirect_location(location, "web.index"))
|
||||
|
||||
|
||||
def delete_book_from_table(book_id, book_format, json_response, location=""):
|
||||
warning = {}
|
||||
def check_delete_book(book_id, book_format, json_response, location=""):
|
||||
if current_user.role_delete_books():
|
||||
book = calibre_db.get_book(book_id)
|
||||
if book:
|
||||
try:
|
||||
result, error = helper.delete_book(book, config.get_book_path(), book_format=book_format.upper())
|
||||
if not result:
|
||||
if json_response:
|
||||
return jsonify([{"location": url_for("edit-book.show_edit_book", book_id=book_id),
|
||||
"type": "danger",
|
||||
"format": "",
|
||||
"message": error}])
|
||||
else:
|
||||
flash(error, category="error")
|
||||
return redirect(url_for('edit-book.show_edit_book', book_id=book_id))
|
||||
if error:
|
||||
if json_response:
|
||||
warning = {"location": url_for("edit-book.show_edit_book", book_id=book_id),
|
||||
"type": "warning",
|
||||
"format": "",
|
||||
"message": error}
|
||||
else:
|
||||
flash(error, category="warning")
|
||||
if not book_format:
|
||||
delete_whole_book(book_id, book)
|
||||
else:
|
||||
calibre_db.session.query(db.Data).filter(db.Data.book == book.id).\
|
||||
filter(db.Data.format == book_format).delete()
|
||||
if book_format.upper() in ['KEPUB', 'EPUB', 'EPUB3']:
|
||||
kobo_sync_status.remove_synced_book(book.id, True)
|
||||
calibre_db.session.commit()
|
||||
except Exception as ex:
|
||||
log.error_or_exception(ex)
|
||||
calibre_db.session.rollback()
|
||||
if json_response:
|
||||
return jsonify([{"location": url_for("edit-book.show_edit_book", book_id=book_id),
|
||||
"type": "danger",
|
||||
"format": "",
|
||||
"message": ex}])
|
||||
else:
|
||||
flash(str(ex), category="error")
|
||||
return redirect(url_for('edit-book.show_edit_book', book_id=book_id))
|
||||
|
||||
if json_response:
|
||||
# if json response is set, it's possible to delete more than one book, but never a format is deleted
|
||||
res = list()
|
||||
for b in book_id:
|
||||
ret = delete_book_from_table(b)
|
||||
if ret:
|
||||
res.extend([ret])
|
||||
if len(res) == 0:
|
||||
return [{"location": get_redirect_location(location, "web.index"),
|
||||
"type": "success",
|
||||
"format": "",
|
||||
"message": _('Book Successfully Deleted')}]
|
||||
return jsonify(res)
|
||||
else:
|
||||
# book not found
|
||||
log.error('Book with id "%s" could not be deleted: not found', book_id)
|
||||
return render_delete_book_result(book_format, json_response, warning, book_id, location)
|
||||
return delete_book_from_UI(book_id, book_format, location)
|
||||
message = _("You are missing permissions to delete books")
|
||||
if json_response:
|
||||
return jsonify({"location": url_for("edit-book.show_edit_book", book_id=book_id),
|
||||
"type": "danger",
|
||||
"format": "",
|
||||
"message": message})
|
||||
try:
|
||||
return jsonify({"location": url_for("edit-book.show_edit_book", book_id=int(book_id)),
|
||||
"type": "danger",
|
||||
"format": "",
|
||||
"message": message})
|
||||
except TypeError as e:
|
||||
return jsonify({"location": url_for("web.index"), "type": "danger", "format": "",
|
||||
"message": str(e)})
|
||||
else:
|
||||
flash(message, category="error")
|
||||
return redirect(url_for('edit-book.show_edit_book', book_id=book_id))
|
||||
|
||||
|
||||
def delete_book_from_UI(book_id, book_format, location=""):
|
||||
book = calibre_db.get_book(book_id)
|
||||
if book:
|
||||
try:
|
||||
result, error = helper.delete_book(book, config.get_book_path(), book_format=book_format.upper())
|
||||
if not result:
|
||||
flash(error, category="error")
|
||||
return redirect(url_for('edit-book.show_edit_book', book_id=book_id))
|
||||
if error:
|
||||
flash(error, category="warning")
|
||||
if not book_format:
|
||||
delete_whole_book(book_id, book)
|
||||
else:
|
||||
calibre_db.session.query(db.Data).filter(db.Data.book == book.id). \
|
||||
filter(db.Data.format == book_format).delete()
|
||||
if book_format.upper() in ['KEPUB', 'EPUB', 'EPUB3']:
|
||||
kobo_sync_status.remove_synced_book(book.id, True)
|
||||
calibre_db.session.commit()
|
||||
except Exception as ex:
|
||||
log.error_or_exception(ex)
|
||||
calibre_db.session.rollback()
|
||||
flash(str(ex), category="error")
|
||||
return redirect(url_for('edit-book.show_edit_book', book_id=book_id))
|
||||
else:
|
||||
# book not found
|
||||
log.error('Book with id "%s" could not be deleted: not found', book_id)
|
||||
return render_delete_book_result(book_format, book_id, location)
|
||||
|
||||
|
||||
def delete_book_from_table(book_id):
|
||||
book = calibre_db.get_book(book_id)
|
||||
if book:
|
||||
try:
|
||||
result, error = helper.delete_book(book, config.get_book_path(), book_format="")
|
||||
if not result:
|
||||
return {"location": url_for("edit-book.show_edit_book", book_id=book_id),
|
||||
"type": "danger",
|
||||
"format": "",
|
||||
"message": error}
|
||||
delete_whole_book(book_id, book)
|
||||
calibre_db.session.commit()
|
||||
if error:
|
||||
return {"location": url_for("edit-book.show_edit_book", book_id=book_id),
|
||||
"type": "warning",
|
||||
"format": "",
|
||||
"message": error}
|
||||
except Exception as ex:
|
||||
log.error_or_exception(ex)
|
||||
calibre_db.session.rollback()
|
||||
return {"location": url_for("edit-book.show_edit_book", book_id=book_id),
|
||||
"type": "danger",
|
||||
"format": "",
|
||||
"message": ex}
|
||||
else:
|
||||
# book not found
|
||||
log.error('Book with id "%s" could not be deleted: not found', book_id)
|
||||
return {"location": url_for("edit-book.show_edit_book", book_id=book_id),
|
||||
"type": "danger",
|
||||
"format": "",
|
||||
"message": _('Book with id "{}" could not be deleted: not found'.format(book_id))}
|
||||
|
||||
|
||||
def render_edit_book(book_id):
|
||||
cc = calibre_db.session.query(db.CustomColumns).filter(db.CustomColumns.datatype.notin_(db.cc_exceptions)).all()
|
||||
book = calibre_db.get_filtered_book(book_id, allow_show_archived=True)
|
||||
|
||||
Reference in New Issue
Block a user