diff --git a/data/interfaces/bookstrap/index.html b/data/interfaces/bookstrap/index.html index f346c117..41d7809f 100644 --- a/data/interfaces/bookstrap/index.html +++ b/data/interfaces/bookstrap/index.html @@ -308,12 +308,12 @@ } }, callback: function (result) { - if (result) { document.getElementById("mark_authors").submit(); } + if (result) { submitFormAjax(); } } }); return false; } - else { document.getElementById("mark_authors").submit(); } + else { submitFormAjax(); } } function bookinfo(bookid) { @@ -339,5 +339,78 @@ }); }; + function submitFormAjax() { + var form = document.getElementById("mark_authors"); + var formData = new FormData(form); + + // Show loading modal + bootbox.dialog({ + message: '


Processing...
', + size: 'small', + backdrop: false + }); + + // Convert FormData to URLSearchParams for the AJAX call + var params = new URLSearchParams(); + var formDataEntries = formData.entries(); + for (var pair of formDataEntries) { + params.append(pair[0], pair[1]); + } + + // Make AJAX call to the new endpoint + fetch('mark_authors_ajax', { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + body: params + }) + .then(response => response.json()) + .then(data => { + // Close loading modal + bootbox.hideAll(); + + // Show result modal + var message = '
Summary:
' + data.summary + '
'; + if (data.passed > 0 || data.failed > 0) { + message += '
'; + message += '
Successful: ' + data.passed + '
'; + if (data.failed > 0) { + message += '
Failed: ' + data.failed + '
'; + } + message += '
'; + } + bootbox.dialog({ + title: 'Mark Authors Complete', + message: message, + buttons: { + ok: { + label: 'OK', + className: 'btn-primary', + callback: function() { + window.location.reload(); + } + } + } + }); + }) + .catch(error => { + // Close loading modal + bootbox.hideAll(); + + // Show error modal + bootbox.dialog({ + title: 'Error', + message: '
An error occurred: ' + error.message + '
', + buttons: { + primary: { + label: "Close", + className: 'btn-primary', + callback: function(result){ window.location.reload('series'); } + }, + } + }); + }); + } diff --git a/lazylibrarian/webServe.py b/lazylibrarian/webServe.py index 27003a24..71de1e4b 100644 --- a/lazylibrarian/webServe.py +++ b/lazylibrarian/webServe.py @@ -448,7 +448,7 @@ class WebInterface: title = 'Inactive Authors' else: title = 'Ignored Authors' - return serve_template(templatename="index.html", title=title) + return serve_template(templatename="index.html", title=title, redirect=title.lower()) @cherrypy.expose def home(self): @@ -512,7 +512,7 @@ class WebInterface: typelist=get_list(CONFIG['EBOOK_TYPE']), themelist=themelist) finally: db.close() - return serve_template(templatename="index.html", title=title) + return serve_template(templatename="index.html", title='Authors', redirect='authors') # noinspection PyUnusedLocal @cherrypy.expose @@ -2116,13 +2116,14 @@ class WebInterface: # AUTHOR ############################################################ @cherrypy.expose - def mark_authors(self, action=None, redirect=None, **args): + @cherrypy.tools.json_out() + def mark_authors_ajax(self, action=None, **args): self.check_permitted(lazylibrarian.perm_status) logger = logging.getLogger(__name__) for arg in ['author_table_length', 'ignored']: args.pop(arg, None) - if not self.valid_source(redirect): - redirect = "authors" + passed = 0 + failed = 0 if action: db = database.DBConnection() try: @@ -2130,9 +2131,11 @@ class WebInterface: check = db.match("SELECT AuthorName from authors WHERE AuthorID=?", (authorid,)) if not check: logger.warning(f'Unable to set Status to "{action}" for "{authorid}"') + failed += 1 elif action in ["Active", "Wanted", "Paused", "Ignored"]: db.upsert("authors", {'Status': action}, {'AuthorID': authorid}) logger.info(f'Status set to "{action}" for "{check["AuthorName"]}"') + passed += 1 elif action == "Delete": logger.info(f"Deleting author and books: {check['AuthorName']}") books = db.select("SELECT BookFile from books WHERE AuthorID=? AND BookFile is not null", @@ -2147,9 +2150,11 @@ class WebInterface: logger.warning(f'rmtree failed on {book["BookFile"]}, {type(e).__name__} {str(e)}') db.action('DELETE from authors WHERE AuthorID=?', (authorid,)) + passed += 1 elif action == "Remove": logger.info(f"Removing author: {check['AuthorName']}") db.action('DELETE from authors WHERE AuthorID=?', (authorid,)) + passed += 1 elif action == 'Subscribe': cookie = cherrypy.request.cookie if cookie and 'll_uid' in list(cookie.keys()): @@ -2158,10 +2163,12 @@ class WebInterface: (userid, 'author', authorid)) if res: logger.debug(f"User {userid} is already subscribed to {authorid}") + failed += 1 else: db.action('INSERT into subscribers (UserID, Type, WantID) VALUES (?, ?, ?)', (userid, 'author', authorid)) logger.debug(f"Subscribe {userid} to author {authorid}") + passed += 1 elif action == 'Unsubscribe': cookie = cherrypy.request.cookie if cookie and 'll_uid' in list(cookie.keys()): @@ -2173,12 +2180,29 @@ class WebInterface: db.action('DELETE from subscribers WHERE UserID=? and Type=? and WantID=?', (userid, 'ebook', iss['bookid'])) logger.debug(f"Unsubscribe {userid} author {authorid}") + passed += 1 finally: db.close() - raise cherrypy.HTTPRedirect(redirect) + # Return JSON response instead of redirect + total = passed + failed + summary = f"Mark Authors '{action}' completed." + if total > 0: + summary += f" {passed} successful" + if failed > 0: + summary += f", {failed} failed" + summary += f" out of {total} total items." + else: + summary += " No items were processed." + return { + 'success': True, + 'action': action, + 'passed': passed, + 'failed': failed, + 'total': total, + 'summary': summary + } - # noinspection PyGlobalUndefined @cherrypy.expose def author_page(self, authorid, book_lang=None, library='eBook', ignored=False, book_filter=''): global lastauthor @@ -5395,6 +5419,7 @@ class WebInterface: # noinspection PyBroadException @cherrypy.expose + @cherrypy.tools.json_out() def magazine_update(self, **kwargs): cherrypy.response.headers['Cache-Control'] = "max-age=0,no-cache,no-store" self.check_permitted(lazylibrarian.perm_edit)