From d0d9985370be37506bdc4782d880885060a0528b Mon Sep 17 00:00:00 2001 From: James Armstong Date: Thu, 1 Aug 2024 19:19:14 -0700 Subject: [PATCH 01/19] Add bulk delete button --- cps/editbooks.py | 22 ++++++++++++++++++ cps/static/js/table.js | 44 +++++++++++++++++++++++++++++++++++ cps/templates/book_table.html | 20 ++++++++++++++++ 3 files changed, 86 insertions(+) diff --git a/cps/editbooks.py b/cps/editbooks.py index 5f58cf819..f976d7eaa 100644 --- a/cps/editbooks.py +++ b/cps/editbooks.py @@ -488,6 +488,28 @@ def simulate_merge_list_book(): return json.dumps({'to': to_book, 'from': from_book}) return "" +@editbook.route("/ajax/simulatedeleteselectedbooks", methods=['POST']) +@user_login_required +@edit_required +def simulate_delete_selected_books(): + vals = request.get_json().get('selections') + books = [] + if vals: + for book_id in vals: + books.append(calibre_db.get_book(book_id).title) + return json.dumps({'books': books}) + 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/mergebooks", methods=['POST']) @user_login_required diff --git a/cps/static/js/table.js b/cps/static/js/table.js index 36361c3ca..1686e5349 100644 --- a/cps/static/js/table.js +++ b/cps/static/js/table.js @@ -81,6 +81,14 @@ $(function() { $("#merge_books").addClass("disabled"); $("#merge_books").attr("aria-disabled", true); } + if (selections.length >= 1) { + $("#delete_selected_books").removeClass("disabled"); + $("#delete_selected_books").attr("aria-disabled", false); + } else { + $("#delete_selected_books").addClass("disabled"); + $("#delete_selected_books").attr("aria-disabled", true); + + } if (selections.length < 1) { $("#delete_selection").addClass("disabled"); $("#delete_selection").attr("aria-disabled", true); @@ -135,6 +143,42 @@ $(function() { }); }); + $("#delete_selected_books").click(function(event) { + if ($(this).hasClass("disabled")) { + event.stopPropagation() + } else { + $('#delete_selected_modal').modal("show"); + } + $.ajax({ + method:"post", + contentType: "application/json; charset=utf-8", + dataType: "json", + url: window.location.pathname + "/../ajax/simulatedeleteselectedbooks", + data: JSON.stringify({"selections":selections}), + success: function success(booTitles) { + $('#selected-books').empty(); + $.each(booTitles.books, function(i, item) { + $("- " + item + "

").appendTo("#selected-books"); + }); + + } + }); + }); + + $("#delete_selected_confirm").click(function(event) { + $.ajax({ + method:"post", + contentType: "application/json; charset=utf-8", + dataType: "json", + url: window.location.pathname + "/../ajax/deleteselectedbooks", + data: JSON.stringify({"selections":selections}), + success: function success(booTitles) { + $("#books-table").bootstrapTable("refresh"); + $("#books-table").bootstrapTable("uncheckAll"); + } + }); + }); + $("#table_xchange").click(function() { $.ajax({ method:"post", diff --git a/cps/templates/book_table.html b/cps/templates/book_table.html index 16836b7b2..12c524842 100644 --- a/cps/templates/book_table.html +++ b/cps/templates/book_table.html @@ -35,6 +35,7 @@

{{_(title)}}

{{_('Merge selected books')}}
+
{{_('Delete selected books')}}
{{_('Remove Selections')}}
@@ -129,6 +130,25 @@

{{_(title)}}

+ + + + + + -{% endif %} + +{% endif %} {% endblock %} + {% block js %} From ca9fc7454121f7572666995af97a6f6d7f8df132 Mon Sep 17 00:00:00 2001 From: James Armstrong <32995055+jmarmstrong1207@users.noreply.github.com> Date: Fri, 2 Aug 2024 00:40:47 -0700 Subject: [PATCH 03/19] typo fix --- cps/templates/book_table.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cps/templates/book_table.html b/cps/templates/book_table.html index 9800cb06f..388323b12 100644 --- a/cps/templates/book_table.html +++ b/cps/templates/book_table.html @@ -186,7 +186,7 @@

{{_(title)}}

