diff --git a/cps/admin.py b/cps/admin.py index 4038977e..935aee45 100644 --- a/cps/admin.py +++ b/cps/admin.py @@ -31,13 +31,14 @@ from datetime import datetime, timedelta from babel import Locale as LC from babel.dates import format_datetime -from flask import Blueprint, flash, redirect, url_for, abort, request, make_response, send_from_directory, g +from flask import Blueprint, flash, redirect, url_for, abort, request, make_response, send_from_directory, g, Response from flask_login import login_required, current_user, logout_user, confirm_login from flask_babel import gettext as _ from sqlalchemy import and_ from sqlalchemy.orm.attributes import flag_modified from sqlalchemy.exc import IntegrityError, OperationalError, InvalidRequestError from sqlalchemy.sql.expression import func, or_, text +# from sqlalchemy.func import field from . import constants, logger, helper, services from .cli import filepicker @@ -241,29 +242,44 @@ def edit_user_table(): @login_required @admin_required def list_users(): - off = request.args.get("offset") or 0 - limit = request.args.get("limit") or 10 + off = int(request.args.get("offset") or 0) + limit = int(request.args.get("limit") or 10) search = request.args.get("search") - sort = request.args.get("sort") + sort = request.args.get("sort", "state") order = request.args.get("order") - if sort and order: + state = None + if sort != "state" and order: order = text(sort + " " + order) else: order = ub.User.name.desc() + if sort == "state": + state = json.loads(request.args.get("state")) all_user = ub.session.query(ub.User) if not config.config_anonbrowse: all_user = all_user.filter(ub.User.role.op('&')(constants.ROLE_ANONYMOUS) != constants.ROLE_ANONYMOUS) - total_count = all_user.count() + + total_count = filtered_count = all_user.count() + if search: - users = all_user.filter(or_(func.lower(ub.User.name).ilike("%" + search + "%"), + all_user = all_user.filter(or_(func.lower(ub.User.name).ilike("%" + search + "%"), func.lower(ub.User.kindle_mail).ilike("%" + search + "%"), - func.lower(ub.User.email).ilike("%" + search + "%")))\ - .order_by(order).offset(off).limit(limit).all() - filtered_count = len(users) + func.lower(ub.User.email).ilike("%" + search + "%"))) + if state: + outcome = list() + userlist = {user.id:user for user in all_user.all()} + for entry in state: + outcome.append(userlist[entry]) + del userlist[entry] + for entry in userlist: + outcome.append(userlist[entry]) + if request.args.get("order", "").lower() == "asc": + outcome.reverse() + users = outcome[off:off + limit] else: users = all_user.order_by(order).offset(off).limit(limit).all() - filtered_count = total_count + if search: + filtered_count = len(users) for user in users: if user.default_language == "all": @@ -277,12 +293,19 @@ def list_users(): response.headers["Content-Type"] = "application/json; charset=utf-8" return response -@admi.route("/ajax/deleteuser") +@admi.route("/ajax/deleteuser", methods=['POST']) @login_required @admin_required def delete_user(): - # ToDo User delete check also not last one - return "" + user_id = request.values.get('userid', -1) + content = ub.session.query(ub.User).filter(ub.User.id == int(user_id)).one_or_none() + try: + message = _delete_user(content) + return Response(json.dumps({'type': "success", 'message': message}), mimetype='application/json') + except Exception as ex: + return Response(json.dumps({'type': "danger", 'message':str(ex)}), mimetype='application/json') + log.error("User not found") + return Response(json.dumps({'type': "danger", 'message':_("User not found")}), mimetype='application/json') @admi.route("/ajax/getlocale") @login_required @@ -1194,22 +1217,29 @@ def _handle_new_user(to_save, content, languages, translations, kobo_support): ub.session.rollback() flash(_(u"Settings DB is not Writeable"), category="error") +def _delete_user(content): + if ub.session.query(ub.User).filter(ub.User.role.op('&')(constants.ROLE_ADMIN) == constants.ROLE_ADMIN, + ub.User.id != content.id).count(): + if content.name != "Guest": + ub.session.query(ub.User).filter(ub.User.id == content.id).delete() + ub.session_commit() + log.info(u"User {} deleted".format(content.name)) + return(_(u"User '%(nick)s' deleted", nick=content.name)) + else: + log.warning(_(u"Can't delete Guest User")) + raise Exception(_(u"Can't delete Guest User")) + else: + log.warning(u"No admin user remaining, can't delete user") + raise Exception(_(u"No admin user remaining, can't delete user")) + def _handle_edit_user(to_save, content, languages, translations, kobo_support): if to_save.get("delete"): - if ub.session.query(ub.User).filter(ub.User.role.op('&')(constants.ROLE_ADMIN) == constants.ROLE_ADMIN, - ub.User.id != content.id).count(): - if content.name != "Guest": - ub.session.query(ub.User).filter(ub.User.id == content.id).delete() - ub.session_commit() - flash(_(u"User '%(nick)s' deleted", nick=content.name), category="success") - return redirect(url_for('admin.admin')) - else: - flash(_(u"Can't delete Guest User"), category="error") - return redirect(url_for('admin.admin')) - else: - flash(_(u"No admin user remaining, can't delete user", nick=content.name), category="error") - return redirect(url_for('admin.admin')) + try: + flash(_delete_user(content), category="success") + except Exception as ex: + flash(str(ex), category="error") + return redirect(url_for('admin.admin')) else: if not ub.session.query(ub.User).filter(ub.User.role.op('&')(constants.ROLE_ADMIN) == constants.ROLE_ADMIN, ub.User.id != content.id).count() and 'admin_role' not in to_save: diff --git a/cps/db.py b/cps/db.py index b875ded7..51aeeca1 100644 --- a/cps/db.py +++ b/cps/db.py @@ -689,23 +689,26 @@ class CalibreDB(): return self.session.query(Books) \ .filter(and_(Books.authors.any(and_(*q)), func.lower(Books.title).ilike("%" + title + "%"))).first() - # read search results from calibre-database and return it (function is used for feed and simple search - def get_search_results(self, term, offset=None, order=None, limit=None): - order = order or [Books.sort] - pagination = None + def search_query(self, term): term.strip().lower() self.session.connection().connection.connection.create_function("lower", 1, lcase) q = list() authorterms = re.split("[, ]+", term) for authorterm in authorterms: q.append(Books.authors.any(func.lower(Authors.name).ilike("%" + authorterm + "%"))) - result = self.session.query(Books).filter(self.common_filters(True)).filter( + return self.session.query(Books).filter(self.common_filters(True)).filter( or_(Books.tags.any(func.lower(Tags.name).ilike("%" + term + "%")), Books.series.any(func.lower(Series.name).ilike("%" + term + "%")), Books.authors.any(and_(*q)), Books.publishers.any(func.lower(Publishers.name).ilike("%" + term + "%")), func.lower(Books.title).ilike("%" + term + "%") - )).order_by(*order).all() + )) + + # read search results from calibre-database and return it (function is used for feed and simple search + def get_search_results(self, term, offset=None, order=None, limit=None): + order = order or [Books.sort] + pagination = None + result = self.search_query(term).order_by(*order).all() result_count = len(result) if offset != None and limit != None: offset = int(offset) diff --git a/cps/helper.py b/cps/helper.py index f1c32ea0..29163685 100644 --- a/cps/helper.py +++ b/cps/helper.py @@ -795,8 +795,8 @@ def tags_filters(): # checks if domain is in database (including wildcards) # example SELECT * FROM @TABLE WHERE 'abcdefg' LIKE Name; # from https://code.luasoftware.com/tutorials/flask/execute-raw-sql-in-flask-sqlalchemy/ +# in all calls the email address is checked for validity def check_valid_domain(domain_text): - # domain_text = domain_text.split('@', 1)[-1].lower() sql = "SELECT * FROM registration WHERE (:domain LIKE domain and allow = 1);" result = ub.session.query(ub.Registration).from_statement(text(sql)).params(domain=domain_text).all() if not len(result): diff --git a/cps/static/js/table.js b/cps/static/js/table.js index a0503976..59b70626 100644 --- a/cps/static/js/table.js +++ b/cps/static/js/table.js @@ -422,6 +422,7 @@ $(function() { $("#user-table").bootstrapTable({ sidePagination: "server", + queryParams: queryParams, pagination: true, paginationLoop: false, paginationDetailHAlign: " hidden", @@ -462,28 +463,10 @@ $(function() { $("input[data-name='sidebar_read_and_unread'][data-pk='"+guest.data("pk")+"']").prop("disabled", true); $(".user-remove[data-pk='"+guest.data("pk")+"']").prop("disabled", true); }, - - // eslint-disable-next-line no-unused-vars - /*onEditableSave: function (field, row, oldvalue, $el) { - if (field === "title" || field === "authors") { - $.ajax({ - method:"get", - dataType: "json", - url: window.location.pathname + "/../../ajax/sort_value/" + field + "/" + row.id, - success: function success(data) { - var key = Object.keys(data)[0]; - $("#books-table").bootstrapTable("updateCellByUniqueId", { - id: row.id, - field: key, - value: data[key] - }); - // console.log(data); - } - }); - } - },*/ - // eslint-disable-next-line no-unused-vars - onColumnSwitch: function (field, checked) { + onSort: function(a, b) { + console.log("huh"); + }, + onColumnSwitch: function () { var visible = $("#user-table").bootstrapTable("getVisibleColumns"); var hidden = $("#user-table").bootstrapTable("getHiddenColumns"); var st = ""; @@ -525,7 +508,6 @@ $(function() { }); } - $("#user-table").on("click-cell.bs.table", function (field, value, row, $element) { if (value === "denied_column_value") { ConfirmDialog("btndeluser", "GeneralDeleteModal", $element.id, user_handle); @@ -563,7 +545,6 @@ $(function() { $(".button_head").removeClass("disabled"); $(".header_select").removeAttr("disabled"); } - }); }); @@ -603,7 +584,7 @@ function EbookActions (value, row) { /* Function for deleting books */ function UserActions (value, row) { return [ - "