Merge remote-tracking branch 'origin/back' into Develop

This commit is contained in:
Ozzie Isaacs 2024-02-26 18:07:57 +01:00
commit 4fbd064b85
10 changed files with 43 additions and 32 deletions

View File

@ -60,6 +60,7 @@ from .tasks.upload import TaskUpload
from .render_template import render_title_template from .render_template import render_title_template
from .usermanagement import login_required_if_no_ano from .usermanagement import login_required_if_no_ano
from .kobo_sync_status import change_archived_books from .kobo_sync_status import change_archived_books
from .redirect import get_redirect_location
editbook = Blueprint('edit-book', __name__) editbook = Blueprint('edit-book', __name__)
@ -96,7 +97,7 @@ def delete_book_from_details(book_id):
@editbook.route("/delete/<int:book_id>/<string:book_format>", methods=["POST"]) @editbook.route("/delete/<int:book_id>/<string:book_format>", methods=["POST"])
@login_required @login_required
def delete_book_ajax(book_id, book_format): def delete_book_ajax(book_id, book_format):
return delete_book_from_table(book_id, book_format, False) return delete_book_from_table(book_id, book_format, False, request.form.to_dict().get('location', ""))
@editbook.route("/admin/book/<int:book_id>", methods=['GET']) @editbook.route("/admin/book/<int:book_id>", methods=['GET'])
@ -823,7 +824,7 @@ def delete_whole_book(book_id, book):
calibre_db.session.query(db.Books).filter(db.Books.id == book_id).delete() 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): def render_delete_book_result(book_format, json_response, warning, book_id, location=""):
if book_format: if book_format:
if json_response: if json_response:
return json.dumps([warning, {"location": url_for("edit-book.show_edit_book", book_id=book_id), return json.dumps([warning, {"location": url_for("edit-book.show_edit_book", book_id=book_id),
@ -835,16 +836,16 @@ def render_delete_book_result(book_format, json_response, warning, book_id):
return redirect(url_for('edit-book.show_edit_book', book_id=book_id)) return redirect(url_for('edit-book.show_edit_book', book_id=book_id))
else: else:
if json_response: if json_response:
return json.dumps([warning, {"location": url_for('web.index'), return json.dumps([warning, {"location": get_redirect_location(location, "web.index"),
"type": "success", "type": "success",
"format": book_format, "format": book_format,
"message": _('Book Successfully Deleted')}]) "message": _('Book Successfully Deleted')}])
else: else:
flash(_('Book Successfully Deleted'), category="success") flash(_('Book Successfully Deleted'), category="success")
return redirect(url_for('web.index')) return redirect(get_redirect_location(location, "web.index"))
def delete_book_from_table(book_id, book_format, json_response): def delete_book_from_table(book_id, book_format, json_response, location=""):
warning = {} warning = {}
if current_user.role_delete_books(): if current_user.role_delete_books():
book = calibre_db.get_book(book_id) book = calibre_db.get_book(book_id)
@ -891,7 +892,7 @@ def delete_book_from_table(book_id, book_format, json_response):
else: else:
# book not found # book not found
log.error('Book with id "%s" could not be deleted: not found', book_id) 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) return render_delete_book_result(book_format, json_response, warning, book_id, location)
message = _("You are missing permissions to delete books") message = _("You are missing permissions to delete books")
if json_response: if json_response:
return json.dumps({"location": url_for("edit-book.show_edit_book", book_id=book_id), return json.dumps({"location": url_for("edit-book.show_edit_book", book_id=book_id),

View File

@ -692,15 +692,15 @@ def valid_password(check_password):
if config.config_password_policy: if config.config_password_policy:
verify = "" verify = ""
if config.config_password_min_length > 0: if config.config_password_min_length > 0:
verify += "^(?=.{" + str(config.config_password_min_length) + ",}$)" verify += r"^(?=.{" + str(config.config_password_min_length) + ",}$)"
if config.config_password_number: if config.config_password_number:
verify += "(?=.*?\d)" verify += r"(?=.*?\d)"
if config.config_password_lower: if config.config_password_lower:
verify += "(?=.*?[a-z])" verify += r"(?=.*?[a-z])"
if config.config_password_upper: if config.config_password_upper:
verify += "(?=.*?[A-Z])" verify += r"(?=.*?[A-Z])"
if config.config_password_special: if config.config_password_special:
verify += "(?=.*?[^A-Za-z\s0-9])" verify += r"(?=.*?[^A-Za-z\s0-9])"
match = re.match(verify, check_password) match = re.match(verify, check_password)
if not match: if not match:
raise Exception(_("Password doesn't comply with password validation rules")) raise Exception(_("Password doesn't comply with password validation rules"))
@ -1039,7 +1039,7 @@ def check_calibre(calibre_location):
binaries_available = [os.path.isfile(binary_path) for binary_path in supported_binary_paths] binaries_available = [os.path.isfile(binary_path) for binary_path in supported_binary_paths]
binaries_executable = [os.access(binary_path, os.X_OK) for binary_path in supported_binary_paths] binaries_executable = [os.access(binary_path, os.X_OK) for binary_path in supported_binary_paths]
if all(binaries_available) and all(binaries_executable): if all(binaries_available) and all(binaries_executable):
values = [process_wait([binary_path, "--version"], pattern='\(calibre (.*)\)') values = [process_wait([binary_path, "--version"], pattern=r'\(calibre (.*)\)')
for binary_path in supported_binary_paths] for binary_path in supported_binary_paths]
if all(values): if all(values):
version = values[0].group(1) version = values[0].group(1)

View File

@ -44,9 +44,9 @@ def remove_prefix(text, prefix):
return "" return ""
def redirect_back(endpoint, **values): def get_redirect_location(next, endpoint, **values):
target = request.form.get('next', None) or url_for(endpoint, **values) target = next or url_for(endpoint, **values)
adapter = current_app.url_map.bind(urlparse(request.host_url).netloc) adapter = current_app.url_map.bind(urlparse(request.host_url).netloc)
if not len(adapter.allowed_methods(remove_prefix(target, request.environ.get('HTTP_X_SCRIPT_NAME',"")))): if not len(adapter.allowed_methods(remove_prefix(target, request.environ.get('HTTP_X_SCRIPT_NAME',"")))):
target = url_for(endpoint, **values) target = url_for(endpoint, **values)
return redirect(target) return target

View File

@ -20,7 +20,7 @@ function getPath() {
return jsFileLocation.substr(0, jsFileLocation.search("/static/js/libs/jquery.min.js")); // the js folder path return jsFileLocation.substr(0, jsFileLocation.search("/static/js/libs/jquery.min.js")); // the js folder path
} }
function postButton(event, action){ function postButton(event, action, location=""){
event.preventDefault(); event.preventDefault();
var newForm = jQuery('<form>', { var newForm = jQuery('<form>', {
"action": action, "action": action,
@ -30,7 +30,14 @@ function postButton(event, action){
'name': 'csrf_token', 'name': 'csrf_token',
'value': $("input[name=\'csrf_token\']").val(), 'value': $("input[name=\'csrf_token\']").val(),
'type': 'hidden' 'type': 'hidden'
})).appendTo('body'); })).appendTo('body')
if(location !== "") {
newForm.append(jQuery('<input>', {
'name': 'location',
'value': location,
'type': 'hidden'
})).appendTo('body');
}
newForm.submit(); newForm.submit();
} }
@ -212,17 +219,20 @@ $("#delete_confirm").click(function(event) {
$( ".navbar" ).after( '<div class="row-fluid text-center" >' + $( ".navbar" ).after( '<div class="row-fluid text-center" >' +
'<div id="flash_'+item.type+'" class="alert alert-'+item.type+'">'+item.message+'</div>' + '<div id="flash_'+item.type+'" class="alert alert-'+item.type+'">'+item.message+'</div>' +
'</div>'); '</div>');
} }
}); });
$("#books-table").bootstrapTable("refresh"); $("#books-table").bootstrapTable("refresh");
} }
}); });
} else { } else {
postButton(event, getPath() + "/delete/" + deleteId); var loc = sessionStorage.getItem("back");
if (!loc) {
loc = $(this).data("back");
}
sessionStorage.removeItem("back");
postButton(event, getPath() + "/delete/" + deleteId, location=loc);
} }
} }
}); });
//triggered when modal is about to be shown //triggered when modal is about to be shown
@ -541,6 +551,7 @@ $(function() {
$.get(e.relatedTarget.href).done(function(content) { $.get(e.relatedTarget.href).done(function(content) {
$modalBody.html(content); $modalBody.html(content);
preFilters.remove(useCache); preFilters.remove(useCache);
$("#back").remove();
}); });
}) })
.on("hidden.bs.modal", function() { .on("hidden.bs.modal", function() {

View File

@ -32,7 +32,7 @@
</div> </div>
<div class="row display-flex"> <div class="row display-flex">
{% for entry in entries %} {% for entry in entries %}
<div id="books" class="col-sm-3 col-lg-2 col-xs-6 book"> <div id="books" class="col-sm-3 col-lg-2 col-xs-6 book session">
<div class="cover"> <div class="cover">
<a href="{{ url_for('web.show_book', book_id=entry.Books.id) }}" {% if simple==false %}data-toggle="modal" data-target="#bookDetailsModal" data-remote="false"{% endif %}> <a href="{{ url_for('web.show_book', book_id=entry.Books.id) }}" {% if simple==false %}data-toggle="modal" data-target="#bookDetailsModal" data-remote="false"{% endif %}>
<span class="img" title="{{entry.Books.title}}"> <span class="img" title="{{entry.Books.title}}">
@ -99,7 +99,7 @@
<h3>{{_("More by")}} {{ author.name.replace('|',',') }}</h3> <h3>{{_("More by")}} {{ author.name.replace('|',',') }}</h3>
<div class="row"> <div class="row">
{% for entry in other_books %} {% for entry in other_books %}
<div class="col-sm-3 col-lg-2 col-xs-6 book"> <div class="col-sm-3 col-lg-2 col-xs-6 book session">
<div class="cover"> <div class="cover">
<a href="https://www.goodreads.com/book/show/{{ entry.gid['#text'] }}" target="_blank" rel="noopener"> <a href="https://www.goodreads.com/book/show/{{ entry.gid['#text'] }}" target="_blank" rel="noopener">
<img title="{{entry.title}}" src="{{ entry.image_url }}" /> <img title="{{entry.title}}" src="{{ entry.image_url }}" />

7
cps/templates/detail.html Executable file → Normal file
View File

@ -333,15 +333,15 @@
{% endif %} {% endif %}
{% if current_user.role_edit() %} {% if current_user.role_edit() %}
<div class="btn-toolbar" role="toolbar"> <div class="col-sm-12">
<div class="btn-group" role="group" aria-label="Edit/Delete book"> <div class="btn-group" role="group" aria-label="Edit/Delete book">
<a href="{{ url_for('edit-book.show_edit_book', book_id=entry.id) }}" <a href="{{ url_for('edit-book.show_edit_book', book_id=entry.id) }}"
class="btn btn-sm btn-primary" id="edit_book" role="button"><span class="btn btn-sm btn-primary" id="edit_book" role="button"><span
class="glyphicon glyphicon-edit"></span> {{ _('Edit Metadata') }}</a> class="glyphicon glyphicon-edit"></span> {{ _('Edit Metadata') }}</a>
</div> </div>
</div> <div class="btn btn-default" data-back="{{ url_for('web.index') }}" id="back">{{_('Cancel')}}</div>
</div>
{% endif %} {% endif %}
</div>
</div> </div>
</div> </div>
</div> </div>
@ -367,4 +367,3 @@
</script> </script>
{% endblock %} {% endblock %}

View File

@ -6,7 +6,7 @@
<h2 class="random-books">{{_('Discover (Random Books)')}}</h2> <h2 class="random-books">{{_('Discover (Random Books)')}}</h2>
<div class="row display-flex"> <div class="row display-flex">
{% for entry in random %} {% for entry in random %}
<div class="col-sm-3 col-lg-2 col-xs-6 book" id="books_rand"> <div class="col-sm-3 col-lg-2 col-xs-6 book session" id="books_rand">
<div class="cover"> <div class="cover">
<a href="{{ url_for('web.show_book', book_id=entry.Books.id) }}" {% if simple==false %}data-toggle="modal" data-target="#bookDetailsModal" data-remote="false"{% endif %}> <a href="{{ url_for('web.show_book', book_id=entry.Books.id) }}" {% if simple==false %}data-toggle="modal" data-target="#bookDetailsModal" data-remote="false"{% endif %}>
<span class="img" title="{{ entry.Books.title }}"> <span class="img" title="{{ entry.Books.title }}">
@ -89,7 +89,7 @@
<div class="row display-flex"> <div class="row display-flex">
{% if entries[0] %} {% if entries[0] %}
{% for entry in entries %} {% for entry in entries %}
<div class="col-sm-3 col-lg-2 col-xs-6 book" id="books"> <div class="col-sm-3 col-lg-2 col-xs-6 book session" id="books">
<div class="cover"> <div class="cover">
<a href="{{ url_for('web.show_book', book_id=entry.Books.id) }}" {% if simple==false %}data-toggle="modal" data-target="#bookDetailsModal" data-remote="false"{% endif %}> <a href="{{ url_for('web.show_book', book_id=entry.Books.id) }}" {% if simple==false %}data-toggle="modal" data-target="#bookDetailsModal" data-remote="false"{% endif %}>
<span class="img" title="{{ entry.Books.title }}"> <span class="img" title="{{ entry.Books.title }}">

View File

@ -41,7 +41,7 @@
<div class="row display-flex"> <div class="row display-flex">
{% for entry in entries %} {% for entry in entries %}
<div class="col-sm-3 col-lg-2 col-xs-6 book"> <div class="col-sm-3 col-lg-2 col-xs-6 book session">
<div class="cover"> <div class="cover">
{% if entry.Books.has_cover is defined %} {% if entry.Books.has_cover is defined %}
<a href="{{ url_for('web.show_book', book_id=entry.Books.id) }}" {% if simple==false %}data-toggle="modal" data-target="#bookDetailsModal" data-remote="false"{% endif %}> <a href="{{ url_for('web.show_book', book_id=entry.Books.id) }}" {% if simple==false %}data-toggle="modal" data-target="#bookDetailsModal" data-remote="false"{% endif %}>

View File

@ -31,7 +31,7 @@
{% endif %} {% endif %}
<div class="row display-flex"> <div class="row display-flex">
{% for entry in entries %} {% for entry in entries %}
<div class="col-sm-3 col-lg-2 col-xs-6 book"> <div class="col-sm-3 col-lg-2 col-xs-6 book session">
<div class="cover"> <div class="cover">
<a href="{{ url_for('web.show_book', book_id=entry.Books.id) }}" {% if simple==false %}data-toggle="modal" data-target="#bookDetailsModal" data-remote="false"{% endif %}> <a href="{{ url_for('web.show_book', book_id=entry.Books.id) }}" {% if simple==false %}data-toggle="modal" data-target="#bookDetailsModal" data-remote="false"{% endif %}>
<span class="img" title="{{entry.Books.title}}" > <span class="img" title="{{entry.Books.title}}" >

View File

@ -50,7 +50,7 @@ from .helper import check_valid_domain, check_email, check_username, \
send_registration_mail, check_send_to_ereader, check_read_formats, tags_filters, reset_password, valid_email, \ send_registration_mail, check_send_to_ereader, check_read_formats, tags_filters, reset_password, valid_email, \
edit_book_read_status, valid_password edit_book_read_status, valid_password
from .pagination import Pagination from .pagination import Pagination
from .redirect import redirect_back from .redirect import get_redirect_location
from .babel import get_available_locale from .babel import get_available_locale
from .usermanagement import login_required_if_no_ano from .usermanagement import login_required_if_no_ano
from .kobo_sync_status import remove_synced_book from .kobo_sync_status import remove_synced_book
@ -1342,7 +1342,7 @@ def handle_login_user(user, remember, message, category):
ub.store_user_session() ub.store_user_session()
flash(message, category=category) flash(message, category=category)
[limiter.limiter.storage.clear(k.key) for k in limiter.current_limits] [limiter.limiter.storage.clear(k.key) for k in limiter.current_limits]
return redirect_back("web.index") return redirect(get_redirect_location(request.form.get('next', None), "web.index"))
def render_login(username="", password=""): def render_login(username="", password=""):