From 34fec0ed97a36011076ceab3db3a16f78fb30406 Mon Sep 17 00:00:00 2001 From: James Armstrong Date: Fri, 2 Aug 2024 10:19:25 -0700 Subject: [PATCH 04/19] Move buttons into table --- cps/templates/book_table.html | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/cps/templates/book_table.html b/cps/templates/book_table.html index 388323b12..61bdced1f 100644 --- a/cps/templates/book_table.html +++ b/cps/templates/book_table.html @@ -19,6 +19,12 @@ {% if sort %}data-sortable="true" {% endif %} data-visible="{{visiblility.get(parameter)}}" data-formatter="bookCheckboxFormatter"> + {% if parameter == "is_archived"%} +
{{_('Archive selected books')}}
+
+
{{_('Unarchive selected books')}}
+
+ {% endif %} {{show_text}} {%- endmacro %} @@ -35,10 +41,7 @@

{{_(title)}}

{{_('Merge selected books')}}
-
{{_('Delete selected books')}}
-
{{_('Archive selected books')}}
-
{{_('Unarchive selected books')}}
-
{{_('Remove Selections')}}
+
{{_('Clear selections')}}
{{_('Exchange author and title')}}
@@ -100,7 +103,7 @@

{{_(title)}}

{% endif %} {% endfor %} {% if current_user.role_delete_books() and current_user.role_edit()%} - {{_('Delete')}} +
{{_('Delete selected books')}}

{{_('Delete')}} {% endif %} From 4ed0633edfd4a5cbbc355756e3e1da8084a7dd68 Mon Sep 17 00:00:00 2001 From: James Armstrong Date: Fri, 2 Aug 2024 14:57:20 -0700 Subject: [PATCH 05/19] Add bulk read/unread buttons; Fix buttons not working when moved into table --- cps/editbooks.py | 21 ++++++++ cps/static/js/table.js | 96 ++++++++++++++++++++++++++++++++--- cps/templates/book_table.html | 84 ++++++++++++++++++++++++++---- 3 files changed, 184 insertions(+), 17 deletions(-) diff --git a/cps/editbooks.py b/cps/editbooks.py index 230b6ec5e..45cec33fe 100644 --- a/cps/editbooks.py +++ b/cps/editbooks.py @@ -525,6 +525,27 @@ def delete_selected_books(): return json.dumps({'success': True}) 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']) @user_login_required @edit_required diff --git a/cps/static/js/table.js b/cps/static/js/table.js index 73ebc2f13..24a1c6854 100644 --- a/cps/static/js/table.js +++ b/cps/static/js/table.js @@ -90,6 +90,12 @@ $(function() { $("#unarchive_selected_books").removeClass("disabled"); $("#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 { $("#delete_selected_books").addClass("disabled"); $("#delete_selected_books").attr("aria-disabled", true); @@ -99,6 +105,12 @@ $(function() { $("#unarchive_selected_books").addClass("disabled"); $("#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) { $("#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")) { event.stopPropagation() } else { @@ -176,7 +188,7 @@ $(function() { }); }); - $("#archive_selected_confirm").click(function(event) { + $(document).on('click', '#archive_selected_confirm', function(event) { $.ajax({ method:"post", 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")) { event.stopPropagation() } else { @@ -212,7 +224,7 @@ $(function() { }); }); - $("#unarchive_selected_confirm").click(function(event) { + $(document).on('click', '#unarchive_selected_confirm', function(event) { $.ajax({ method:"post", 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")) { event.stopPropagation() } else { @@ -248,7 +260,7 @@ $(function() { }); }); - $("#delete_selected_confirm").click(function(event) { + $(document).on('click', '#delete_selected_confirm', function(event) { $.ajax({ method:"post", 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) { + $("- " + item + "

").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) { + $("- " + item + "

").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() { $.ajax({ method:"post", diff --git a/cps/templates/book_table.html b/cps/templates/book_table.html index 61bdced1f..396a812d7 100644 --- a/cps/templates/book_table.html +++ b/cps/templates/book_table.html @@ -15,14 +15,26 @@ {%- endmacro %} {% macro book_checkbox_row(parameter, show_text, sort) -%} - - {% if parameter == "is_archived"%} -
{{_('Archive selected books')}}
+ {% if parameter == "is_archived" %} +
+ {{_('Archive selected books')}} +
+
+
+ {{_('Unarchive selected books')}} +
+
+ {% elif parameter == "read_status" %} +
+ {{_('Mark selected books as read')}} +

