From 513ac6cfb41b4dc221a7134876659a805e468fd3 Mon Sep 17 00:00:00 2001 From: Ozzieisaacs Date: Sun, 29 Dec 2019 13:54:52 +0100 Subject: [PATCH 1/2] Fix for #768 (If email server is configured, admins can send user passwords, also domains can be denied from registration) Fixes from tests Fix response opds with read/unread changed db_reconnect Changed output for error 500 (now including error message) Fix in task queue after 20 messages --- cps/admin.py | 38 +- cps/config_sql.py | 6 +- cps/constants.py | 2 + cps/db.py | 10 +- cps/helper.py | 12 +- cps/opds.py | 13 +- cps/static/js/table.js | 44 +- cps/templates/email_edit.html | 32 +- cps/templates/user_edit.html | 7 +- cps/ub.py | 17 +- cps/web.py | 58 +- cps/worker.py | 2 +- test/Calibre-Web TestSummary.html | 5199 +++++++++++------------------ 13 files changed, 2128 insertions(+), 3312 deletions(-) diff --git a/cps/admin.py b/cps/admin.py index 57796080..3bb747db 100644 --- a/cps/admin.py +++ b/cps/admin.py @@ -190,10 +190,10 @@ def update_view_configuration(): return view_configuration() -@admi.route("/ajax/editdomain", methods=['POST']) +@admi.route("/ajax/editdomain/", methods=['POST']) @login_required @admin_required -def edit_domain(): +def edit_domain(allow): # POST /post # name: 'username', //name of field (column in db) # pk: 1 //primary key (record id) @@ -206,14 +206,14 @@ def edit_domain(): return "" -@admi.route("/ajax/adddomain", methods=['POST']) +@admi.route("/ajax/adddomain/", methods=['POST']) @login_required @admin_required -def add_domain(): +def add_domain(allow): domain_name = request.form.to_dict()['domainname'].replace('*', '%').replace('?', '_').lower() - check = ub.session.query(ub.Registration).filter(ub.Registration.domain == domain_name).first() + check = ub.session.query(ub.Registration).filter(ub.Registration.domain == domain_name).filter(ub.Registration.allow == allow).first() if not check: - new_domain = ub.Registration(domain=domain_name) + new_domain = ub.Registration(domain=domain_name, allow=allow) ub.session.add(new_domain) ub.session.commit() return "" @@ -227,18 +227,18 @@ def delete_domain(): ub.session.query(ub.Registration).filter(ub.Registration.id == domain_id).delete() ub.session.commit() # If last domain was deleted, add all domains by default - if not ub.session.query(ub.Registration).count(): - new_domain = ub.Registration(domain="%.%") + if not ub.session.query(ub.Registration).filter(ub.Registration.allow==1).count(): + new_domain = ub.Registration(domain="%.%",allow=1) ub.session.add(new_domain) ub.session.commit() return "" -@admi.route("/ajax/domainlist") +@admi.route("/ajax/domainlist/") @login_required @admin_required -def list_domain(): - answer = ub.session.query(ub.Registration).all() +def list_domain(allow): + answer = ub.session.query(ub.Registration).filter(ub.Registration.allow == allow).all() json_dumps = json.dumps([{"domain": r.domain.replace('%', '*').replace('_', '?'), "id": r.id} for r in answer]) js = json.dumps(json_dumps.replace('"', "'")).lstrip('"').strip('"') response = make_response(js.replace("'", '"')) @@ -605,6 +605,7 @@ def edit_user(user_id): else: flash(_(u"Found an existing account for this e-mail address."), category="error") return render_title_template("user_edit.html", translations=translations, languages=languages, + mail_configured = config.get_mail_server_configured(), new_user=0, content=content, downloads=downloads, registered_oauth=oauth_check, title=_(u"Edit User %(nick)s", nick=content.nickname), page="edituser") if "nickname" in to_save and to_save["nickname"] != content.nickname: @@ -616,11 +617,11 @@ def edit_user(user_id): return render_title_template("user_edit.html", translations=translations, languages=languages, + mail_configured=config.get_mail_server_configured(), new_user=0, content=content, downloads=downloads, registered_oauth=oauth_check, - title=_(u"Edit User %(nick)s", - nick=content.nickname), + title=_(u"Edit User %(nick)s", nick=content.nickname), page="edituser") if "kindle_mail" in to_save and to_save["kindle_mail"] != content.kindle_mail: @@ -633,21 +634,27 @@ def edit_user(user_id): flash(_(u"An unknown error occured."), category="error") return render_title_template("user_edit.html", translations=translations, languages=languages, new_user=0, content=content, downloads=downloads, registered_oauth=oauth_check, + mail_configured=config.get_mail_server_configured(), title=_(u"Edit User %(nick)s", nick=content.nickname), page="edituser") @admi.route("/admin/resetpassword/") @login_required @admin_required -def reset_password(user_id): +def reset_user_password(user_id): if not config.config_public_reg: abort(404) if current_user is not None and current_user.is_authenticated: ret, message = reset_password(user_id) if ret == 1: + log.debug(u"Password for user %(user)s reset", user=message) flash(_(u"Password for user %(user)s reset", user=message), category="success") - else: + elif ret == 0: + log.error(u"An unknown error occurred. Please try again later.") flash(_(u"An unknown error occurred. Please try again later."), category="error") + else: + log.error(u"Please configure the SMTP mail settings first...") + flash(_(u"Please configure the SMTP mail settings first..."), category="error") return redirect(url_for('admin.admin')) @@ -681,6 +688,7 @@ def send_logfile(logtype): @admi.route("/get_update_status", methods=['GET']) @login_required_if_no_ano def get_update_status(): + log.info(u"Update status requested") return updater_thread.get_available_updates(request.method, locale=get_locale()) diff --git a/cps/config_sql.py b/cps/config_sql.py index 809e97d8..1ae8f235 100644 --- a/cps/config_sql.py +++ b/cps/config_sql.py @@ -38,7 +38,7 @@ class _Settings(_Base): __tablename__ = 'settings' id = Column(Integer, primary_key=True) - mail_server = Column(String, default='mail.example.org') + mail_server = Column(String, default=constants.DEFAULT_MAIL_SERVER) mail_port = Column(Integer, default=25) mail_use_ssl = Column(SmallInteger, default=0) mail_login = Column(String, default='mail@example.com') @@ -189,6 +189,10 @@ class _ConfigSQL(object): def get_mail_settings(self): return {k:v for k, v in self.__dict__.items() if k.startswith('mail_')} + def get_mail_server_configured(self): + return not bool(self.mail_server == constants.DEFAULT_MAIL_SERVER) + + def set_from_dictionary(self, dictionary, field, convertor=None, default=None): '''Possibly updates a field of this object. The new value, if present, is grabbed from the given dictionary, and optionally passed through a convertor. diff --git a/cps/constants.py b/cps/constants.py index 97b13403..7630b38f 100644 --- a/cps/constants.py +++ b/cps/constants.py @@ -94,6 +94,7 @@ LOGIN_LDAP = 1 LOGIN_OAUTH = 2 # LOGIN_OAUTH_GOOGLE = 3 +DEFAULT_MAIL_SERVER = "mail.example.org" DEFAULT_PASSWORD = "admin123" DEFAULT_PORT = 8083 @@ -105,6 +106,7 @@ except ValueError: del env_CALIBRE_PORT + EXTENSIONS_AUDIO = {'mp3', 'm4a', 'm4b'} EXTENSIONS_CONVERT = {'pdf', 'epub', 'mobi', 'azw3', 'docx', 'rtf', 'fb2', 'lit', 'lrf', 'txt', 'htmlz', 'rtf', 'odt'} EXTENSIONS_UPLOAD = {'txt', 'pdf', 'epub', 'mobi', 'azw', 'azw3', 'cbr', 'cbz', 'cbt', 'djvu', 'prc', 'doc', 'docx', diff --git a/cps/db.py b/cps/db.py index b9853896..1ef06427 100755 --- a/cps/db.py +++ b/cps/db.py @@ -33,7 +33,7 @@ from sqlalchemy.ext.declarative import declarative_base session = None cc_exceptions = ['datetime', 'comments', 'float', 'composite', 'series'] cc_classes = {} - +engine = None Base = declarative_base() @@ -288,7 +288,7 @@ class Books(Base): @property def atom_timestamp(self): - return (self.timestamp or '').replace(' ', 'T') + return (self.timestamp.strftime('%Y-%m-%dT%H:%M:%S+00:00') or '') class Custom_Columns(Base): __tablename__ = 'custom_columns' @@ -327,6 +327,7 @@ def update_title_sort(config, conn=None): def setup_db(config): dispose() + global engine if not config.config_calibre_dir: config.invalidate() @@ -428,3 +429,8 @@ def dispose(): if name.startswith("custom_column_") or name.startswith("books_custom_column_"): if table is not None: Base.metadata.remove(table) + +def reconnect_db(config): + session.close() + engine.dispose() + setup_db(config) diff --git a/cps/helper.py b/cps/helper.py index 2b92ef75..ff6fbb98 100644 --- a/cps/helper.py +++ b/cps/helper.py @@ -125,7 +125,7 @@ def send_registration_mail(e_mail, user_name, default_password, resend=False): if not resend: text += "Your new account at Calibre-Web has been created. Thanks for joining us!\r\n" text += "Please log in to your account using the following informations:\r\n" - text += "User name: %s\n" % user_name + text += "User name: %s\r\n" % user_name text += "Password: %s\r\n" % default_password text += "Don't forget to change your password after first login.\r\n" text += "Sincerely\r\n\r\n" @@ -416,6 +416,8 @@ def reset_password(user_id): existing_user = ub.session.query(ub.User).filter(ub.User.id == user_id).first() password = generate_random_password() existing_user.password = generate_password_hash(password) + if not config.get_mail_server_configured(): + return (2, None) try: ub.session.commit() send_registration_mail(existing_user.email, existing_user.nickname, password, True) @@ -699,9 +701,13 @@ def speaking_language(languages=None): # from https://code.luasoftware.com/tutorials/flask/execute-raw-sql-in-flask-sqlalchemy/ def check_valid_domain(domain_text): domain_text = domain_text.split('@', 1)[-1].lower() - sql = "SELECT * FROM registration WHERE :domain LIKE domain;" + 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() - return len(result) + if not len(result): + return False + sql = "SELECT * FROM registration WHERE (:domain LIKE domain and allow = 0);" + result = ub.session.query(ub.Registration).from_statement(text(sql)).params(domain=domain_text).all() + return not len(result) # Orders all Authors in the list according to authors sort diff --git a/cps/opds.py b/cps/opds.py index f5cc4673..3ff718da 100644 --- a/cps/opds.py +++ b/cps/opds.py @@ -276,7 +276,7 @@ def feed_languages(book_id): isoLanguages.get(part3=entry.languages[index].lang_code).name)''' return render_xml_template('feed.xml', entries=entries, pagination=pagination) -@opds.route("/opds/shelfindex/", defaults={'public': 0}) +@opds.route("/opds/shelfindex", defaults={'public': 0}) @opds.route("/opds/shelfindex/") @requires_basic_auth_if_no_ano def feed_shelfindex(public): @@ -378,15 +378,16 @@ def render_xml_template(*args, **kwargs): def feed_get_cover(book_id): return get_book_cover(book_id) -@opds.route("/opds/readbooks/") +@opds.route("/opds/readbooks") @requires_basic_auth_if_no_ano def feed_read_books(): off = request.args.get("offset") or 0 - return render_read_books(int(off) / (int(config.config_books_per_page)) + 1, True, True) + result, pagination = render_read_books(int(off) / (int(config.config_books_per_page)) + 1, True, True) + return render_xml_template('feed.xml', entries=result, pagination=pagination) - -@opds.route("/opds/unreadbooks/") +@opds.route("/opds/unreadbooks") @requires_basic_auth_if_no_ano def feed_unread_books(): off = request.args.get("offset") or 0 - return render_read_books(int(off) / (int(config.config_books_per_page)) + 1, False, True) + result, pagination = render_read_books(int(off) / (int(config.config_books_per_page)) + 1, False, True) + return render_xml_template('feed.xml', entries=result, pagination=pagination) diff --git a/cps/static/js/table.js b/cps/static/js/table.js index 47daa6da..12c07102 100644 --- a/cps/static/js/table.js +++ b/cps/static/js/table.js @@ -19,21 +19,41 @@ $(function() { - $("#domain_submit").click(function(event) { + $("#domain_allow_submit").click(function(event) { event.preventDefault(); - $("#domain_add").ajaxForm(); + $("#domain_add_allow").ajaxForm(); $(this).closest("form").submit(); $.ajax ({ method:"get", - url: window.location.pathname + "/../../ajax/domainlist", + url: window.location.pathname + "/../../ajax/domainlist/1", async: true, timeout: 900, success:function(data) { - $("#domain-table").bootstrapTable("load", data); + $("#domain-allow-table").bootstrapTable("load", data); } }); }); - $("#domain-table").bootstrapTable({ + $("#domain-allow-table").bootstrapTable({ + formatNoMatches: function () { + return ""; + }, + striped: false + }); + $("#domain_deny_submit").click(function(event) { + event.preventDefault(); + $("#domain_add_deny").ajaxForm(); + $(this).closest("form").submit(); + $.ajax ({ + method:"get", + url: window.location.pathname + "/../../ajax/domainlist/0", + async: true, + timeout: 900, + success:function(data) { + $("#domain-deny-table").bootstrapTable("load", data); + } + }); + }); + $("#domain-deny-table").bootstrapTable({ formatNoMatches: function () { return ""; }, @@ -50,14 +70,22 @@ $(function() { $("#DeleteDomain").modal("hide"); $.ajax({ method:"get", - url: window.location.pathname + "/../../ajax/domainlist", + url: window.location.pathname + "/../../ajax/domainlist/1", async: true, timeout: 900, success:function(data) { - $("#domain-table").bootstrapTable("load", data); + $("#domain-allow-table").bootstrapTable("load", data); + } + }); + $.ajax({ + method:"get", + url: window.location.pathname + "/../../ajax/domainlist/0", + async: true, + timeout: 900, + success:function(data) { + $("#domain-deny-table").bootstrapTable("load", data); } }); - }); //triggered when modal is about to be shown $("#DeleteDomain").on("show.bs.modal", function(e) { diff --git a/cps/templates/email_edit.html b/cps/templates/email_edit.html index bb5c60a0..d1b6e893 100644 --- a/cps/templates/email_edit.html +++ b/cps/templates/email_edit.html @@ -41,22 +41,40 @@ {% if g.allow_registration %}

