mirror of
https://github.com/janeczku/calibre-web
synced 2025-10-23 03:27:37 +00:00
Update mass edit
Refactored delete User function Updated testresults
This commit is contained in:
23
cps/admin.py
23
cps/admin.py
@@ -40,7 +40,7 @@ from flask_babel import gettext as _
|
||||
from flask_babel import get_locale, format_time, format_datetime, format_timedelta
|
||||
from sqlalchemy import and_
|
||||
from sqlalchemy.orm.attributes import flag_modified
|
||||
from sqlalchemy.exc import IntegrityError, OperationalError, InvalidRequestError
|
||||
from sqlalchemy.exc import IntegrityError, OperationalError, InvalidRequestError, ArgumentError
|
||||
from sqlalchemy.sql.expression import func, or_, text
|
||||
|
||||
from . import constants, logger, helper, services, cli_param
|
||||
@@ -386,13 +386,12 @@ def list_users():
|
||||
@user_login_required
|
||||
@admin_required
|
||||
def delete_user():
|
||||
user_ids = request.form.to_dict(flat=False)
|
||||
users = None
|
||||
user_ids = request.get_json().get("userid")
|
||||
message = ""
|
||||
if "userid[]" in user_ids:
|
||||
users = ub.session.query(ub.User).filter(ub.User.id.in_(user_ids['userid[]'])).all()
|
||||
elif "userid" in user_ids:
|
||||
users = ub.session.query(ub.User).filter(ub.User.id == user_ids['userid'][0]).all()
|
||||
try:
|
||||
users = ub.session.query(ub.User).filter(ub.User.id.in_(user_ids)).all()
|
||||
except (ArgumentError):
|
||||
users = None
|
||||
count = 0
|
||||
errors = list()
|
||||
success = list()
|
||||
@@ -408,10 +407,10 @@ def delete_user():
|
||||
errors.append({'type': "danger", 'message': str(ex)})
|
||||
|
||||
if count == 1:
|
||||
log.info("User {} deleted".format(user_ids))
|
||||
log.info("User {} deleted".format(user_ids[0]))
|
||||
success = [{'type': "success", 'message': message}]
|
||||
elif count > 1:
|
||||
log.info("Users {} deleted".format(user_ids))
|
||||
log.info("Users {} deleted".format(", ".join([str(user_id) for user_id in user_ids])))
|
||||
success = [{'type': "success", 'message': _("{} users deleted successfully").format(count)}]
|
||||
success.extend(errors)
|
||||
return make_response(jsonify(success))
|
||||
@@ -618,6 +617,8 @@ def load_dialogtexts(element_id):
|
||||
texts["main"] = _('Do you really want to delete this domain?')
|
||||
elif element_id == "btndeluser":
|
||||
texts["main"] = _('Do you really want to delete this user?')
|
||||
elif element_id == "btndelbook":
|
||||
texts["main"] = _('Do you really want to delete this book?')
|
||||
elif element_id == "delete_shelf":
|
||||
texts["main"] = _('Are you sure you want to delete this shelf?')
|
||||
elif element_id == "select_locale":
|
||||
@@ -626,6 +627,10 @@ def load_dialogtexts(element_id):
|
||||
texts["main"] = _('Are you sure you want to change visible book languages for selected user(s)?')
|
||||
elif element_id == "role":
|
||||
texts["main"] = _('Are you sure you want to change the selected role for the selected user(s)?')
|
||||
elif element_id == "archive_books":
|
||||
texts["main"] = _('Are you sure you want to change the archive status for the selected book(s)?')
|
||||
elif element_id == "read_books":
|
||||
texts["main"] = _('Are you sure you want to change the read status for the selected book(s)?')
|
||||
elif element_id == "restrictions":
|
||||
texts["main"] = _('Are you sure you want to change the selected restrictions for the selected user(s)?')
|
||||
elif element_id == "sidebar_view":
|
||||
|
197
cps/editbooks.py
197
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,10 +234,8 @@ 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,
|
||||
"pk": selections,
|
||||
"value": None,
|
||||
"checkA": checkA,
|
||||
}
|
||||
@@ -268,7 +267,6 @@ def edit_selected_books():
|
||||
vals['value'] = comments
|
||||
edit_book_param('comments', vals)
|
||||
return json.dumps({'success': True})
|
||||
return ""
|
||||
|
||||
# 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'])
|
||||
elements = vals.get('pk',[])
|
||||
ret = {}
|
||||
for elem in elements:
|
||||
book = calibre_db.get_book(elem)
|
||||
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))
|
||||
ret = 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])))
|
||||
ret = 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])))
|
||||
ret = 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])))
|
||||
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 = make_response(jsonify(success=False,
|
||||
msg='Invalid languages in request: {}'.format(','.join(invalid))))
|
||||
ret = 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)))
|
||||
ret = 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))
|
||||
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 = make_response(jsonify(success=True, newValue=book.title))
|
||||
ret = jsonify(success=True, newValue=book.title)
|
||||
else:
|
||||
ret = make_response(jsonify(success=False, msg=rename_error))
|
||||
ret = jsonify(success=False, msg=rename_error)
|
||||
elif param == 'sort':
|
||||
book.sort = vals['value']
|
||||
ret = make_response(jsonify(success=True,newValue=book.sort))
|
||||
ret = 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))
|
||||
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")
|
||||
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(
|
||||
ret = jsonify(
|
||||
success=True,
|
||||
newValue=' & '.join([author.replace('|', ',') for author in input_authors])))
|
||||
newValue=' & '.join([author.replace('|', ',') for author in input_authors]))
|
||||
else:
|
||||
ret = make_response(jsonify(success=False, msg=rename_error))
|
||||
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']))
|
||||
message="Book {} archive bit set to: {}".format(book.id,
|
||||
vals['value']))
|
||||
if is_archived:
|
||||
kobo_sync_status.remove_synced_book(book.id)
|
||||
return ""
|
||||
continue
|
||||
elif param == 'read_status':
|
||||
ret = helper.edit_book_read_status(book.id, vals['value'] == "True")
|
||||
if ret:
|
||||
return ret, 400
|
||||
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 = ""
|
||||
ret = {}
|
||||
else:
|
||||
ret = make_response(jsonify(success=True, newValue=vals['value']))
|
||||
ret = jsonify(success=True, newValue=vals['value'])
|
||||
else:
|
||||
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":
|
||||
if param == 'title' and vals.get('checkT') == False:
|
||||
book.sort = sort_param
|
||||
calibre_db.session.commit()
|
||||
except (OperationalError, IntegrityError, StaleDataError) as e:
|
||||
except (OperationalError, IntegrityError, StaleDataError, AttributeError) 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)))
|
||||
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,50 +958,56 @@ 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))
|
||||
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"))
|
||||
|
||||
|
||||
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():
|
||||
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:
|
||||
return delete_book_from_UI(book_id, book_format, location)
|
||||
message = _("You are missing permissions to delete books")
|
||||
if json_response:
|
||||
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:
|
||||
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)
|
||||
@@ -1024,28 +1020,45 @@ def delete_book_from_table(book_id, book_format, json_response, location=""):
|
||||
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))
|
||||
|
||||
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)
|
||||
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),
|
||||
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": message})
|
||||
"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:
|
||||
flash(message, category="error")
|
||||
return redirect(url_for('edit-book.show_edit_book', book_id=book_id))
|
||||
# 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):
|
||||
|
@@ -307,7 +307,9 @@ def edit_book_read_status(book_id, read_status=None):
|
||||
if not config.config_read_column:
|
||||
book = ub.session.query(ub.ReadBook).filter(and_(ub.ReadBook.user_id == int(current_user.id),
|
||||
ub.ReadBook.book_id == book_id)).first()
|
||||
if book:
|
||||
if not book:
|
||||
read_book = ub.ReadBook(user_id=current_user.id, book_id=book_id)
|
||||
book = read_book
|
||||
if read_status is None:
|
||||
if book.read_status == ub.ReadBook.STATUS_FINISHED:
|
||||
book.read_status = ub.ReadBook.STATUS_UNREAD
|
||||
@@ -315,10 +317,6 @@ def edit_book_read_status(book_id, read_status=None):
|
||||
book.read_status = ub.ReadBook.STATUS_FINISHED
|
||||
else:
|
||||
book.read_status = ub.ReadBook.STATUS_FINISHED if read_status == True else ub.ReadBook.STATUS_UNREAD
|
||||
else:
|
||||
read_book = ub.ReadBook(user_id=current_user.id, book_id=book_id)
|
||||
read_book.read_status = ub.ReadBook.STATUS_FINISHED
|
||||
book = read_book
|
||||
if not book.kobo_reading_state:
|
||||
kobo_reading_state = ub.KoboReadingState(user_id=current_user.id, book_id=book_id)
|
||||
kobo_reading_state.current_bookmark = ub.KoboBookmark()
|
||||
|
@@ -56,7 +56,7 @@ def remove_synced_book(book_id, all=False, session=None):
|
||||
def change_archived_books(book_id, state=None, message=None):
|
||||
archived_book = ub.session.query(ub.ArchivedBook).filter(and_(ub.ArchivedBook.user_id == int(current_user.id),
|
||||
ub.ArchivedBook.book_id == book_id)).first()
|
||||
if not archived_book and (state == True or state == None):
|
||||
if not archived_book: # and (state == True or state == None):
|
||||
archived_book = ub.ArchivedBook(user_id=current_user.id, book_id=book_id)
|
||||
|
||||
archived_book.is_archived = state if state != None else not archived_book.is_archived
|
||||
|
@@ -229,10 +229,12 @@ $("#delete_confirm").click(function(event) {
|
||||
postButton(event, getPath() + "/delete/" + deleteId + "/" + bookFormat);
|
||||
} else {
|
||||
if (ajaxResponse) {
|
||||
path = getPath() + "/ajax/delete/" + deleteId;
|
||||
$.ajax({
|
||||
url: getPath() + "/ajax/deletebook",
|
||||
method: "post",
|
||||
url: path,
|
||||
contentType: "application/json; charset=utf-8",
|
||||
dataType: "json",
|
||||
data: JSON.stringify({"bookid": [deleteId]}),
|
||||
timeout: 900,
|
||||
success:function(data) {
|
||||
data.forEach(function(item) {
|
||||
|
@@ -119,18 +119,17 @@ $(function() {
|
||||
$("#edit_selected_books").attr("aria-disabled", true);
|
||||
}
|
||||
if (selections.length < 1) {
|
||||
$("#delete_selection").addClass("disabled");
|
||||
$("#delete_selection").attr("aria-disabled", true);
|
||||
// $("#book_delete_selection").addClass("disabled");
|
||||
// $("#book_delete_selection").attr("aria-disabled", true);
|
||||
$("#table_xchange").addClass("disabled");
|
||||
$("#table_xchange").attr("aria-disabled", true);
|
||||
} else {
|
||||
$("#delete_selection").removeClass("disabled");
|
||||
$("#delete_selection").attr("aria-disabled", false);
|
||||
// $("#book_delete_selection").removeClass("disabled");
|
||||
// $("#book_delete_selection").attr("aria-disabled", false);
|
||||
$("#table_xchange").removeClass("disabled");
|
||||
$("#table_xchange").attr("aria-disabled", false);
|
||||
|
||||
}
|
||||
|
||||
handle_header_buttons();
|
||||
});
|
||||
|
||||
// Small block to initialize the state of the author/title sort inputs in metadata form
|
||||
@@ -153,7 +152,7 @@ $(function() {
|
||||
})
|
||||
/////
|
||||
|
||||
$("#delete_selection").click(function() {
|
||||
$("#book_delete_selection").click(function () {
|
||||
$("#books-table").bootstrapTable("uncheckAll");
|
||||
});
|
||||
|
||||
@@ -162,7 +161,7 @@ $(function() {
|
||||
method:"post",
|
||||
contentType: "application/json; charset=utf-8",
|
||||
dataType: "json",
|
||||
url: window.location.pathname + "/../ajax/mergebooks",
|
||||
url: getPath() + "/ajax/mergebooks",
|
||||
data: JSON.stringify({"Merge_books":selections}),
|
||||
success: function success() {
|
||||
$("#books-table").bootstrapTable("refresh");
|
||||
@@ -181,7 +180,7 @@ $(function() {
|
||||
method:"post",
|
||||
contentType: "application/json; charset=utf-8",
|
||||
dataType: "json",
|
||||
url: window.location.pathname + "/../ajax/simulatemerge",
|
||||
url: getPath() + "/ajax/simulatemerge",
|
||||
data: JSON.stringify({"Merge_books":selections}),
|
||||
success: function success(booTitles) {
|
||||
$('#merge_from').empty();
|
||||
@@ -207,7 +206,7 @@ $(function() {
|
||||
method:"post",
|
||||
contentType: "application/json; charset=utf-8",
|
||||
dataType: "json",
|
||||
url: window.location.pathname + "/../ajax/editselectedbooks",
|
||||
url: getPath() + "/ajax/editselectedbooks",
|
||||
data: JSON.stringify({
|
||||
"selections": selections,
|
||||
"title": $("#title_input").val(),
|
||||
@@ -250,7 +249,7 @@ $(function() {
|
||||
method:"post",
|
||||
contentType: "application/json; charset=utf-8",
|
||||
dataType: "json",
|
||||
url: window.location.pathname + "/../ajax/displayselectedbooks",
|
||||
url: getPath() + "/ajax/displayselectedbooks",
|
||||
data: JSON.stringify({"selections":selections}),
|
||||
success: function success(booTitles) {
|
||||
$('#display-archive-selected-books').empty();
|
||||
@@ -262,12 +261,12 @@ $(function() {
|
||||
});
|
||||
});
|
||||
|
||||
$(document).on('click', '#archive_selected_confirm', function(event) {
|
||||
/*$(document).on('click', '#archive_selected_confirm', function(event) {
|
||||
$.ajax({
|
||||
method:"post",
|
||||
contentType: "application/json; charset=utf-8",
|
||||
dataType: "json",
|
||||
url: window.location.pathname + "/../ajax/archiveselectedbooks",
|
||||
url: getPath() + "/ajax/archiveselectedbooks",
|
||||
data: JSON.stringify({"selections":selections, "archive": true}),
|
||||
success: function success(booTitles) {
|
||||
$("#books-table").bootstrapTable("refresh");
|
||||
@@ -286,7 +285,7 @@ $(function() {
|
||||
method:"post",
|
||||
contentType: "application/json; charset=utf-8",
|
||||
dataType: "json",
|
||||
url: window.location.pathname + "/../ajax/displayselectedbooks",
|
||||
url: getPath() + "/ajax/displayselectedbooks",
|
||||
data: JSON.stringify({"selections":selections}),
|
||||
success: function success(booTitles) {
|
||||
$('#display-unarchive-selected-books').empty();
|
||||
@@ -303,7 +302,7 @@ $(function() {
|
||||
method:"post",
|
||||
contentType: "application/json; charset=utf-8",
|
||||
dataType: "json",
|
||||
url: window.location.pathname + "/../ajax/archiveselectedbooks",
|
||||
url: getPath() + "/ajax/archiveselectedbooks",
|
||||
data: JSON.stringify({"selections":selections, "archive": false}),
|
||||
success: function success(booTitles) {
|
||||
$("#books-table").bootstrapTable("refresh");
|
||||
@@ -322,7 +321,7 @@ $(function() {
|
||||
method:"post",
|
||||
contentType: "application/json; charset=utf-8",
|
||||
dataType: "json",
|
||||
url: window.location.pathname + "/../ajax/displayselectedbooks",
|
||||
url: getPath() + "/ajax/displayselectedbooks",
|
||||
data: JSON.stringify({"selections":selections}),
|
||||
success: function success(booTitles) {
|
||||
$('#display-delete-selected-books').empty();
|
||||
@@ -339,7 +338,7 @@ $(function() {
|
||||
method:"post",
|
||||
contentType: "application/json; charset=utf-8",
|
||||
dataType: "json",
|
||||
url: window.location.pathname + "/../ajax/deleteselectedbooks",
|
||||
url: getPath() + "/ajax/deleteselectedbooks",
|
||||
data: JSON.stringify({"selections":selections}),
|
||||
success: function success(booTitles) {
|
||||
$("#books-table").bootstrapTable("refresh");
|
||||
@@ -358,7 +357,7 @@ $(function() {
|
||||
method:"post",
|
||||
contentType: "application/json; charset=utf-8",
|
||||
dataType: "json",
|
||||
url: window.location.pathname + "/../ajax/displayselectedbooks",
|
||||
url: getPath() + "/ajax/displayselectedbooks",
|
||||
data: JSON.stringify({"selections":selections}),
|
||||
success: function success(booTitles) {
|
||||
$('#display-read-selected-books').empty();
|
||||
@@ -375,7 +374,7 @@ $(function() {
|
||||
method:"post",
|
||||
contentType: "application/json; charset=utf-8",
|
||||
dataType: "json",
|
||||
url: window.location.pathname + "/../ajax/readselectedbooks",
|
||||
url: getPath() + "/ajax/readselectedbooks",
|
||||
data: JSON.stringify({"selections":selections, "markAsRead": true}),
|
||||
success: function success(booTitles) {
|
||||
$("#books-table").bootstrapTable("refresh");
|
||||
@@ -394,7 +393,7 @@ $(function() {
|
||||
method:"post",
|
||||
contentType: "application/json; charset=utf-8",
|
||||
dataType: "json",
|
||||
url: window.location.pathname + "/../ajax/displayselectedbooks",
|
||||
url: getPath() + "/ajax/displayselectedbooks",
|
||||
data: JSON.stringify({"selections":selections}),
|
||||
success: function success(booTitles) {
|
||||
$('#display-unread-selected-books').empty();
|
||||
@@ -411,21 +410,21 @@ $(function() {
|
||||
method:"post",
|
||||
contentType: "application/json; charset=utf-8",
|
||||
dataType: "json",
|
||||
url: window.location.pathname + "/../ajax/readselectedbooks",
|
||||
url: getPath() + "/ajax/readselectedbooks",
|
||||
data: JSON.stringify({"selections":selections, "markAsRead": false}),
|
||||
success: function success(booTitles) {
|
||||
$("#books-table").bootstrapTable("refresh");
|
||||
$("#books-table").bootstrapTable("uncheckAll");
|
||||
}
|
||||
});
|
||||
});
|
||||
});*/
|
||||
|
||||
$("#table_xchange").click(function() {
|
||||
$.ajax({
|
||||
method:"post",
|
||||
contentType: "application/json; charset=utf-8",
|
||||
dataType: "json",
|
||||
url: window.location.pathname + "/../ajax/xchange",
|
||||
url: getPath() + "/ajax/xchange",
|
||||
data: JSON.stringify({"xchange":selections}),
|
||||
success: function success() {
|
||||
$("#books-table").bootstrapTable("refresh");
|
||||
@@ -442,6 +441,10 @@ $(function() {
|
||||
editable: {
|
||||
mode: "inline",
|
||||
emptytext: "<span class='glyphicon glyphicon-plus'></span>",
|
||||
ajaxOptions: {
|
||||
contentType: "application/json; charset=utf-8",
|
||||
dataType: "json",
|
||||
},
|
||||
success: function (response, __) {
|
||||
if (!response.success) return response.msg;
|
||||
return {newValue: response.newValue};
|
||||
@@ -449,7 +452,8 @@ $(function() {
|
||||
params: function (params) {
|
||||
params.checkA = $('#autoupdate_authorsort').prop('checked');
|
||||
params.checkT = $('#autoupdate_titlesort').prop('checked');
|
||||
return params
|
||||
params.pk = [params.pk];
|
||||
return JSON.stringify(params);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -483,7 +487,7 @@ $(function() {
|
||||
searchAlign: "left",
|
||||
showSearchButton : true,
|
||||
searchOnEnterKey: true,
|
||||
checkboxHeader: false,
|
||||
checkboxHeader: true,
|
||||
maintainMetaData: true,
|
||||
responseHandler: responseHandler,
|
||||
columns: column,
|
||||
@@ -497,7 +501,7 @@ $(function() {
|
||||
$.ajax({
|
||||
method:"get",
|
||||
dataType: "json",
|
||||
url: window.location.pathname + "/../ajax/sort_value/" + field + "/" + row.id,
|
||||
url: getPath() + "/ajax/sort_value/" + field + "/" + row.id,
|
||||
success: function success(data) {
|
||||
var key = Object.keys(data)[0];
|
||||
$("#books-table").bootstrapTable("updateCellByUniqueId", {
|
||||
@@ -509,6 +513,66 @@ $(function() {
|
||||
});
|
||||
}
|
||||
},
|
||||
onPostBody () {
|
||||
// Remove all checkboxes from Headers for showing the texts in the column selector
|
||||
$('.columns [data-field]').each(function(){
|
||||
var elText = $(this).next().text();
|
||||
$(this).next().empty();
|
||||
var index = elText.lastIndexOf('\n', elText.length - 2);
|
||||
if ( index > -1) {
|
||||
elText = elText.substr(index);
|
||||
}
|
||||
$(this).next().text(elText);
|
||||
});
|
||||
},
|
||||
onPostHeader() {
|
||||
$(".form-check").each(function () {
|
||||
var item = $(this).parent();
|
||||
var parent = item.parent().parent();
|
||||
if (parent.prop('nodeName') === "TH") {
|
||||
item.prependTo(parent);
|
||||
}
|
||||
});
|
||||
|
||||
if ($(".button_head").length) {
|
||||
if (!$._data($(".button_head").get(0), "events")) {
|
||||
$(".button_head").on("click", function () {
|
||||
var result = $('#books-table').bootstrapTable('getSelections').map(a => a.id);
|
||||
confirmDialog(
|
||||
"btndelbook",
|
||||
"GeneralDeleteModal",
|
||||
0,
|
||||
function () {
|
||||
$.ajax({
|
||||
method: "post",
|
||||
url: getPath() + "/ajax/deletebook",
|
||||
contentType: "application/json; charset=utf-8",
|
||||
dataType: "json",
|
||||
data: JSON.stringify({"bookid": result}),
|
||||
success: function (data) {
|
||||
selections = selections.filter((el) => !result.includes(el));
|
||||
handleListServerResponse(data);
|
||||
},
|
||||
error: function (data) {
|
||||
handleListServerResponse([{type: "danger", message: data.responseText}])
|
||||
},
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
if ($(".check_head").length) {
|
||||
if (!$._data($(".check_head").get(0), "events")) {
|
||||
$(".check_head").on("change", function () {
|
||||
var val = $(this).data("set");
|
||||
var name = $(this).data("name");
|
||||
var data = $(this).data("val");
|
||||
bookCheckboxHeader(val, name, data);
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
onColumnSwitch: function (field, checked) {
|
||||
var visible = $("#books-table").bootstrapTable("getVisibleColumns");
|
||||
@@ -525,10 +589,16 @@ $(function() {
|
||||
method:"post",
|
||||
contentType: "application/json; charset=utf-8",
|
||||
dataType: "json",
|
||||
url: window.location.pathname + "/../ajax/table_settings",
|
||||
url: getPath() + "/ajax/table_settings",
|
||||
data: "{" + st + "}",
|
||||
});
|
||||
handle_header_buttons();
|
||||
},
|
||||
onLoadSuccess: function() {
|
||||
$("input:radio.check_head:checked").each(function () {
|
||||
$(this).prop('checked', false);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
$("#domain_allow_submit").click(function(event) {
|
||||
@@ -537,7 +607,7 @@ $(function() {
|
||||
$(this).closest("form").submit();
|
||||
$.ajax ({
|
||||
method:"get",
|
||||
url: window.location.pathname + "/../../ajax/domainlist/1",
|
||||
url: getPath() + "/ajax/domainlist/1",
|
||||
async: true,
|
||||
timeout: 900,
|
||||
success:function(data) {
|
||||
@@ -558,7 +628,7 @@ $(function() {
|
||||
$(this).closest("form").submit();
|
||||
$.ajax ({
|
||||
method:"get",
|
||||
url: window.location.pathname + "/../../ajax/domainlist/0",
|
||||
url: getPath() + "/ajax/domainlist/0",
|
||||
async: true,
|
||||
timeout: 900,
|
||||
success:function(data) {
|
||||
@@ -576,12 +646,12 @@ $(function() {
|
||||
function domainHandle(domainId) {
|
||||
$.ajax({
|
||||
method:"post",
|
||||
url: window.location.pathname + "/../../ajax/deletedomain",
|
||||
url: getPath() + "/ajax/deletedomain",
|
||||
data: {"domainid":domainId}
|
||||
});
|
||||
$.ajax({
|
||||
method:"get",
|
||||
url: window.location.pathname + "/../../ajax/domainlist/1",
|
||||
url: getPath() + "/ajax/domainlist/1",
|
||||
async: true,
|
||||
timeout: 900,
|
||||
success:function(data) {
|
||||
@@ -590,7 +660,7 @@ $(function() {
|
||||
});
|
||||
$.ajax({
|
||||
method:"get",
|
||||
url: window.location.pathname + "/../../ajax/domainlist/0",
|
||||
url: getPath() + "/ajax/domainlist/0",
|
||||
async: true,
|
||||
timeout: 900,
|
||||
success:function(data) {
|
||||
@@ -826,7 +896,7 @@ $(function() {
|
||||
method:"post",
|
||||
contentType: "application/json; charset=utf-8",
|
||||
dataType: "json",
|
||||
url: window.location.pathname + "/../../ajax/user_table_settings",
|
||||
url: getPath() + "/ajax/user_table_settings",
|
||||
data: "{" + st + "}",
|
||||
});
|
||||
handle_header_buttons();
|
||||
@@ -852,8 +922,8 @@ $(function() {
|
||||
|
||||
function handle_header_buttons () {
|
||||
if (selections.length < 1) {
|
||||
$("#user_delete_selection").addClass("disabled");
|
||||
$("#user_delete_selection").attr("aria-disabled", true);
|
||||
$(".mass_selection").addClass("disabled");
|
||||
$(".mass_selection").attr("aria-disabled", true);
|
||||
$(".check_head").attr("aria-disabled", true);
|
||||
$(".check_head").attr("disabled", true);
|
||||
$(".check_head").prop('checked', false);
|
||||
@@ -865,8 +935,8 @@ function handle_header_buttons () {
|
||||
$(".multi_selector").attr("disabled", true);
|
||||
$(".header_select").attr("disabled", true);
|
||||
} else {
|
||||
$("#user_delete_selection").removeClass("disabled");
|
||||
$("#user_delete_selection").attr("aria-disabled", false);
|
||||
$(".mass_selection").removeClass("disabled");
|
||||
$(".mass_selection").attr("aria-disabled", false);
|
||||
$(".check_head").attr("aria-disabled", false);
|
||||
$(".check_head").removeAttr("disabled");
|
||||
$(".button_head").attr("aria-disabled", false);
|
||||
@@ -875,8 +945,10 @@ function handle_header_buttons () {
|
||||
$(".multi_head").removeClass("hidden");
|
||||
$(".multi_selector").attr("aria-disabled", false);
|
||||
$(".multi_selector").removeAttr("disabled");
|
||||
$('.multi_selector').selectpicker('refresh');
|
||||
$(".header_select").removeAttr("disabled");
|
||||
if (typeof $.fn.selectpicker === "function") {
|
||||
$('.multi_selector').selectpicker('refresh');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1045,7 +1117,7 @@ function move_header_elements() {
|
||||
function () {
|
||||
$.ajax({
|
||||
method: "post",
|
||||
url: window.location.pathname + "/../../ajax/editlistusers/" + field,
|
||||
url: getPath() + "/ajax/editlistusers/" + field,
|
||||
data: {"pk": result, "value": values, "action": val},
|
||||
success: function (data) {
|
||||
handleListServerResponse(data);
|
||||
@@ -1059,7 +1131,6 @@ function move_header_elements() {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
$("#user_delete_selection").click(function () {
|
||||
$("#user-table").bootstrapTable("uncheckAll");
|
||||
});
|
||||
@@ -1090,8 +1161,10 @@ function move_header_elements() {
|
||||
function () {
|
||||
$.ajax({
|
||||
method: "post",
|
||||
url: window.location.pathname + "/../../ajax/deleteuser",
|
||||
data: {"userid": result},
|
||||
url: getPath() + "/ajax/deleteuser",
|
||||
contentType: "application/json; charset=utf-8",
|
||||
dataType: "json",
|
||||
data: JSON.stringify({"userid": result}),
|
||||
success: function (data) {
|
||||
selections = selections.filter((el) => !result.includes(el));
|
||||
handleListServerResponse(data);
|
||||
@@ -1117,7 +1190,7 @@ function handleListServerResponse (data) {
|
||||
'</div>');
|
||||
});
|
||||
}
|
||||
$("#user-table").bootstrapTable("refresh");
|
||||
$(".table.table-striped").bootstrapTable("refresh");
|
||||
}
|
||||
|
||||
function checkboxChange(checkbox, userId, field, field_index) {
|
||||
@@ -1132,20 +1205,21 @@ function checkboxChange(checkbox, userId, field, field_index) {
|
||||
});
|
||||
}
|
||||
|
||||
function BookCheckboxChange(checkbox, userId, field) {
|
||||
function BookCheckboxChange(checkbox, bookId, field) {
|
||||
var value = checkbox.checked ? "True" : "False";
|
||||
var element = checkbox;
|
||||
$.ajax({
|
||||
method: "post",
|
||||
url: getPath() + "/ajax/editbooks/" + field,
|
||||
data: {"pk": userId, "value": value},
|
||||
data: JSON.stringify({"pk": [bookId], "value": value}),
|
||||
contentType: "application/json; charset=utf-8",
|
||||
dataType: "json",
|
||||
error: function(data) {
|
||||
element.checked = !element.checked;
|
||||
handleListServerResponse([{type:"danger", message:data.responseText}])
|
||||
},
|
||||
success: handleListServerResponse
|
||||
});
|
||||
console.log("test");
|
||||
}
|
||||
|
||||
function selectHeader(element, field) {
|
||||
@@ -1154,7 +1228,7 @@ function selectHeader(element, field) {
|
||||
var result = $('#user-table').bootstrapTable('getSelections').map(a => a.id);
|
||||
$.ajax({
|
||||
method: "post",
|
||||
url: window.location.pathname + "/../../ajax/editlistusers/" + field,
|
||||
url: getPath() + "/ajax/editlistusers/" + field,
|
||||
data: {"pk": result, "value": element.value},
|
||||
error: function (data) {
|
||||
handleListServerResponse([{type:"danger", message:data.responseText}])
|
||||
@@ -1167,12 +1241,35 @@ function selectHeader(element, field) {
|
||||
}
|
||||
}
|
||||
|
||||
function bookCheckboxHeader(CheckboxState, text, field_index) {
|
||||
confirmDialog(text, "GeneralChangeModal", 0, function() {
|
||||
var result = $('#books-table').bootstrapTable('getSelections').map(a => a.id);
|
||||
$.ajax({
|
||||
method: "post",
|
||||
url: getPath() + "/ajax/editbooks/" + field_index,
|
||||
data: JSON.stringify({"pk": result, "field_index": field_index, "value": CheckboxState}),
|
||||
contentType: "application/json; charset=utf-8",
|
||||
dataType: "json",
|
||||
error: function (data) {
|
||||
handleListServerResponse([{type:"danger", message:data.responseText}])
|
||||
},
|
||||
success: function (data) {
|
||||
handleListServerResponse (data, true)
|
||||
},
|
||||
});
|
||||
},function() {
|
||||
$("input:radio.check_head:checked").each(function() {
|
||||
$(this).prop('checked', false);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function checkboxHeader(CheckboxState, field, field_index) {
|
||||
confirmDialog(field, "GeneralChangeModal", 0, function() {
|
||||
var result = $('#user-table').bootstrapTable('getSelections').map(a => a.id);
|
||||
$.ajax({
|
||||
method: "post",
|
||||
url: window.location.pathname + "/../../ajax/editlistusers/" + field,
|
||||
url: getPath() + "/ajax/editlistusers/" + field,
|
||||
data: {"pk": result, "field_index": field_index, "value": CheckboxState},
|
||||
error: function (data) {
|
||||
handleListServerResponse([{type:"danger", message:data.responseText}])
|
||||
@@ -1196,8 +1293,10 @@ function deleteUser(a,id){
|
||||
function() {
|
||||
$.ajax({
|
||||
method:"post",
|
||||
url: window.location.pathname + "/../../ajax/deleteuser",
|
||||
data: {"userid":id},
|
||||
url: getPath() + "/ajax/deleteuser",
|
||||
contentType: "application/json; charset=utf-8",
|
||||
dataType: "json",
|
||||
data: JSON.stringify({"userid": [id]}),
|
||||
success: function (data) {
|
||||
userId = parseInt(id, 10);
|
||||
selections = selections.filter(item => item !== userId);
|
||||
@@ -1224,8 +1323,10 @@ function storeLocation() {
|
||||
function user_handle (userId) {
|
||||
$.ajax({
|
||||
method:"post",
|
||||
url: window.location.pathname + "/../../ajax/deleteuser",
|
||||
data: {"userid":userId}
|
||||
contentType: "application/json; charset=utf-8",
|
||||
dataType: "json",
|
||||
data: JSON.stringify({"userid": [userId]}),
|
||||
url: getPath() + "/ajax/deleteuser",
|
||||
});
|
||||
$("#user-table").bootstrapTable("refresh");
|
||||
}
|
||||
@@ -1233,6 +1334,5 @@ function user_handle (userId) {
|
||||
function shorten_html(value, response) {
|
||||
if(value) {
|
||||
$(this).html("[...]");
|
||||
// value.split('\n').slice(0, 2).join("") +
|
||||
}
|
||||
}
|
||||
|
@@ -15,27 +15,28 @@
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro book_checkbox_row(parameter, show_text, sort) -%}
|
||||
<th data-name="{{parameter}}" data-field="{{parameter}}" data-switchable="false"
|
||||
<th data-name="{{parameter}}" data-field="{{parameter}}"
|
||||
{% if sort %}data-sortable="true" {% endif %}
|
||||
data-visible="{{visiblility.get(parameter)}}"
|
||||
data-formatter="bookCheckboxFormatter">
|
||||
{% if parameter == "is_archived" %}
|
||||
<div class="btn btn-default disabled" id="archive_selected_books" aria-disabled="true">
|
||||
{{_('Archive selected books')}}
|
||||
<div class="form-check">
|
||||
<div>
|
||||
<input type="radio" class="check_head" data-set="True" data-val="{{ parameter }}" name="options_archive_selected_books" id="false_archive_selected_books" data-name="archive_books" disabled>{{_('Archive selected books')}}
|
||||
</div>
|
||||
<div>
|
||||
<input type="radio" class="check_head" data-set="False" data-val="{{ parameter }}" name="options_unarchive_selected_books" data-name="archive_books" disabled>{{_('Unarchive selected books')}}
|
||||
</div>
|
||||
<br>
|
||||
<div class="btn btn-default disabled" id="unarchive_selected_books" aria-disabled="true">
|
||||
{{_('Unarchive selected books')}}
|
||||
</div>
|
||||
<br>
|
||||
{% elif parameter == "read_status" %}
|
||||
<div class="btn btn-default disabled" id="read_selected_books" aria-disabled="true">
|
||||
{{_('Mark selected books as read')}}
|
||||
<div class="form-check">
|
||||
<div>
|
||||
<input type="radio" class="check_head" data-set="True" data-val="{{ parameter }}" name="options_read_selected_books" id="false_read_selected_books" data-name="read_books" disabled>{{_('Mark selected books as read')}}
|
||||
</div>
|
||||
<div>
|
||||
<input type="radio" class="check_head" data-set="False" data-val="{{ parameter }}" name="options_unread_selected_books" data-name="read_books" disabled>{{_('Mark selected books as unread')}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
<div class="btn btn-default disabled" id="unread_selected_books" aria-disabled="true">
|
||||
{{_('Mark selected books as unread')}}</div>
|
||||
<br>
|
||||
{% endif %}
|
||||
{{show_text}}
|
||||
</th>
|
||||
@@ -55,7 +56,7 @@
|
||||
<div class="btn btn-default disabled" id="merge_books" aria-disabled="true">
|
||||
{{_('Merge selected books')}}
|
||||
</div>
|
||||
<div class="btn btn-default disabled" id="delete_selection" aria-disabled="true">
|
||||
<div class="btn btn-default disabled mass_selection" id="book_delete_selection" aria-disabled="true">
|
||||
{{_('Clear selections')}}
|
||||
</div>
|
||||
<div class="btn btn-default disabled" id="edit_selected_books" aria-disabled="true">
|
||||
@@ -82,7 +83,7 @@
|
||||
<thead>
|
||||
<tr>
|
||||
{% if current_user.role_edit() %}
|
||||
<th data-field="state" data-checkbox="true" data-sortable="true"></th>
|
||||
<th data-field="state" data-checkbox="true" data-visible="true" data-sortable="true"></th>
|
||||
{% endif %}
|
||||
<th data-field="id" id="id" data-visible="false" data-switchable="false"></th>
|
||||
{{ text_table_row('title', _('Enter Title'),_('Title'), true, true) }}
|
||||
@@ -123,9 +124,9 @@
|
||||
{% endfor %}
|
||||
{% if current_user.role_delete_books() and current_user.role_edit()%}
|
||||
<th data-align="right" data-formatter="EbookActions" data-switchable="false">
|
||||
<div class="btn btn-default disabled" id="delete_selected_books" aria-disabled="true">
|
||||
<div><div class="btn btn-default button_head disabled" aria-disabled="true">
|
||||
{{_('Delete selected books')}}
|
||||
</div>
|
||||
</div></div>
|
||||
<br>
|
||||
{{_('Delete')}}
|
||||
</th>
|
||||
@@ -137,6 +138,8 @@
|
||||
{% endblock %}
|
||||
{% block modal %}
|
||||
{{ delete_book(current_user.role_delete_books()) }}
|
||||
{{ delete_confirm_modal() }}
|
||||
{{ change_confirm_modal() }}
|
||||
{% if current_user.role_edit() %}
|
||||
<div class="modal fade" id="mergeModal" role="dialog" aria-labelledby="metaMergeLabel">
|
||||
<div class="modal-dialog">
|
||||
@@ -162,7 +165,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="delete_selected_modal" role="dialog" aria-labelledby="metaDeleteSelectedLabel">
|
||||
<!--div class="modal fade" id="delete_selected_modal" role="dialog" aria-labelledby="metaDeleteSelectedLabel">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header bg-danger text-center">
|
||||
@@ -180,7 +183,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div-->
|
||||
|
||||
<div class="modal fade" id="archive_selected_modal" role="dialog" aria-labelledby="metaArchiveSelectedLabel">
|
||||
<div class="modal-dialog">
|
||||
@@ -265,7 +268,7 @@
|
||||
<div class="modal fade" id="edit_selected_modal" role="dialog" aria-labelledby="metaEditSelectedLabel">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header text-center">
|
||||
<div class="modal-header bg-info text-center">
|
||||
<span>{{_('Edit Metadata')}}</span>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
|
@@ -121,7 +121,7 @@
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||||
<div class="col-xs-12 col-sm-12">
|
||||
<div class="row">
|
||||
<div class="btn btn-default disabled" id="user_delete_selection" aria-disabled="true">{{_('Remove Selections')}}</div>
|
||||
<div class="btn btn-default disabled mass_selection" id="user_delete_selection" aria-disabled="true">{{_('Clear selections')}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<table id="user-table" class="table table-no-bordered table-striped"
|
||||
|
@@ -71,13 +71,13 @@ content-type = "text/markdown"
|
||||
gdrive = [
|
||||
"google-api-python-client>=1.7.11,<2.200.0",
|
||||
"gevent>20.6.0,<24.3.0",
|
||||
"greenlet>=0.4.17,<3.1.0",
|
||||
"greenlet>=0.4.17,<3.2.0",
|
||||
"httplib2>=0.9.2,<0.23.0",
|
||||
"oauth2client>=4.0.0,<4.1.4",
|
||||
"uritemplate>=3.0.0,<4.2.0",
|
||||
"pyasn1-modules>=0.0.8,<0.5.0",
|
||||
"pyasn1>=0.1.9,<0.7.0",
|
||||
"PyDrive2>=1.3.1,<1.20.0",
|
||||
"PyDrive2>=1.3.1,<1.22.0",
|
||||
"PyYAML>=3.12,<6.1",
|
||||
"rsa>=3.4.2,<4.10.0",
|
||||
]
|
||||
|
@@ -37,20 +37,20 @@
|
||||
<div class="row">
|
||||
<div class="col-xs-6 col-md-6 col-sm-offset-3" style="margin-top:50px;">
|
||||
|
||||
<p class='text-justify attribute'><strong>Start Time: </strong>2024-12-06 17:23:58</p>
|
||||
<p class='text-justify attribute'><strong>Start Time: </strong>2024-12-12 21:32:32</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-6 col-md-6 col-sm-offset-3">
|
||||
|
||||
<p class='text-justify attribute'><strong>Stop Time: </strong>2024-12-07 00:45:19</p>
|
||||
<p class='text-justify attribute'><strong>Stop Time: </strong>2024-12-13 04:26:53</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-6 col-md-6 col-sm-offset-3">
|
||||
<p class='text-justify attribute'><strong>Duration: </strong>6h 11 min</p>
|
||||
<p class='text-justify attribute'><strong>Duration: </strong>5h 49 min</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -2074,11 +2074,11 @@ IndexError: list index out of range</pre>
|
||||
|
||||
|
||||
|
||||
<tr id="su" class="failClass">
|
||||
<tr id="su" class="passClass">
|
||||
<td>TestEditBooksOnGdrive</td>
|
||||
<td class="text-center">18</td>
|
||||
<td class="text-center">16</td>
|
||||
<td class="text-center">2</td>
|
||||
<td class="text-center">18</td>
|
||||
<td class="text-center">0</td>
|
||||
<td class="text-center">0</td>
|
||||
<td class="text-center">0</td>
|
||||
<td class="text-center">
|
||||
@@ -2205,31 +2205,11 @@ IndexError: list index out of range</pre>
|
||||
|
||||
|
||||
|
||||
<tr id="ft19.14" class="none bg-danger">
|
||||
<tr id='pt19.14' class='hiddenRow bg-success'>
|
||||
<td>
|
||||
<div class='testcase'>TestEditBooksOnGdrive - test_edit_rating</div>
|
||||
</td>
|
||||
<td colspan='6'>
|
||||
<div class="text-center">
|
||||
<a class="popup_link text-center" onfocus='blur()' onclick="showTestDetail('div_ft19.14')">FAIL</a>
|
||||
</div>
|
||||
<!--css div popup start-->
|
||||
<div id="div_ft19.14" class="popup_window test_output" style="display:block;">
|
||||
<div class='close_button pull-right'>
|
||||
<button type="button" class="close" aria-label="Close" onfocus="this.blur();"
|
||||
onclick="document.getElementById('div_ft19.14').style.display='none'"><span
|
||||
aria-hidden="true">×</span></button>
|
||||
</div>
|
||||
<div class="text-left pull-left">
|
||||
<pre class="text-left">Traceback (most recent call last):
|
||||
File "/home/ozzie/Development/calibre-web-test/test/test_edit_ebooks_gdrive.py", line 632, in test_edit_rating
|
||||
self.assertEqual(4, values['rating'])
|
||||
AssertionError: 4 != 0</pre>
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
<!--css div popup end-->
|
||||
</td>
|
||||
<td colspan='6' align='center'>PASS</td>
|
||||
</tr>
|
||||
|
||||
|
||||
@@ -2261,31 +2241,11 @@ AssertionError: 4 != 0</pre>
|
||||
|
||||
|
||||
|
||||
<tr id="ft19.18" class="none bg-danger">
|
||||
<tr id='pt19.18' class='hiddenRow bg-success'>
|
||||
<td>
|
||||
<div class='testcase'>TestEditBooksOnGdrive - test_watch_metadata</div>
|
||||
</td>
|
||||
<td colspan='6'>
|
||||
<div class="text-center">
|
||||
<a class="popup_link text-center" onfocus='blur()' onclick="showTestDetail('div_ft19.18')">FAIL</a>
|
||||
</div>
|
||||
<!--css div popup start-->
|
||||
<div id="div_ft19.18" class="popup_window test_output" style="display:block;">
|
||||
<div class='close_button pull-right'>
|
||||
<button type="button" class="close" aria-label="Close" onfocus="this.blur();"
|
||||
onclick="document.getElementById('div_ft19.18').style.display='none'"><span
|
||||
aria-hidden="true">×</span></button>
|
||||
</div>
|
||||
<div class="text-left pull-left">
|
||||
<pre class="text-left">Traceback (most recent call last):
|
||||
File "/home/ozzie/Development/calibre-web-test/test/test_edit_ebooks_gdrive.py", line 976, in test_watch_metadata
|
||||
self.assertNotIn('series', book)
|
||||
AssertionError: 'series' unexpectedly found in {'id': 5, 'reader': [], 'title': 'testbook', 'author': ['John Döe'], 'rating': 0, 'languages': ['English'], 'identifier': [], 'cover': '/cover/5/og?c=1733511155', 'tag': [], 'publisher': ['Randomhäus'], 'pubdate': 'Jan 19, 2017', 'comment': 'Lorem ipsum dolor sit amet, consectetuer adipiscing elit.Aenean commodo ligula eget dolor.Aenean massa.Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem.Nulla consequat massa quis enim.Donec pede justo, fringilla vel, aliquet nec, vulputate', 'add_shelf': [], 'del_shelf': [], 'edit_enable': True, 'kindle': None, 'kindlebtn': None, 'download': ['EPUB\n (6.7 kB)'], 'read': False, 'archived': False, 'series_all': 'Book 1 of test', 'series_index': '1', 'series': 'test', 'cust_columns': []}</pre>
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
<!--css div popup end-->
|
||||
</td>
|
||||
<td colspan='6' align='center'>PASS</td>
|
||||
</tr>
|
||||
|
||||
|
||||
@@ -3579,11 +3539,11 @@ AssertionError: 'series' unexpectedly found in {'id': 5, 're
|
||||
|
||||
|
||||
|
||||
<tr id="su" class="passClass">
|
||||
<tr id="su" class="failClass">
|
||||
<td>TestMergeBooksList</td>
|
||||
<td class="text-center">2</td>
|
||||
<td class="text-center">2</td>
|
||||
<td class="text-center">0</td>
|
||||
<td class="text-center">1</td>
|
||||
<td class="text-center">1</td>
|
||||
<td class="text-center">0</td>
|
||||
<td class="text-center">0</td>
|
||||
<td class="text-center">
|
||||
@@ -3602,11 +3562,31 @@ AssertionError: 'series' unexpectedly found in {'id': 5, 're
|
||||
|
||||
|
||||
|
||||
<tr id='pt37.2' class='hiddenRow bg-success'>
|
||||
<tr id="ft37.2" class="none bg-danger">
|
||||
<td>
|
||||
<div class='testcase'>TestMergeBooksList - test_delete_book</div>
|
||||
</td>
|
||||
<td colspan='6' align='center'>PASS</td>
|
||||
<td colspan='6'>
|
||||
<div class="text-center">
|
||||
<a class="popup_link text-center" onfocus='blur()' onclick="showTestDetail('div_ft37.2')">FAIL</a>
|
||||
</div>
|
||||
<!--css div popup start-->
|
||||
<div id="div_ft37.2" class="popup_window test_output" style="display:block;">
|
||||
<div class='close_button pull-right'>
|
||||
<button type="button" class="close" aria-label="Close" onfocus="this.blur();"
|
||||
onclick="document.getElementById('div_ft37.2').style.display='none'"><span
|
||||
aria-hidden="true">×</span></button>
|
||||
</div>
|
||||
<div class="text-left pull-left">
|
||||
<pre class="text-left">Traceback (most recent call last):
|
||||
File "/home/ozzie/Development/calibre-web-test/test/test_merge_books_list.py", line 67, in test_delete_book
|
||||
self.assertTrue(self.check_element_on_page((By.ID, "flash_warning")))
|
||||
AssertionError: False is not true</pre>
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
<!--css div popup end-->
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
|
||||
@@ -5841,8 +5821,8 @@ AssertionError: 'series' unexpectedly found in {'id': 5, 're
|
||||
<tr id='total_row' class="text-center bg-grey">
|
||||
<td>Total</td>
|
||||
<td>523</td>
|
||||
<td>513</td>
|
||||
<td>2</td>
|
||||
<td>514</td>
|
||||
<td>1</td>
|
||||
<td>1</td>
|
||||
<td>7</td>
|
||||
<td> </td>
|
||||
@@ -6046,7 +6026,7 @@ AssertionError: 'series' unexpectedly found in {'id': 5, 're
|
||||
|
||||
<tr>
|
||||
<th>google-api-python-client</th>
|
||||
<td>2.154.0</td>
|
||||
<td>2.155.0</td>
|
||||
<td>TestBackupMetadataGdrive</td>
|
||||
</tr>
|
||||
|
||||
@@ -6076,7 +6056,7 @@ AssertionError: 'series' unexpectedly found in {'id': 5, 're
|
||||
|
||||
<tr>
|
||||
<th>google-api-python-client</th>
|
||||
<td>2.154.0</td>
|
||||
<td>2.155.0</td>
|
||||
<td>TestCliGdrivedb</td>
|
||||
</tr>
|
||||
|
||||
@@ -6106,7 +6086,7 @@ AssertionError: 'series' unexpectedly found in {'id': 5, 're
|
||||
|
||||
<tr>
|
||||
<th>google-api-python-client</th>
|
||||
<td>2.154.0</td>
|
||||
<td>2.155.0</td>
|
||||
<td>TestEbookConvertCalibreGDrive</td>
|
||||
</tr>
|
||||
|
||||
@@ -6136,7 +6116,7 @@ AssertionError: 'series' unexpectedly found in {'id': 5, 're
|
||||
|
||||
<tr>
|
||||
<th>google-api-python-client</th>
|
||||
<td>2.154.0</td>
|
||||
<td>2.155.0</td>
|
||||
<td>TestEbookConvertGDriveKepubify</td>
|
||||
</tr>
|
||||
|
||||
@@ -6184,7 +6164,7 @@ AssertionError: 'series' unexpectedly found in {'id': 5, 're
|
||||
|
||||
<tr>
|
||||
<th>google-api-python-client</th>
|
||||
<td>2.154.0</td>
|
||||
<td>2.155.0</td>
|
||||
<td>TestEditAuthorsGdrive</td>
|
||||
</tr>
|
||||
|
||||
@@ -6220,7 +6200,7 @@ AssertionError: 'series' unexpectedly found in {'id': 5, 're
|
||||
|
||||
<tr>
|
||||
<th>google-api-python-client</th>
|
||||
<td>2.154.0</td>
|
||||
<td>2.155.0</td>
|
||||
<td>TestEditBooksOnGdrive</td>
|
||||
</tr>
|
||||
|
||||
@@ -6262,7 +6242,7 @@ AssertionError: 'series' unexpectedly found in {'id': 5, 're
|
||||
|
||||
<tr>
|
||||
<th>google-api-python-client</th>
|
||||
<td>2.154.0</td>
|
||||
<td>2.155.0</td>
|
||||
<td>TestEmbedMetadataGdrive</td>
|
||||
</tr>
|
||||
|
||||
@@ -6292,7 +6272,7 @@ AssertionError: 'series' unexpectedly found in {'id': 5, 're
|
||||
|
||||
<tr>
|
||||
<th>google-api-python-client</th>
|
||||
<td>2.154.0</td>
|
||||
<td>2.155.0</td>
|
||||
<td>TestSetupGdrive</td>
|
||||
</tr>
|
||||
|
||||
@@ -6388,7 +6368,7 @@ AssertionError: 'series' unexpectedly found in {'id': 5, 're
|
||||
</div>
|
||||
|
||||
<script>
|
||||
drawCircle(513, 2, 1, 7);
|
||||
drawCircle(514, 1, 1, 7);
|
||||
showCase(5);
|
||||
</script>
|
||||
|
||||
|
Reference in New Issue
Block a user