-
{{_('Unarchive selected books')}}
+
+ {{_('Mark selected books as unread')}}

{% endif %} {{show_text}} @@ -40,8 +52,12 @@

{{_(title)}}

-
{{_('Merge selected books')}}
-
{{_('Clear selections')}}
+
+ {{_('Merge selected books')}} +
+
+ {{_('Clear selections')}} +
{{_('Exchange author and title')}}
@@ -103,7 +119,13 @@

{{_(title)}}

{% endif %} {% endfor %} {% if current_user.role_delete_books() and current_user.role_edit()%} -
{{_('Delete selected books')}}

{{_('Delete')}} + +
+ {{_('Delete selected books')}} +
+
+ {{_('Delete')}} + {% endif %} @@ -130,7 +152,7 @@

{{_(title)}}

@@ -149,7 +171,7 @@

{{_(title)}}

@@ -169,7 +191,7 @@

{{_(title)}}

@@ -189,13 +211,53 @@

{{_(title)}}

+ + + + {% endif %} {% endblock %} From ecda717bed2406ef0d1c864f19a0eaf7313fee95 Mon Sep 17 00:00:00 2001 From: James Armstong Date: Fri, 2 Aug 2024 01:11:58 -0700 Subject: [PATCH 06/19] Add bulk metadata edit button --- cps/static/js/table.js | 68 +++++++++++++++++++++++++++++++++++ cps/templates/book_table.html | 58 ++++++++++++++++++++++++++++++ 2 files changed, 126 insertions(+) diff --git a/cps/static/js/table.js b/cps/static/js/table.js index 24a1c6854..2cdd16dbd 100644 --- a/cps/static/js/table.js +++ b/cps/static/js/table.js @@ -96,6 +96,9 @@ $(function() { $("#unread_selected_books").removeClass("disabled"); $("#unread_selected_books").attr("aria-disabled", false); + + $("#edit_selected_books").removeClass("disabled"); + $("#edit_selected_books").attr("aria-disabled", false); } else { $("#delete_selected_books").addClass("disabled"); $("#delete_selected_books").attr("aria-disabled", true); @@ -111,6 +114,9 @@ $(function() { $("#unread_selected_books").addClass("disabled"); $("#unread_selected_books").attr("aria-disabled", true); + + $("#edit_selected_books").addClass("disabled"); + $("#edit_selected_books").attr("aria-disabled", true); } if (selections.length < 1) { $("#delete_selection").addClass("disabled"); @@ -166,6 +172,68 @@ $(function() { }); }); + $("#edit_selected_books").click(function(event) { + if ($(this).hasClass("disabled")) { + event.stopPropagation() + } else { + $('#edit_selected_modal').modal("show"); + } + }); + + $("#edit_selected_confirm").click(function(event) { + let title = $("#title_input").val() + let title_sort = $("#title_sort_input").val() + let author_sort = $("#author_sort_input").val() + let authors = $("#authors_input").val() + let categories = $("#categories_input").val() + let series = $("#series_input").val() + let languages = $("#languages_input").val() + let publishers = $("#publishers_input").val() + let comments = $("#comments_input").val().toString() + + function loopThrough(param, value) + { + selections.forEach((book_id) => { + $.ajax({ + method: "post", + url: getPath() + "/ajax/editbooks/" + param, + data: { pk: book_id, value: value }, + error: function (data) { + handleListServerResponse([ + { type: "danger", message: data.responseText }, + ]); + }, + success: function success(booTitles) { + $("#books-table").bootstrapTable("refresh"); + $("#books-table").bootstrapTable("uncheckAll"); + + $("#title_input").value = ""; + $("#title_sort_input").value = ""; + $("#author_sort_input").value = ""; + $("#authors_input").value = ""; + $("#categories_input").value = ""; + $("#series_input").value = ""; + $("#languages_input").value = ""; + $("#publishers_input").value = ""; + $("#comments_input").value = ""; + + handleListServerResponse; + }, + }); + }) + } + + if (title) loopThrough('title', title); + if (title_sort) loopThrough('title_sort', title_sort); + if (author_sort) loopThrough('author_sort', author_sort); + if (authors) loopThrough('authors', authors); + if (categories) loopThrough('tags', categories); + if (series) loopThrough('series', series); + if (languages) loopThrough('languages', languages); + if (publishers) loopThrough('publishers', publishers); + if (comments) loopThrough('comments', comments); + }); + $(document).on('click', '#archive_selected_books', function(event) { if ($(this).hasClass("disabled")) { event.stopPropagation() diff --git a/cps/templates/book_table.html b/cps/templates/book_table.html index 396a812d7..e93fbd573 100644 --- a/cps/templates/book_table.html +++ b/cps/templates/book_table.html @@ -58,6 +58,9 @@

{{_(title)}}

{{_('Clear selections')}}
+
+ {{_('Edit selected books')}} +
{{_('Exchange author and title')}}
@@ -258,6 +261,61 @@

{{_(title)}}

+ + + {% endif %} {% endblock %} From ab3d4e4f212f463016a9d7ec9ff57af256a049b0 Mon Sep 17 00:00:00 2001 From: James Armstong Date: Fri, 2 Aug 2024 20:10:43 -0700 Subject: [PATCH 07/19] Fix emptying metadata form after submit --- cps/static/js/table.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/cps/static/js/table.js b/cps/static/js/table.js index 2cdd16dbd..24e637672 100644 --- a/cps/static/js/table.js +++ b/cps/static/js/table.js @@ -207,15 +207,15 @@ $(function() { $("#books-table").bootstrapTable("refresh"); $("#books-table").bootstrapTable("uncheckAll"); - $("#title_input").value = ""; - $("#title_sort_input").value = ""; - $("#author_sort_input").value = ""; - $("#authors_input").value = ""; - $("#categories_input").value = ""; - $("#series_input").value = ""; - $("#languages_input").value = ""; - $("#publishers_input").value = ""; - $("#comments_input").value = ""; + $("#title_input").val(""); + $("#title_sort_input").val(""); + $("#author_sort_input").val(""); + $("#authors_input").val(""); + $("#categories_input").val(""); + $("#series_input").val(""); + $("#languages_input").val(""); + $("#publishers_input").val(""); + $("#comments_input").val(""); handleListServerResponse; }, From 9def9109249bf59af9c6c5012a5c2367cf200811 Mon Sep 17 00:00:00 2001 From: James Armstong Date: Fri, 2 Aug 2024 21:16:34 -0700 Subject: [PATCH 08/19] switch title_sort to sort in api edit request --- cps/static/js/table.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cps/static/js/table.js b/cps/static/js/table.js index 24e637672..e556d85c3 100644 --- a/cps/static/js/table.js +++ b/cps/static/js/table.js @@ -224,7 +224,7 @@ $(function() { } if (title) loopThrough('title', title); - if (title_sort) loopThrough('title_sort', title_sort); + if (title_sort) loopThrough('sort', title_sort); if (author_sort) loopThrough('author_sort', author_sort); if (authors) loopThrough('authors', authors); if (categories) loopThrough('tags', categories); From 96fb2c143960c6ec238868002082adc396ea9377 Mon Sep 17 00:00:00 2001 From: James Armstong Date: Fri, 2 Aug 2024 21:39:01 -0700 Subject: [PATCH 09/19] Auto disable author/title sort input in metadata edit form --- cps/static/js/table.js | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/cps/static/js/table.js b/cps/static/js/table.js index e556d85c3..269549a41 100644 --- a/cps/static/js/table.js +++ b/cps/static/js/table.js @@ -130,7 +130,29 @@ $(function() { $("#table_xchange").attr("aria-disabled", false); } + }); + + // Small block to initialize the state of the author/title sort inputs in metadata form + { + let checkA = $('#autoupdate_authorsort').prop('checked'); + $('#author_sort_input').prop('disabled', checkA); + let checkT = $('#autoupdate_titlesort').prop('checked'); + $('#title_sort_input').prop('disabled', checkT); + } + + // Disable/enable author and title sort input in respect to auto-update title/author sort being checked on or not + $("#autoupdate_authorsort").on('change', function(event) { + let checkA = $('#autoupdate_authorsort').prop('checked'); + $('#author_sort_input').prop('disabled', checkA); + }) + + $("#autoupdate_titlesort").on('change', function(event) { + let checkT = $('#autoupdate_titlesort').prop('checked'); + $('#title_sort_input').prop('disabled', checkT); + }) + ///// + $("#delete_selection").click(function() { $("#books-table").bootstrapTable("uncheckAll"); }); From a335dd75a182bdca58f2b529c00b8870d6a04e03 Mon Sep 17 00:00:00 2001 From: James Armstong Date: Fri, 2 Aug 2024 23:29:34 -0700 Subject: [PATCH 10/19] Make edit metadata pass all data in a single REST call. Modularize editbooks --- cps/editbooks.py | 71 +++++++++++++++++++++++++++++++++++ cps/static/js/table.js | 84 +++++++++++++++++------------------------- 2 files changed, 105 insertions(+), 50 deletions(-) diff --git a/cps/editbooks.py b/cps/editbooks.py index 45cec33fe..3bb74b254 100644 --- a/cps/editbooks.py +++ b/cps/editbooks.py @@ -350,6 +350,77 @@ def table_get_custom_enum(c_id): @edit_required def edit_list_book(param): vals = request.form.to_dict() + return edit_book_param(param, vals) + +@editbook.route("/ajax/editselectedbooks", methods=['POST']) +@login_required_if_no_ano +@edit_required +def edit_selected_books(): + d = request.get_json() + selections = d.get('selections') + title = d.get('title') + title_sort = d.get('title_sort') + author_sort = d.get('author_sortj') + authors = d.get('authors') + categories = d.get('categories') + series = d.get('series') + languages = d.get('languages') + publishers = d.get('publishers') + comments = d.get('comments') + + + + if len(selections) != 0: + for book_id in selections: + vals = { + "pk": book_id, + "value": None, + } + 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 "" + +# Separated from /editbooks so that /editselectedbooks can also use this +# +# param: the property of the book to be changed +# vals - JSON Object: +# { +# 'pk': "the book id", +# 'value': "changes value of param to what's passed here" +# 'checkA': "Optional. Used to check if autosort author is enabled. Assumed as true if not passed" +# 'checkT': "Optional. Used to check if autotitle author is enabled. Assumed as true if not passed" +# } +# +@login_required_if_no_ano +@edit_required +def edit_book_param(param, vals): book = calibre_db.get_book(vals['pk']) sort_param = "" ret = "" diff --git a/cps/static/js/table.js b/cps/static/js/table.js index 269549a41..acc8ccfb1 100644 --- a/cps/static/js/table.js +++ b/cps/static/js/table.js @@ -203,57 +203,41 @@ $(function() { }); $("#edit_selected_confirm").click(function(event) { - let title = $("#title_input").val() - let title_sort = $("#title_sort_input").val() - let author_sort = $("#author_sort_input").val() - let authors = $("#authors_input").val() - let categories = $("#categories_input").val() - let series = $("#series_input").val() - let languages = $("#languages_input").val() - let publishers = $("#publishers_input").val() - let comments = $("#comments_input").val().toString() - - function loopThrough(param, value) - { - selections.forEach((book_id) => { - $.ajax({ - method: "post", - url: getPath() + "/ajax/editbooks/" + param, - data: { pk: book_id, value: value }, - error: function (data) { - handleListServerResponse([ - { type: "danger", message: data.responseText }, - ]); - }, - success: function success(booTitles) { - $("#books-table").bootstrapTable("refresh"); - $("#books-table").bootstrapTable("uncheckAll"); - - $("#title_input").val(""); - $("#title_sort_input").val(""); - $("#author_sort_input").val(""); - $("#authors_input").val(""); - $("#categories_input").val(""); - $("#series_input").val(""); - $("#languages_input").val(""); - $("#publishers_input").val(""); - $("#comments_input").val(""); - - handleListServerResponse; - }, - }); - }) - } - if (title) loopThrough('title', title); - if (title_sort) loopThrough('sort', title_sort); - if (author_sort) loopThrough('author_sort', author_sort); - if (authors) loopThrough('authors', authors); - if (categories) loopThrough('tags', categories); - if (series) loopThrough('series', series); - if (languages) loopThrough('languages', languages); - if (publishers) loopThrough('publishers', publishers); - if (comments) loopThrough('comments', comments); + $.ajax({ + method:"post", + contentType: "application/json; charset=utf-8", + dataType: "json", + url: window.location.pathname + "/../ajax/editselectedbooks", + data: JSON.stringify({ + "selections": selections, + "title": $("#title_input").val(), + "title_sort": $("#title_sort_input").val(), + "author_sort": $("#author_sort_input").val(), + "authors": $("#authors_input").val(), + "categories": $("#categories_input").val(), + "series": $("#series_input").val(), + "languages": $("#languages_input").val(), + "publishers": $("#publishers_input").val(), + "comments": $("#comments_input").val().toString(), + }), + success: function success(booTitles) { + $("#books-table").bootstrapTable("refresh"); + $("#books-table").bootstrapTable("uncheckAll"); + + $("#title_input").val(""); + $("#title_sort_input").val(""); + $("#author_sort_input").val(""); + $("#authors_input").val(""); + $("#categories_input").val(""); + $("#series_input").val(""); + $("#languages_input").val(""); + $("#publishers_input").val(""); + $("#comments_input").val(""); + + handleListServerResponse; + } + }); }); $(document).on('click', '#archive_selected_books', function(event) { From bee6a3593dcf603eb614c6c73e00a7dd3078b58f Mon Sep 17 00:00:00 2001 From: James Armstong Date: Fri, 2 Aug 2024 23:31:04 -0700 Subject: [PATCH 11/19] remove spacing --- cps/editbooks.py | 3 --- cps/static/js/table.js | 1 - 2 files changed, 4 deletions(-) diff --git a/cps/editbooks.py b/cps/editbooks.py index 3bb74b254..938b19f3d 100644 --- a/cps/editbooks.py +++ b/cps/editbooks.py @@ -368,8 +368,6 @@ def edit_selected_books(): publishers = d.get('publishers') comments = d.get('comments') - - if len(selections) != 0: for book_id in selections: vals = { @@ -403,7 +401,6 @@ def edit_selected_books(): if comments: vals['value'] = comments edit_book_param('comments', vals) - return json.dumps({'success': True}) return "" diff --git a/cps/static/js/table.js b/cps/static/js/table.js index acc8ccfb1..b80fccc71 100644 --- a/cps/static/js/table.js +++ b/cps/static/js/table.js @@ -203,7 +203,6 @@ $(function() { }); $("#edit_selected_confirm").click(function(event) { - $.ajax({ method:"post", contentType: "application/json; charset=utf-8", From e31763d38879e5c8c0a0a67b247472a7c64e0981 Mon Sep 17 00:00:00 2001 From: James Armstong Date: Sat, 3 Aug 2024 11:27:58 -0700 Subject: [PATCH 12/19] Fix kobo sync status marking as archived even though state = false --- cps/kobo_sync_status.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cps/kobo_sync_status.py b/cps/kobo_sync_status.py index ef732aaad..8c1ca7c5f 100644 --- a/cps/kobo_sync_status.py +++ b/cps/kobo_sync_status.py @@ -54,10 +54,10 @@ 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: + if not archived_book and state == True: archived_book = ub.ArchivedBook(user_id=current_user.id, book_id=book_id) - archived_book.is_archived = state if state else not archived_book.is_archived + archived_book.is_archived = state if state != None else not archived_book.is_archived archived_book.last_modified = datetime.datetime.utcnow() # toDo. Check utc timestamp ub.session.merge(archived_book) From 2ae80d3007d4d42147ce02b23bafe26aa3ef7d1d Mon Sep 17 00:00:00 2001 From: James Armstong Date: Sat, 3 Aug 2024 11:40:11 -0700 Subject: [PATCH 13/19] Fix book_read_status marking as read even though read_status is passed as false --- cps/helper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cps/helper.py b/cps/helper.py index 004e1b0e0..bc982cfe7 100644 --- a/cps/helper.py +++ b/cps/helper.py @@ -313,7 +313,7 @@ def edit_book_read_status(book_id, read_status=None): else: book.read_status = ub.ReadBook.STATUS_FINISHED else: - book.read_status = ub.ReadBook.STATUS_FINISHED if read_status else ub.ReadBook.STATUS_UNREAD + 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 From 2afce66486c91f818adea79a7cadda305ddc5e28 Mon Sep 17 00:00:00 2001 From: James Armstong Date: Sat, 3 Aug 2024 11:50:28 -0700 Subject: [PATCH 14/19] Fix change_archived so state=none is a toggle. Fixes /togglearchived endpoint --- cps/kobo_sync_status.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cps/kobo_sync_status.py b/cps/kobo_sync_status.py index 8c1ca7c5f..ee9f0779c 100644 --- a/cps/kobo_sync_status.py +++ b/cps/kobo_sync_status.py @@ -54,7 +54,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: + 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 From de3f883992dc949a67c3053b2e555699b1da2476 Mon Sep 17 00:00:00 2001 From: James Armstong Date: Sat, 3 Aug 2024 11:52:28 -0700 Subject: [PATCH 15/19] Add change_archived_books() description --- cps/kobo_sync_status.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cps/kobo_sync_status.py b/cps/kobo_sync_status.py index ee9f0779c..94f1d073a 100644 --- a/cps/kobo_sync_status.py +++ b/cps/kobo_sync_status.py @@ -51,6 +51,8 @@ def remove_synced_book(book_id, all=False, session=None): ub.session_commit(_session=session) +# If state == none, it will toggle the archive state of the passed book_id. +# state = true archives it, state = false unarchives it 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() From fe78222142448d58d55737329d164e3190650c50 Mon Sep 17 00:00:00 2001 From: James Armstrong Date: Mon, 5 Aug 2024 10:43:51 -0700 Subject: [PATCH 16/19] Add shift-click to select multiple books at once --- cps/static/js/table.js | 1 + 1 file changed, 1 insertion(+) diff --git a/cps/static/js/table.js b/cps/static/js/table.js index b80fccc71..6acf63084 100644 --- a/cps/static/js/table.js +++ b/cps/static/js/table.js @@ -484,6 +484,7 @@ $(function() { searchOnEnterKey: true, checkboxHeader: false, maintainMetaData: true, + multipleSelectRow: true, responseHandler: responseHandler, columns: column, formatNoMatches: function () { From 31380f2af9e85ed04904f490aacbc621c2135f0c Mon Sep 17 00:00:00 2001 From: James Armstrong <32995055+jmarmstrong1207@users.noreply.github.com> Date: Tue, 17 Sep 2024 03:19:03 -0700 Subject: [PATCH 17/19] fix typo --- cps/editbooks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cps/editbooks.py b/cps/editbooks.py index 84fa754ee..bc76ec748 100644 --- a/cps/editbooks.py +++ b/cps/editbooks.py @@ -224,7 +224,7 @@ def edit_selected_books(): selections = d.get('selections') title = d.get('title') title_sort = d.get('title_sort') - author_sort = d.get('author_sortj') + author_sort = d.get('author_sort') authors = d.get('authors') categories = d.get('categories') series = d.get('series') From 338441f1165fe71ef9f5983d040b90775bc62f10 Mon Sep 17 00:00:00 2001 From: James Armstrong Date: Tue, 17 Sep 2024 12:26:35 -0700 Subject: [PATCH 18/19] fix author sort not updating when bulk editing --- cps/editbooks.py | 2 ++ cps/static/js/table.js | 1 + 2 files changed, 3 insertions(+) diff --git a/cps/editbooks.py b/cps/editbooks.py index bc76ec748..761c61c75 100644 --- a/cps/editbooks.py +++ b/cps/editbooks.py @@ -231,12 +231,14 @@ def edit_selected_books(): languages = d.get('languages') publishers = d.get('publishers') 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 diff --git a/cps/static/js/table.js b/cps/static/js/table.js index 6acf63084..15c4f958b 100644 --- a/cps/static/js/table.js +++ b/cps/static/js/table.js @@ -219,6 +219,7 @@ $(function() { "languages": $("#languages_input").val(), "publishers": $("#publishers_input").val(), "comments": $("#comments_input").val().toString(), + "checkA": $('#autoupdate_authorsort').prop('checked').toString() }), success: function success(booTitles) { $("#books-table").bootstrapTable("refresh"); From 54d9d33e538e6f625b7e191046ef1197897092a2 Mon Sep 17 00:00:00 2001 From: James Armstrong Date: Mon, 23 Sep 2024 09:42:04 -0700 Subject: [PATCH 19/19] Revert "Add shift-click to select multiple books at once" This reverts commit fe78222142448d58d55737329d164e3190650c50. --- cps/static/js/table.js | 1 - 1 file changed, 1 deletion(-) diff --git a/cps/static/js/table.js b/cps/static/js/table.js index 15c4f958b..58ed5999c 100644 --- a/cps/static/js/table.js +++ b/cps/static/js/table.js @@ -485,7 +485,6 @@ $(function() { searchOnEnterKey: true, checkboxHeader: false, maintainMetaData: true, - multipleSelectRow: true, responseHandler: responseHandler, columns: column, formatNoMatches: function () {