{{_('Allowed domains for registering')}}

- + +
+ + +
+ + +
+ + + + + + + +
+

{{_('Denied domains for registering')}}

+ - - + +
-
+
- - + +
- +
+ {% endif %} {% endblock %} diff --git a/cps/templates/user_edit.html b/cps/templates/user_edit.html index e4e36c64..cbd4f2e8 100644 --- a/cps/templates/user_edit.html +++ b/cps/templates/user_edit.html @@ -14,15 +14,14 @@ {% if ( g.user and g.user.role_passwd() or g.user.role_admin() ) and not content.role_anonymous() %} - {% if g.user and g.user.role_admin() and g.allow_registration and not new_user and not profile %} - - {% else %} + {% if g.user and g.user.role_admin() and not new_user and not profile and ( mail_configured and content.email if content.email != None %} + + {% endif %}
{% endif %} - {% endif %}
diff --git a/cps/ub.py b/cps/ub.py index b262e0eb..85ed1127 100644 --- a/cps/ub.py +++ b/cps/ub.py @@ -297,6 +297,7 @@ class Registration(Base): id = Column(Integer, primary_key=True) domain = Column(String) + allow = Column(Integer) def __repr__(self): return u"".format(self.domain) @@ -332,24 +333,32 @@ def migrate_Database(session): if not engine.dialect.has_table(engine.connect(), "registration"): ReadBook.__table__.create(bind=engine) conn = engine.connect() - conn.execute("insert into registration (domain) values('%.%')") + conn.execute("insert into registration (domain, allow) values('%.%',1)") + session.commit() + try: + session.query(exists().where(Registration.allow)).scalar() + session.commit() + except exc.OperationalError: # Database is not compatible, some columns are missing + conn = engine.connect() + conn.execute("ALTER TABLE registration ADD column 'allow' INTEGER") + conn.execute("update registration set 'allow' = 1") session.commit() # Handle table exists, but no content cnt = session.query(Registration).count() if not cnt: conn = engine.connect() - conn.execute("insert into registration (domain) values('%.%')") + conn.execute("insert into registration (domain, allow) values('%.%',1)") session.commit() try: session.query(exists().where(BookShelf.order)).scalar() - except exc.OperationalError: # Database is not compatible, some rows are missing + except exc.OperationalError: # Database is not compatible, some columns are missing conn = engine.connect() conn.execute("ALTER TABLE book_shelf_link ADD column 'order' INTEGER DEFAULT 1") session.commit() try: create = False session.query(exists().where(User.sidebar_view)).scalar() - except exc.OperationalError: # Database is not compatible, some rows are missing + except exc.OperationalError: # Database is not compatible, some columns are missing conn = engine.connect() conn.execute("ALTER TABLE user ADD column `sidebar_view` Integer DEFAULT 1") session.commit() diff --git a/cps/web.py b/cps/web.py index 572ac969..76152613 100644 --- a/cps/web.py +++ b/cps/web.py @@ -43,7 +43,7 @@ from werkzeug.exceptions import default_exceptions from werkzeug.datastructures import Headers from werkzeug.security import generate_password_hash, check_password_hash -from . import constants, logger, isoLanguages, services, worker +from . import constants, config, logger, isoLanguages, services, worker from . import searched_ids, lm, babel, db, ub, config, get_locale, app from .gdriveutils import getFileFromEbooksFolder, do_gdrive_download from .helper import common_filters, get_search_results, fill_indexpage, speaking_language, check_valid_domain, \ @@ -93,12 +93,11 @@ def error_http(error): def internal_error(error): - __, __, tb = sys.exc_info() return render_template('http_error.html', error_code="Internal Server Error", error_name=str(error), issue=True, - error_stack=traceback.format_tb(tb), + error_stack=traceback.format_exc().split("\n"), instance=config.config_calibre_web_title ), 500 @@ -791,9 +790,7 @@ def get_tasks_status(): @app.route("/reconnect") def reconnect(): - db.session.close() - db.engine.dispose() - db.setup_db() + db.reconnect_db(config) return json.dumps({}) @web.route("/search", methods=["GET"]) @@ -985,10 +982,7 @@ def render_read_books(page, are_read, as_xml=False, order=None): entries, random, pagination = fill_indexpage(page, db.Books, db_filter, order) if as_xml: - xml = render_title_template('feed.xml', entries=entries, pagination=pagination) - response = make_response(xml) - response.headers["Content-Type"] = "application/xml; charset=utf-8" - return response + return entries, pagination else: if are_read: name = _(u'Read Books') + ' (' + str(len(readBookIds)) + ')' @@ -1041,8 +1035,7 @@ def download_link(book_id, book_format): @login_required @download_required def send_to_kindle(book_id, book_format, convert): - settings = config.get_mail_settings() - if settings.get("mail_server", "mail.example.org") == "mail.example.org": + if not config.get_mail_server_configured(): flash(_(u"Please configure the SMTP mail settings first..."), category="error") elif current_user.kindle_mail: result = send_mail(book_id, book_format, convert, current_user.kindle_mail, config.config_calibre_dir, @@ -1067,16 +1060,19 @@ def register(): abort(404) if current_user is not None and current_user.is_authenticated: return redirect(url_for('web.index')) + if not config.get_mail_server_configured(): + flash(_(u"E-Mail server is not configured, please contact your administrator!"), category="error") + return render_title_template('register.html', title=_(u"register"), page="register") if request.method == "POST": to_save = request.form.to_dict() if not to_save["nickname"] or not to_save["email"]: flash(_(u"Please fill out all fields!"), category="error") return render_title_template('register.html', title=_(u"register"), page="register") - existing_user = ub.session.query(ub.User).filter(func.lower(ub.User.nickname) == to_save["nickname"] .lower()).first() existing_email = ub.session.query(ub.User).filter(ub.User.email == to_save["email"].lower()).first() + if not existing_user and not existing_email: content = ub.User() # content.password = generate_password_hash(to_save["password"]) @@ -1116,10 +1112,12 @@ def register(): @web.route('/login', methods=['GET', 'POST']) def login(): if not config.db_configured: + log.debug(u"Redirect to initial configuration") return redirect(url_for('admin.basic_configuration')) if current_user is not None and current_user.is_authenticated: return redirect(url_for('web.index')) if config.config_login_type == constants.LOGIN_LDAP and not services.ldap: + log.error(u"Cannot activate LDAP authentication") flash(_(u"Cannot activate LDAP authentication"), category="error") if request.method == "POST": form = request.form.to_dict() @@ -1129,10 +1127,12 @@ def login(): login_result = services.ldap.bind_user(form['username'], form['password']) if login_result: login_user(user, remember=True) + log.debug(u"You are now logged in as: '%s'", user.nickname) flash(_(u"you are now logged in as: '%(nickname)s'", nickname=user.nickname), category="success") return redirect_back(url_for("web.index")) if login_result is None: + log.error('Could not login. LDAP server down, please contact your administrator') flash(_(u"Could not login. LDAP server down, please contact your administrator"), category="error") else: ipAdress = request.headers.get('X-Forwarded-For', request.remote_addr) @@ -1147,6 +1147,7 @@ def login(): flash(_(u"New Password was send to your email address"), category="info") log.info('Password reset for user "%s" IP-adress: %s', form['username'], ipAdress) else: + log.info(u"An unknown error occurred. Please try again later.") flash(_(u"An unknown error occurred. Please try again later."), category="error") else: flash(_(u"Please enter valid username to reset password"), category="error") @@ -1154,18 +1155,16 @@ def login(): else: if user and check_password_hash(str(user.password), form['password']) and user.nickname != "Guest": login_user(user, remember=True) + log.debug(u"You are now logged in as: '%s'", user.nickname) flash(_(u"You are now logged in as: '%(nickname)s'", nickname=user.nickname), category="success") return redirect_back(url_for("web.index")) else: log.info('Login failed for user "%s" IP-adress: %s', form['username'], ipAdress) flash(_(u"Wrong Username or Password"), category="error") - settings = config.get_mail_settings() - mail_configured = bool(settings.get("mail_server", "mail.example.org") != "mail.example.org") next_url = url_for('web.index') - return render_title_template('login.html', title=_(u"login"), next_url=next_url, config=config, - mail = mail_configured, page="login") + mail = config.get_mail_server_configured(), page="login") @web.route('/logout') @@ -1175,6 +1174,7 @@ def logout(): logout_user() if feature_support['oauth'] and (config.config_login_type == 2 or config.config_login_type == 3): logout_oauth_user() + log.debug(u"User logged out") return redirect(url_for('web.login')) @@ -1186,7 +1186,7 @@ def remote_login(): ub.session.commit() verify_url = url_for('web.verify_token', token=auth_token.auth_token, _external=true) - + log.debug(u"Remot Login request with token: %s", auth_token.auth_token) return render_title_template('remote_login.html', title=_(u"login"), token=auth_token.auth_token, verify_url=verify_url, page="remotelogin") @@ -1200,6 +1200,7 @@ def verify_token(token): # Token not found if auth_token is None: flash(_(u"Token not found"), category="error") + log.error(u"Remote Login token not found") return redirect(url_for('web.index')) # Token expired @@ -1208,6 +1209,7 @@ def verify_token(token): ub.session.commit() flash(_(u"Token has expired"), category="error") + log.error(u"Remote Login token expired") return redirect(url_for('web.index')) # Update token with user information @@ -1216,6 +1218,7 @@ def verify_token(token): ub.session.commit() flash(_(u"Success! Please return to your device"), category="success") + log.debug(u"Remote Login token for userid %s verified", auth_token.user_id) return redirect(url_for('web.index')) @@ -1251,6 +1254,7 @@ def token_verified(): ub.session.commit() data['status'] = 'success' + log.debug(u"Remote Login for userid %s succeded", user.id) flash(_(u"you are now logged in as: '%(nickname)s'", nickname=user.nickname), category="success") response = make_response(json.dumps(data, ensure_ascii=False)) @@ -1278,8 +1282,6 @@ def profile(): downloads.append(db.session.query(db.Books).filter(db.Books.id == book.book_id).first()) else: ub.delete_download(book.book_id) - # ub.session.query(ub.Downloads).filter(book.book_id == ub.Downloads.book_id).delete() - # ub.session.commit() if request.method == "POST": to_save = request.form.to_dict() current_user.random_books = 0 @@ -1332,14 +1334,16 @@ def profile(): except IntegrityError: ub.session.rollback() flash(_(u"Found an existing account for this e-mail address."), category="error") + log.debug(u"Found an existing account for this e-mail address.") return render_title_template("user_edit.html", content=current_user, downloads=downloads, translations=translations, title=_(u"%(name)s's profile", name=current_user.nickname), page="me", registered_oauth=oauth_check, oauth_status=oauth_status) flash(_(u"Profile updated"), category="success") + log.debug(u"Profile updated") return render_title_template("user_edit.html", translations=translations, profile=1, languages=languages, - content=current_user, downloads=downloads, title= _(u"%(name)s's profile", - name=current_user.nickname), + content=current_user, downloads=downloads, + title= _(u"%(name)s's profile", name=current_user.nickname), page="me", registered_oauth=oauth_check, oauth_status=oauth_status) @@ -1353,6 +1357,7 @@ def read_book(book_id, book_format): book = db.session.query(db.Books).filter(db.Books.id == book_id).filter(common_filters()).first() if not book: flash(_(u"Error opening eBook. File does not exist or file is not accessible:"), category="error") + log.debug(u"Error opening eBook. File does not exist or file is not accessible:") return redirect(url_for("web.index")) # check if book has bookmark @@ -1362,20 +1367,25 @@ def read_book(book_id, book_format): ub.Bookmark.book_id == book_id, ub.Bookmark.format == book_format.upper())).first() if book_format.lower() == "epub": + log.debug(u"Start epub reader for %d", book_id) return render_title_template('read.html', bookid=book_id, title=_(u"Read a Book"), bookmark=bookmark) elif book_format.lower() == "pdf": + log.debug(u"Start pdf reader for %d", book_id) return render_title_template('readpdf.html', pdffile=book_id, title=_(u"Read a Book")) elif book_format.lower() == "txt": + log.debug(u"Start txt reader for %d", book_id) return render_title_template('readtxt.html', txtfile=book_id, title=_(u"Read a Book")) else: for fileExt in ["mp3", "m4b", "m4a"]: if book_format.lower() == fileExt: entries = db.session.query(db.Books).filter(db.Books.id == book_id).filter(common_filters()).first() + log.debug(u"Start mp3 listening for %d", book_id) return render_title_template('listenmp3.html', mp3file=book_id, audioformat=book_format.lower(), title=_(u"Read a Book"), entry=entries, bookmark=bookmark) for fileExt in ["cbr", "cbt", "cbz"]: if book_format.lower() == fileExt: all_name = str(book_id) + log.debug(u"Start comic reader for %d", book_id) return render_title_template('readcbr.html', comicfile=all_name, title=_(u"Read a Book"), extension=fileExt) # if feature_support['rar']: @@ -1386,6 +1396,7 @@ def read_book(book_id, book_format): # if book_format.lower() == fileext: # return render_title_template('readcbr.html', comicfile=book_id, # extension=fileext, title=_(u"Read a Book"), book=book) + log.debug(u"Error opening eBook. File does not exist or file is not accessible:") flash(_(u"Error opening eBook. File does not exist or file is not accessible."), category="error") return redirect(url_for("web.index")) @@ -1418,7 +1429,7 @@ def show_book(book_id): matching_have_read_book = getattr(entries, 'custom_column_'+str(config.config_read_column)) have_read = len(matching_have_read_book) > 0 and matching_have_read_book[0].value except KeyError: - log.error("Custom Column No.%d is not exisiting in calibre database", config.config_read_column) + log.error("Custom Column No.%d is not existing in calibre database", config.config_read_column) have_read = None else: @@ -1440,5 +1451,6 @@ def show_book(book_id): is_xhr=request.is_xhr, title=entries.title, books_shelfs=book_in_shelfs, have_read=have_read, kindle_list=kindle_list, reader_list=reader_list, page="book") else: + log.debug(u"Error opening eBook. File does not exist or file is not accessible:") flash(_(u"Error opening eBook. File does not exist or file is not accessible:"), category="error") return redirect(url_for("web.index")) diff --git a/cps/worker.py b/cps/worker.py index b508c437..409201c7 100644 --- a/cps/worker.py +++ b/cps/worker.py @@ -231,7 +231,7 @@ class WorkerThread(threading.Thread): self.queue.pop(index) self.UIqueue.pop(index) # if we are deleting entries before the current index, adjust the index - if index <= self.current and index: + if index <= self.current and self.current: self.current -= 1 self.last = len(self.queue) diff --git a/test/Calibre-Web TestSummary.html b/test/Calibre-Web TestSummary.html index 6963d268..51db76cb 100644 --- a/test/Calibre-Web TestSummary.html +++ b/test/Calibre-Web TestSummary.html @@ -30,15 +30,15 @@
-

Start Time: 2019-07-09 18:14:27.709466

+

Start Time: 2019-12-29 09:32:57.266265

-

Stop Time: 2019-07-09 19:21:03.774799

+

Stop Time: 2019-12-29 10:08:09.098085

-

Duration: 1:06:36.065333

+

Duration: 0:35:11.831820

@@ -95,550 +95,173 @@ Skip View - - test_opds_feed.test_opds_feed - 16 - 2 + + test_anonymous.test_anonymous + 10 + 10 + 0 0 0 - 14 - Detail + Detail -
test_opds
+
test_guest_about
PASS - + -
test_opds_author
- - -
- SKIP -
- - - +
test_guest_change_visibility_category
+ PASS - + -
test_opds_calibre_companion
- - -
- SKIP -
- - - +
test_guest_change_visibility_hot
+ PASS - + -
test_opds_cover
- - -
- SKIP -
- - - +
test_guest_change_visibility_language
+ PASS - + -
test_opds_download_book
- - -
- SKIP -
- - - +
test_guest_change_visibility_publisher
+ PASS -
test_opds_guest_user
+
test_guest_change_visibility_rated
PASS - + -
test_opds_hot
- - -
- SKIP -
- - - +
test_guest_change_visibility_series
+ PASS - + -
test_opds_language
- - -
- SKIP -
- - - +
test_guest_random_books_available
+ PASS - + -
test_opds_non_admin
- - -
- SKIP -
- - - +
test_guest_visibility_read
+ PASS - + -
test_opds_paging
- - -
- SKIP -
- - - - - - - -
test_opds_publisher
- - -
- SKIP -
- - - - - - - -
test_opds_random
- - -
- SKIP -
- - - - - - - -
test_opds_read_unread
- - -
- SKIP -
- - - - - - - -
test_opds_search
- - -
- SKIP -
- - - - - - - -
test_opds_series
- - -
- SKIP -
- - - - - - - -
test_opds_shelf_access
- - -
- SKIP -
- - - +
test_guest_visibility_sidebar
+ PASS - test_register.test_register - 4 + test_cli.test_cli + 6 + 5 0 0 - 0 - 4 + 1 - Detail + Detail - + -
test_login_with_password
+
test_already_started
+ + PASS + + + +
test_cli_SSL_files
+ + PASS + + + +
test_cli_different_folder
+ + PASS + + + +
test_cli_different_settings_database
+ + PASS + + + +
test_cli_gdrive_location
- SKIP + SKIP
- - - test_email_ssl.test_SSL_Python36 - 4 - 4 - 0 + + +
test_convert_failed_and_email
+ + +
+ PASS +
+ + + + + + + +
test_convert_only
+ + PASS + + + +
test_convert_parameter
+ + PASS + + + +
test_convert_wrong_excecutable
+ + PASS + + + +
test_email_failed
+ + +
+ PASS +
+ + + + + + + +
test_email_only
+ + +
+ PASS +
+ + + + + + + +
test_kindle_send_not_configured
+ + PASS + + + test_edit_books.test_edit_books + 23 + 12 0 0 + 11 - Detail + Detail - + -
test_SSL_None_setup_error
+
test_database_errors
- PASS + SKIP
-