mirror of
https://github.com/janeczku/calibre-web
synced 2025-10-24 03:47:40 +00:00
Add bulk read/unread buttons; Fix buttons not working when moved into table
This commit is contained in:
@@ -525,6 +525,27 @@ def delete_selected_books():
|
|||||||
return json.dumps({'success': True})
|
return json.dumps({'success': True})
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
@editbook.route("/ajax/readselectedbooks", methods=['POST'])
|
||||||
|
@user_login_required
|
||||||
|
@edit_required
|
||||||
|
def read_selected_books():
|
||||||
|
vals = request.get_json().get('selections')
|
||||||
|
markAsRead = request.get_json().get('markAsRead')
|
||||||
|
if vals:
|
||||||
|
try:
|
||||||
|
for book_id in vals:
|
||||||
|
ret = helper.edit_book_read_status(book_id, markAsRead)
|
||||||
|
|
||||||
|
except (OperationalError, IntegrityError, StaleDataError) as e:
|
||||||
|
calibre_db.session.rollback()
|
||||||
|
log.error_or_exception("Database error: {}".format(e))
|
||||||
|
ret = Response(json.dumps({'success': False,
|
||||||
|
'msg': 'Database error: {}'.format(e.orig if hasattr(e, "orig") else e)}),
|
||||||
|
mimetype='application/json')
|
||||||
|
|
||||||
|
return json.dumps({'success': True})
|
||||||
|
return ""
|
||||||
|
|
||||||
@editbook.route("/ajax/mergebooks", methods=['POST'])
|
@editbook.route("/ajax/mergebooks", methods=['POST'])
|
||||||
@user_login_required
|
@user_login_required
|
||||||
@edit_required
|
@edit_required
|
||||||
|
|||||||
@@ -90,6 +90,12 @@ $(function() {
|
|||||||
|
|
||||||
$("#unarchive_selected_books").removeClass("disabled");
|
$("#unarchive_selected_books").removeClass("disabled");
|
||||||
$("#unarchive_selected_books").attr("aria-disabled", false);
|
$("#unarchive_selected_books").attr("aria-disabled", false);
|
||||||
|
|
||||||
|
$("#read_selected_books").removeClass("disabled");
|
||||||
|
$("#read_selected_books").attr("aria-disabled", false);
|
||||||
|
|
||||||
|
$("#unread_selected_books").removeClass("disabled");
|
||||||
|
$("#unread_selected_books").attr("aria-disabled", false);
|
||||||
} else {
|
} else {
|
||||||
$("#delete_selected_books").addClass("disabled");
|
$("#delete_selected_books").addClass("disabled");
|
||||||
$("#delete_selected_books").attr("aria-disabled", true);
|
$("#delete_selected_books").attr("aria-disabled", true);
|
||||||
@@ -99,6 +105,12 @@ $(function() {
|
|||||||
|
|
||||||
$("#unarchive_selected_books").addClass("disabled");
|
$("#unarchive_selected_books").addClass("disabled");
|
||||||
$("#unarchive_selected_books").attr("aria-disabled", true);
|
$("#unarchive_selected_books").attr("aria-disabled", true);
|
||||||
|
|
||||||
|
$("#read_selected_books").addClass("disabled");
|
||||||
|
$("#read_selected_books").attr("aria-disabled", true);
|
||||||
|
|
||||||
|
$("#unread_selected_books").addClass("disabled");
|
||||||
|
$("#unread_selected_books").attr("aria-disabled", true);
|
||||||
}
|
}
|
||||||
if (selections.length < 1) {
|
if (selections.length < 1) {
|
||||||
$("#delete_selection").addClass("disabled");
|
$("#delete_selection").addClass("disabled");
|
||||||
@@ -154,7 +166,7 @@ $(function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#archive_selected_books").click(function(event) {
|
$(document).on('click', '#archive_selected_books', function(event) {
|
||||||
if ($(this).hasClass("disabled")) {
|
if ($(this).hasClass("disabled")) {
|
||||||
event.stopPropagation()
|
event.stopPropagation()
|
||||||
} else {
|
} else {
|
||||||
@@ -176,7 +188,7 @@ $(function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#archive_selected_confirm").click(function(event) {
|
$(document).on('click', '#archive_selected_confirm', function(event) {
|
||||||
$.ajax({
|
$.ajax({
|
||||||
method:"post",
|
method:"post",
|
||||||
contentType: "application/json; charset=utf-8",
|
contentType: "application/json; charset=utf-8",
|
||||||
@@ -190,7 +202,7 @@ $(function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#unarchive_selected_books").click(function(event) {
|
$(document).on('click', '#unarchive_selected_books', function(event) {
|
||||||
if ($(this).hasClass("disabled")) {
|
if ($(this).hasClass("disabled")) {
|
||||||
event.stopPropagation()
|
event.stopPropagation()
|
||||||
} else {
|
} else {
|
||||||
@@ -212,7 +224,7 @@ $(function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#unarchive_selected_confirm").click(function(event) {
|
$(document).on('click', '#unarchive_selected_confirm', function(event) {
|
||||||
$.ajax({
|
$.ajax({
|
||||||
method:"post",
|
method:"post",
|
||||||
contentType: "application/json; charset=utf-8",
|
contentType: "application/json; charset=utf-8",
|
||||||
@@ -226,7 +238,7 @@ $(function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#delete_selected_books").click(function(event) {
|
$(document).on('click', '#delete_selected_books', function(event) {
|
||||||
if ($(this).hasClass("disabled")) {
|
if ($(this).hasClass("disabled")) {
|
||||||
event.stopPropagation()
|
event.stopPropagation()
|
||||||
} else {
|
} else {
|
||||||
@@ -248,7 +260,7 @@ $(function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#delete_selected_confirm").click(function(event) {
|
$(document).on('click', '#delete_selected_confirm', function(event) {
|
||||||
$.ajax({
|
$.ajax({
|
||||||
method:"post",
|
method:"post",
|
||||||
contentType: "application/json; charset=utf-8",
|
contentType: "application/json; charset=utf-8",
|
||||||
@@ -262,6 +274,78 @@ $(function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$(document).on('click', '#read_selected_books', function(event) {
|
||||||
|
if ($(this).hasClass("disabled")) {
|
||||||
|
event.stopPropagation()
|
||||||
|
} else {
|
||||||
|
$('#read_selected_modal').modal("show");
|
||||||
|
}
|
||||||
|
$.ajax({
|
||||||
|
method:"post",
|
||||||
|
contentType: "application/json; charset=utf-8",
|
||||||
|
dataType: "json",
|
||||||
|
url: window.location.pathname + "/../ajax/displayselectedbooks",
|
||||||
|
data: JSON.stringify({"selections":selections}),
|
||||||
|
success: function success(booTitles) {
|
||||||
|
$('#display-read-selected-books').empty();
|
||||||
|
$.each(booTitles.books, function(i, item) {
|
||||||
|
$("<span>- " + item + "</span><p></p>").appendTo("#display-read-selected-books");
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$(document).on('click', '#read_selected_confirm', function(event) {
|
||||||
|
$.ajax({
|
||||||
|
method:"post",
|
||||||
|
contentType: "application/json; charset=utf-8",
|
||||||
|
dataType: "json",
|
||||||
|
url: window.location.pathname + "/../ajax/readselectedbooks",
|
||||||
|
data: JSON.stringify({"selections":selections, "markAsRead": true}),
|
||||||
|
success: function success(booTitles) {
|
||||||
|
$("#books-table").bootstrapTable("refresh");
|
||||||
|
$("#books-table").bootstrapTable("uncheckAll");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$(document).on('click', '#unread_selected_books', function(event) {
|
||||||
|
if ($(this).hasClass("disabled")) {
|
||||||
|
event.stopPropagation()
|
||||||
|
} else {
|
||||||
|
$('#unread_selected_modal').modal("show");
|
||||||
|
}
|
||||||
|
$.ajax({
|
||||||
|
method:"post",
|
||||||
|
contentType: "application/json; charset=utf-8",
|
||||||
|
dataType: "json",
|
||||||
|
url: window.location.pathname + "/../ajax/displayselectedbooks",
|
||||||
|
data: JSON.stringify({"selections":selections}),
|
||||||
|
success: function success(booTitles) {
|
||||||
|
$('#display-unread-selected-books').empty();
|
||||||
|
$.each(booTitles.books, function(i, item) {
|
||||||
|
$("<span>- " + item + "</span><p></p>").appendTo("#display-unread-selected-books");
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$(document).on('click', '#unread_selected_confirm', function(event) {
|
||||||
|
$.ajax({
|
||||||
|
method:"post",
|
||||||
|
contentType: "application/json; charset=utf-8",
|
||||||
|
dataType: "json",
|
||||||
|
url: window.location.pathname + "/../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() {
|
$("#table_xchange").click(function() {
|
||||||
$.ajax({
|
$.ajax({
|
||||||
method:"post",
|
method:"post",
|
||||||
|
|||||||
@@ -15,14 +15,26 @@
|
|||||||
{%- endmacro %}
|
{%- endmacro %}
|
||||||
|
|
||||||
{% macro book_checkbox_row(parameter, show_text, sort) -%}
|
{% macro book_checkbox_row(parameter, show_text, sort) -%}
|
||||||
<th data-name="{{parameter}}" data-field="{{parameter}}"
|
<th data-name="{{parameter}}" data-field="{{parameter}}" data-switchable="false"
|
||||||
{% if sort %}data-sortable="true" {% endif %}
|
{% if sort %}data-sortable="true" {% endif %}
|
||||||
data-visible="{{visiblility.get(parameter)}}"
|
data-visible="{{visiblility.get(parameter)}}"
|
||||||
data-formatter="bookCheckboxFormatter">
|
data-formatter="bookCheckboxFormatter">
|
||||||
{% if parameter == "is_archived"%}
|
{% if parameter == "is_archived" %}
|
||||||
<div class="btn btn-default disabled" id="archive_selected_books" aria-disabled="true">{{_('Archive selected books')}}</div>
|
<div class="btn btn-default disabled" id="archive_selected_books" aria-disabled="true">
|
||||||
|
{{_('Archive selected books')}}
|
||||||
|
</div>
|
||||||
<br>
|
<br>
|
||||||
<div class="btn btn-default disabled" id="unarchive_selected_books" aria-disabled="true">{{_('Unarchive selected books')}}</div>
|
<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>
|
||||||
|
<br>
|
||||||
|
<div class="btn btn-default disabled" id="unread_selected_books" aria-disabled="true">
|
||||||
|
{{_('Mark selected books as unread')}}</div>
|
||||||
<br>
|
<br>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{{show_text}}
|
{{show_text}}
|
||||||
@@ -40,8 +52,12 @@
|
|||||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||||||
<div class="col-xs-12 col-sm-6">
|
<div class="col-xs-12 col-sm-6">
|
||||||
<div class="row form-group">
|
<div class="row form-group">
|
||||||
<div class="btn btn-default disabled" id="merge_books" aria-disabled="true">{{_('Merge selected books')}}</div>
|
<div class="btn btn-default disabled" id="merge_books" aria-disabled="true">
|
||||||
<div class="btn btn-default disabled" id="delete_selection" aria-disabled="true">{{_('Clear selections')}}</div>
|
{{_('Merge selected books')}}
|
||||||
|
</div>
|
||||||
|
<div class="btn btn-default disabled" id="delete_selection" aria-disabled="true">
|
||||||
|
{{_('Clear selections')}}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row form-group">
|
<div class="row form-group">
|
||||||
<div class="btn btn-default disabled" id="table_xchange" ><span class="glyphicon glyphicon-arrow-up"></span><span class="glyphicon glyphicon-arrow-down"></span>{{_('Exchange author and title')}}</div>
|
<div class="btn btn-default disabled" id="table_xchange" ><span class="glyphicon glyphicon-arrow-up"></span><span class="glyphicon glyphicon-arrow-down"></span>{{_('Exchange author and title')}}</div>
|
||||||
@@ -103,7 +119,13 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% if current_user.role_delete_books() and current_user.role_edit()%}
|
{% 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">{{_('Delete selected books')}}</div><br>{{_('Delete')}}</th>
|
<th data-align="right" data-formatter="EbookActions" data-switchable="false">
|
||||||
|
<div class="btn btn-default disabled" id="delete_selected_books" aria-disabled="true">
|
||||||
|
{{_('Delete selected books')}}
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
{{_('Delete')}}
|
||||||
|
</th>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
@@ -130,7 +152,7 @@
|
|||||||
<div class="text-left" id="merge_to"></div>
|
<div class="text-left" id="merge_to"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<input id="merge_confirm" type="button" class="btn btn-danger" value="{{_('Merge')}}" name="merge_confirm" id="merge_confirm" data-dismiss="modal">
|
<input id="merge_confirm" type="button" class="btn btn-danger" value="{{_('Merge')}}" name="merge_confirm" data-dismiss="modal">
|
||||||
<button id="merge_abort" type="button" class="btn btn-default" data-dismiss="modal">{{_('Cancel')}}</button>
|
<button id="merge_abort" type="button" class="btn btn-default" data-dismiss="modal">{{_('Cancel')}}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -149,7 +171,7 @@
|
|||||||
<p></p>
|
<p></p>
|
||||||
<div class="text-left" id="display-delete-selected-books"></div>
|
<div class="text-left" id="display-delete-selected-books"></div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<input id="delete_selected_confirm" type="button" class="btn btn-danger" value="{{_('Delete')}}" name="delete_selected_confirm" id="delete_selected_confirm" data-dismiss="modal">
|
<input id="delete_selected_confirm" type="button" class="btn btn-danger" value="{{_('Delete')}}" name="delete_selected_confirm" data-dismiss="modal">
|
||||||
<button id="delete_selected_abort" type="button" class="btn btn-default" data-dismiss="modal">{{_('Cancel')}}</button>
|
<button id="delete_selected_abort" type="button" class="btn btn-default" data-dismiss="modal">{{_('Cancel')}}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -169,7 +191,7 @@
|
|||||||
<p></p>
|
<p></p>
|
||||||
<div class="text-left" id="display-archive-selected-books"></div>
|
<div class="text-left" id="display-archive-selected-books"></div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<input id="archive_selected_confirm" type="button" class="btn btn-danger" value="{{_('Archive')}}" name="archive_selected_confirm" id="archive_selected_confirm" data-dismiss="modal">
|
<input id="archive_selected_confirm" type="button" class="btn btn-danger" value="{{_('Archive')}}" name="archive_selected_confirm" data-dismiss="modal">
|
||||||
<button id="archive_selected_abort" type="button" class="btn btn-default" data-dismiss="modal">{{_('Cancel')}}</button>
|
<button id="archive_selected_abort" type="button" class="btn btn-default" data-dismiss="modal">{{_('Cancel')}}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -189,13 +211,53 @@
|
|||||||
<p></p>
|
<p></p>
|
||||||
<div class="text-left" id="display-unarchive-selected-books"></div>
|
<div class="text-left" id="display-unarchive-selected-books"></div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<input id="unarchive_selected_confirm" type="button" class="btn btn-danger" value="{{_('Unarchive')}}" name="unarchive_selected_confirm" id="unarchive_selected_confirm" data-dismiss="modal">
|
<input id="unarchive_selected_confirm" type="button" class="btn btn-danger" value="{{_('Unarchive')}}" name="unarchive_selected_confirm" data-dismiss="modal">
|
||||||
<button id="unarchive_selected_abort" type="button" class="btn btn-default" data-dismiss="modal">{{_('Cancel')}}</button>
|
<button id="unarchive_selected_abort" type="button" class="btn btn-default" data-dismiss="modal">{{_('Cancel')}}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="modal fade" id="read_selected_modal" role="dialog" aria-labelledby="metaReadSelectedLabel">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header bg-danger text-center">
|
||||||
|
<span>{{_('Are you really sure?')}}</span>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<p></p>
|
||||||
|
<div class="text-left">{{_('The following books will be marked read:')}}</div>
|
||||||
|
<p></p>
|
||||||
|
<div class="text-left" id="display-read-selected-books"></div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<input id="read_selected_confirm" type="button" class="btn btn-danger" value="{{_('Mark as read')}}" name="read_selected_confirm" data-dismiss="modal">
|
||||||
|
<button id="read_selected_abort" type="button" class="btn btn-default" data-dismiss="modal">{{_('Cancel')}}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal fade" id="unread_selected_modal" role="dialog" aria-labelledby="metaReadSelectedLabel">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header bg-danger text-center">
|
||||||
|
<span>{{_('Are you really sure?')}}</span>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<p></p>
|
||||||
|
<div class="text-left">{{_('The following books will be marked unread:')}}</div>
|
||||||
|
<p></p>
|
||||||
|
<div class="text-left" id="display-unread-selected-books"></div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<input id="unread_selected_confirm" type="button" class="btn btn-danger" value="{{_('Mark as unread')}}" name="unread_selected_confirm" data-dismiss="modal">
|
||||||
|
<button id="read_selected_abort" type="button" class="btn btn-default" data-dismiss="modal">{{_('Cancel')}}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user