diff --git a/cps/admin.py b/cps/admin.py index 97b41165..cd30f7d4 100644 --- a/cps/admin.py +++ b/cps/admin.py @@ -1370,11 +1370,11 @@ def _delete_user(content): if content.name != "Guest": # Delete all books in shelfs belonging to user, all shelfs of user, downloadstat of user, read status # and user itself - ub.session.query(ub.ReadBook).filter(ub.User.id == ub.ReadBook.user_id).delete() - ub.session.query(ub.Downloads).filter(ub.User.id == ub.Downloads.user_id).delete() - for us in ub.session.query(ub.Shelf).filter(ub.User.id == ub.Shelf.user_id): + ub.session.query(ub.ReadBook).filter(content.id == ub.ReadBook.user_id).delete() + ub.session.query(ub.Downloads).filter(content.id == ub.Downloads.user_id).delete() + for us in ub.session.query(ub.Shelf).filter(content.id == ub.Shelf.user_id): ub.session.query(ub.BookShelf).filter(us.id == ub.BookShelf.shelf).delete() - ub.session.query(ub.Shelf).filter(ub.User.id == ub.Shelf.user_id).delete() + ub.session.query(ub.Shelf).filter(content.id == ub.Shelf.user_id).delete() ub.session.query(ub.User).filter(ub.User.id == content.id).delete() ub.session_commit() log.info(u"User {} deleted".format(content.name)) diff --git a/cps/constants.py b/cps/constants.py index f8ee3721..a0daa515 100644 --- a/cps/constants.py +++ b/cps/constants.py @@ -20,6 +20,9 @@ from __future__ import division, print_function, unicode_literals import sys import os from collections import namedtuple +from sqlalchemy import __version__ as sql_version + +sqlalchemy_version2 = ([int(x) for x in sql_version.split('.')] >= [2,0,0]) # if installed via pip this variable is set to true (empty file with name .HOMEDIR present) HOME_CONFIG = os.path.isfile(os.path.join(os.path.dirname(os.path.abspath(__file__)), '.HOMEDIR')) diff --git a/cps/db.py b/cps/db.py index 3dd446bc..ed164afc 100644 --- a/cps/db.py +++ b/cps/db.py @@ -690,6 +690,8 @@ class CalibreDB(): randm = false() off = int(int(pagesize) * (page - 1)) query = self.session.query(database) + if len(join) == 6: + query = query.outerjoin(join[0], join[1]).outerjoin(join[2]).outerjoin(join[3], join[4]).outerjoin(join[5]) if len(join) == 3: query = query.outerjoin(join[0], join[1]).outerjoin(join[2]) elif len(join) == 2: @@ -755,6 +757,8 @@ class CalibreDB(): for authorterm in authorterms: q.append(Books.authors.any(func.lower(Authors.name).ilike("%" + authorterm + "%"))) query = self.session.query(Books) + if len(join) == 6: + query = query.outerjoin(join[0], join[1]).outerjoin(join[2]).outerjoin(join[3], join[4]).outerjoin(join[5]) if len(join) == 3: query = query.outerjoin(join[0], join[1]).outerjoin(join[2]) elif len(join) == 2: diff --git a/cps/kobo.py b/cps/kobo.py index 11381170..6952a692 100644 --- a/cps/kobo.py +++ b/cps/kobo.py @@ -44,11 +44,11 @@ from werkzeug.datastructures import Headers from sqlalchemy import func from sqlalchemy.sql.expression import and_, or_ from sqlalchemy.exc import StatementError -from sqlalchemy import __version__ as sql_version from sqlalchemy.sql import select import requests from . import config, logger, kobo_auth, db, calibre_db, helper, shelf as shelf_lib, ub +from .constants import sqlalchemy_version2 from .helper import get_download_link from .services import SyncToken as SyncToken from .web import download_required @@ -66,7 +66,6 @@ kobo_auth.register_url_value_preprocessor(kobo) log = logger.create() -sql2 = ([int(x) for x in sql_version.split('.')] >= [2,0,0]) def get_store_url_for_current_request(): # Programmatically modify the current url to point to the official Kobo store @@ -159,7 +158,7 @@ def HandleSyncRequest(): only_kobo_shelves = current_user.kobo_only_shelves_sync if only_kobo_shelves: - if sql2: + if sqlalchemy_version2: changed_entries = select(db.Books, ub.ArchivedBook.last_modified, ub.BookShelf.date_added, @@ -183,7 +182,7 @@ def HandleSyncRequest(): .distinct() ) else: - if sql2: + if sqlalchemy_version2: changed_entries = select(db.Books, ub.ArchivedBook.last_modified, ub.ArchivedBook.is_archived) else: changed_entries = calibre_db.session.query(db.Books, @@ -202,7 +201,7 @@ def HandleSyncRequest(): changed_entries = changed_entries.filter(db.Books.id > sync_token.books_last_id) reading_states_in_new_entitlements = [] - if sql2: + if sqlalchemy_version2: books = calibre_db.session.execute(changed_entries.limit(SYNC_ITEM_LIMIT)) else: books = changed_entries.limit(SYNC_ITEM_LIMIT) @@ -246,7 +245,7 @@ def HandleSyncRequest(): new_books_last_created = max(ts_created, new_books_last_created) - if sql2: + if sqlalchemy_version2: max_change = calibre_db.session.execute(changed_entries .filter(ub.ArchivedBook.is_archived) .order_by(func.datetime(ub.ArchivedBook.last_modified).desc()))\ @@ -260,7 +259,7 @@ def HandleSyncRequest(): new_archived_last_modified = max(new_archived_last_modified, max_change) # no. of books returned - if sql2: + if sqlalchemy_version2: entries = calibre_db.session.execute(changed_entries).all() book_count = len(entries) else: @@ -697,7 +696,7 @@ def sync_shelves(sync_token, sync_results, only_kobo_shelves=False): }) extra_filters.append(ub.Shelf.kobo_sync) - if sql2: + if sqlalchemy_version2: shelflist = ub.session.execute(select(ub.Shelf).outerjoin(ub.BookShelf).filter( or_(func.datetime(ub.Shelf.last_modified) > sync_token.tags_last_modified, func.datetime(ub.BookShelf.date_added) > sync_token.tags_last_modified), diff --git a/cps/shelf.py b/cps/shelf.py index 431eeff8..d232e850 100644 --- a/cps/shelf.py +++ b/cps/shelf.py @@ -72,10 +72,9 @@ def add_to_shelf(shelf_id, book_id): if not check_shelf_edit_permissions(shelf): if not xhr: - flash(_(u"Sorry you are not allowed to add a book to the the shelf: %(shelfname)s", shelfname=shelf.name), - category="error") + flash(_(u"Sorry you are not allowed to add a book to that shelf"), category="error") return redirect(url_for('web.index')) - return "Sorry you are not allowed to add a book to the the shelf: %s" % shelf.name, 403 + return "Sorry you are not allowed to add a book to the that shelf", 403 book_in_shelf = ub.session.query(ub.BookShelf).filter(ub.BookShelf.shelf == shelf_id, ub.BookShelf.book_id == book_id).first() @@ -228,18 +227,21 @@ def remove_from_shelf(shelf_id, book_id): @login_required def create_shelf(): shelf = ub.Shelf() - return create_edit_shelf(shelf, title=_(u"Create a Shelf"), page="shelfcreate") + return create_edit_shelf(shelf, page_title=_(u"Create a Shelf"), page="shelfcreate") @shelf.route("/shelf/edit/", methods=["GET", "POST"]) @login_required def edit_shelf(shelf_id): shelf = ub.session.query(ub.Shelf).filter(ub.Shelf.id == shelf_id).first() - return create_edit_shelf(shelf, title=_(u"Edit a shelf"), page="shelfedit", shelf_id=shelf_id) + if not check_shelf_edit_permissions(shelf): + flash(_(u"Sorry you are not allowed to edit this shelf"), category="error") + return redirect(url_for('web.index')) + return create_edit_shelf(shelf, page_title=_(u"Edit a shelf"), page="shelfedit", shelf_id=shelf_id) # if shelf ID is set, we are editing a shelf -def create_edit_shelf(shelf, title, page, shelf_id=False): +def create_edit_shelf(shelf, page_title, page, shelf_id=False): sync_only_selected_shelves = current_user.kobo_only_shelves_sync # calibre_db.session.query(ub.Shelf).filter(ub.Shelf.user_id == current_user.id).filter(ub.Shelf.kobo_sync).count() if request.method == "POST": @@ -247,20 +249,20 @@ def create_edit_shelf(shelf, title, page, shelf_id=False): shelf.is_public = 1 if to_save.get("is_public") else 0 if config.config_kobo_sync: shelf.kobo_sync = True if to_save.get("kobo_sync") else False - - if check_shelf_is_unique(shelf, to_save, shelf_id): - shelf.name = to_save["title"] + shelf_title = to_save.get("title", "") + if check_shelf_is_unique(shelf, shelf_title, shelf_id): + shelf.name = shelf_title if not shelf_id: shelf.user_id = int(current_user.id) ub.session.add(shelf) shelf_action = "created" - flash_text = _(u"Shelf %(title)s created", title=to_save["title"]) + flash_text = _(u"Shelf %(title)s created", title=shelf_title) else: shelf_action = "changed" - flash_text = _(u"Shelf %(title)s changed", title=to_save["title"]) + flash_text = _(u"Shelf %(title)s changed", title=shelf_title) try: ub.session.commit() - log.info(u"Shelf {} {}".format(to_save["title"], shelf_action)) + log.info(u"Shelf {} {}".format(shelf_title, shelf_action)) flash(flash_text, category="success") return redirect(url_for('shelf.show_shelf', shelf_id=shelf.id)) except (OperationalError, InvalidRequestError) as ex: @@ -274,37 +276,37 @@ def create_edit_shelf(shelf, title, page, shelf_id=False): flash(_(u"There was an error"), category="error") return render_title_template('shelf_edit.html', shelf=shelf, - title=title, + title=page_title, page=page, kobo_sync_enabled=config.config_kobo_sync, sync_only_selected_shelves=sync_only_selected_shelves) -def check_shelf_is_unique(shelf, to_save, shelf_id=False): +def check_shelf_is_unique(shelf, title, shelf_id=False): if shelf_id: ident = ub.Shelf.id != shelf_id else: ident = true() if shelf.is_public == 1: is_shelf_name_unique = ub.session.query(ub.Shelf) \ - .filter((ub.Shelf.name == to_save["title"]) & (ub.Shelf.is_public == 1)) \ + .filter((ub.Shelf.name == title) & (ub.Shelf.is_public == 1)) \ .filter(ident) \ .first() is None if not is_shelf_name_unique: - log.error("A public shelf with the name '{}' already exists.".format(to_save["title"])) - flash(_(u"A public shelf with the name '%(title)s' already exists.", title=to_save["title"]), + log.error("A public shelf with the name '{}' already exists.".format(title)) + flash(_(u"A public shelf with the name '%(title)s' already exists.", title=title), category="error") else: is_shelf_name_unique = ub.session.query(ub.Shelf) \ - .filter((ub.Shelf.name == to_save["title"]) & (ub.Shelf.is_public == 0) & + .filter((ub.Shelf.name == title) & (ub.Shelf.is_public == 0) & (ub.Shelf.user_id == int(current_user.id))) \ .filter(ident) \ .first() is None if not is_shelf_name_unique: - log.error("A private shelf with the name '{}' already exists.".format(to_save["title"])) - flash(_(u"A private shelf with the name '%(title)s' already exists.", title=to_save["title"]), + log.error("A private shelf with the name '{}' already exists.".format(title)) + flash(_(u"A private shelf with the name '%(title)s' already exists.", title=title), category="error") return is_shelf_name_unique @@ -378,7 +380,9 @@ def order_shelf(shelf_id): def change_shelf_order(shelf_id, order): - result = calibre_db.session.query(db.Books).join(ub.BookShelf, ub.BookShelf.book_id == db.Books.id) \ + result = calibre_db.session.query(db.Books).outerjoin(db.books_series_link, + db.Books.id == db.books_series_link.c.book)\ + .outerjoin(db.Series).join(ub.BookShelf, ub.BookShelf.book_id == db.Books.id) \ .filter(ub.BookShelf.shelf == shelf_id).order_by(*order).all() for index, entry in enumerate(result): book = ub.session.query(ub.BookShelf).filter(ub.BookShelf.shelf == shelf_id) \ @@ -408,9 +412,11 @@ def render_show_shelf(shelf_type, shelf_id, page_no, sort_param): if sort_param == 'old': change_shelf_order(shelf_id, [db.Books.timestamp]) if sort_param == 'authaz': - change_shelf_order(shelf_id, [db.Books.author_sort.asc()]) + change_shelf_order(shelf_id, [db.Books.author_sort.asc(), db.Series.name, db.Books.series_index]) if sort_param == 'authza': - change_shelf_order(shelf_id, [db.Books.author_sort.desc()]) + change_shelf_order(shelf_id, [db.Books.author_sort.desc(), + db.Series.name.desc(), + db.Books.series_index.desc()]) page = "shelf.html" pagesize = 0 else: diff --git a/cps/static/js/main.js b/cps/static/js/main.js index ce930cc3..8a31c18c 100644 --- a/cps/static/js/main.js +++ b/cps/static/js/main.js @@ -611,7 +611,10 @@ $(function() { if (xhr.status < 400) { $("#spinning_success").hide(); clearInterval(rebootInterval); - handle_response(data.result); + if (data.result) { + handle_response(data.result); + data.result = ""; + } } }, }); diff --git a/cps/static/js/table.js b/cps/static/js/table.js index a38ca1c6..09e8f472 100644 --- a/cps/static/js/table.js +++ b/cps/static/js/table.js @@ -661,33 +661,34 @@ function move_header_elements() { } }); $(".multi_selector").selectpicker(); - - if (! $._data($(".multi_head").get(0), "events") ) { - // Functions have to be here, otherwise the callbacks are not fired if visible columns are changed - $(".multi_head").on("click", function () { - var val = $(this).data("set"); - var field = $(this).data("name"); - var result = $('#user-table').bootstrapTable('getSelections').map(a => a.id); - var values = $("#" + field).val(); - confirmDialog( - "restrictions", - "GeneralChangeModal", - 0, - function () { - $.ajax({ - method: "post", - url: window.location.pathname + "/../../ajax/editlistusers/" + field, - data: {"pk": result, "value": values, "action": val}, - success: function (data) { - handleListServerResponse(data); - }, - error: function (data) { - handleListServerResponse([{type: "danger", message: data.responseText}]) - }, - }); - } - ); - }); + if ($(".multi_head").length) { + if (!$._data($(".multi_head").get(0), "events")) { + // Functions have to be here, otherwise the callbacks are not fired if visible columns are changed + $(".multi_head").on("click", function () { + var val = $(this).data("set"); + var field = $(this).data("name"); + var result = $('#user-table').bootstrapTable('getSelections').map(a => a.id); + var values = $("#" + field).val(); + confirmDialog( + "restrictions", + "GeneralChangeModal", + 0, + function () { + $.ajax({ + method: "post", + url: window.location.pathname + "/../../ajax/editlistusers/" + field, + data: {"pk": result, "value": values, "action": val}, + success: function (data) { + handleListServerResponse(data); + }, + error: function (data) { + handleListServerResponse([{type: "danger", message: data.responseText}]) + }, + }); + } + ); + }); + } } $("#user_delete_selection").click(function () { @@ -699,38 +700,41 @@ function move_header_elements() { $("#select_default_language").on("change", function () { selectHeader(this, "default_language"); }); - - 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"); - checkboxHeader(val, name, data); - }); + 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"); + checkboxHeader(val, name, data); + }); + } } - if (! $._data($(".button_head").get(0), "events") ) { - $(".button_head").on("click", function () { - var result = $('#user-table').bootstrapTable('getSelections').map(a => a.id); - confirmDialog( - "btndeluser", - "GeneralDeleteModal", - 0, - function () { - $.ajax({ - method: "post", - url: window.location.pathname + "/../../ajax/deleteuser", - data: {"userid": result}, - success: function (data) { - selections = selections.filter((el) => !result.includes(el)); - handleListServerResponse(data); - }, - error: function (data) { - handleListServerResponse([{type: "danger", message: data.responseText}]) - }, - }); - } - ); - }); + if ($(".button_head").length) { + if (!$._data($(".button_head").get(0), "events")) { + $(".button_head").on("click", function () { + var result = $('#user-table').bootstrapTable('getSelections').map(a => a.id); + confirmDialog( + "btndeluser", + "GeneralDeleteModal", + 0, + function () { + $.ajax({ + method: "post", + url: window.location.pathname + "/../../ajax/deleteuser", + data: {"userid": result}, + success: function (data) { + selections = selections.filter((el) => !result.includes(el)); + handleListServerResponse(data); + }, + error: function (data) { + handleListServerResponse([{type: "danger", message: data.responseText}]) + }, + }); + } + ); + }); + } } } diff --git a/cps/templates/user_edit.html b/cps/templates/user_edit.html index cc83a1b5..6fb30fc3 100644 --- a/cps/templates/user_edit.html +++ b/cps/templates/user_edit.html @@ -67,15 +67,14 @@ {% endif %}
- {% for element in sidebar %} - {% if element['config_show'] %} -
- - -
- {% endif %} - {% endfor %} - + {% for element in sidebar %} + {% if element['config_show'] %} +
+ + +
+ {% endif %} + {% endfor %}
@@ -84,6 +83,7 @@ {{_('Add Allowed/Denied Tags')}} {{_('Add allowed/Denied Custom Column Values')}} {% endif %} +
{% if g.user and g.user.role_admin() and not profile %} @@ -131,32 +131,32 @@
{% endif %} -
-
{{_('Save')}}
- {% if not profile %} -
{{_('Cancel')}}
- {% endif %} - {% if g.user and g.user.role_admin() and not profile and not new_user and not content.role_anonymous() %} -
{{_('Delete User')}}
- {% endif %} +
+
{{_('Save')}}
+ {% if not profile %} +
{{_('Cancel')}}
+ {% endif %} + {% if g.user and g.user.role_admin() and not profile and not new_user and not content.role_anonymous() %} +
{{_('Delete User')}}
+ {% endif %}
-