diff --git a/cps/__init__.py b/cps/__init__.py index c41509424..890d5867d 100644 --- a/cps/__init__.py +++ b/cps/__init__.py @@ -25,7 +25,8 @@ import sys import os import mimetypes -from flask import Flask +from flask import Flask, request +from flask.sessions import SecureCookieSessionInterface from .MyLoginManager import MyLoginManager from flask_principal import Principal @@ -114,8 +115,14 @@ if limiter_present: else: limiter = None +class ScriptNameSessionInterface(SecureCookieSessionInterface): + def get_cookie_path(self, app): + # Called once per response, after request context exists + return app.wsgi_app.script_name.rstrip("/") or "/" + def create_app(): + app.session_interface = ScriptNameSessionInterface() if csrf: csrf.init_app(app) diff --git a/cps/cw_login/login_manager.py b/cps/cw_login/login_manager.py index a3714af60..268b92855 100644 --- a/cps/cw_login/login_manager.py +++ b/cps/cw_login/login_manager.py @@ -7,7 +7,6 @@ from flask import abort from flask import current_app from flask import flash from flask import g -from flask import has_app_context from flask import redirect from flask import request from flask import session @@ -469,7 +468,7 @@ class LoginManager: config = current_app.config cookie_name = config.get("REMEMBER_COOKIE_NAME", COOKIE_NAME) domain = config.get("REMEMBER_COOKIE_DOMAIN") - path = config.get("REMEMBER_COOKIE_PATH", "/") + path = config.get("REMEMBER_COOKIE_PATH", current_app.wsgi_app.script_name) secure = config.get("REMEMBER_COOKIE_SECURE", COOKIE_SECURE) httponly = config.get("REMEMBER_COOKIE_HTTPONLY", COOKIE_HTTPONLY) @@ -520,36 +519,5 @@ class LoginManager: config = current_app.config cookie_name = config.get("REMEMBER_COOKIE_NAME", COOKIE_NAME) domain = config.get("REMEMBER_COOKIE_DOMAIN") - path = config.get("REMEMBER_COOKIE_PATH", "/") + path = config.get("REMEMBER_COOKIE_PATH", current_app.wsgi_app.script_name) response.delete_cookie(cookie_name, domain=domain, path=path) - - @property - def _login_disabled(self): - """Legacy property, use app.config['LOGIN_DISABLED'] instead.""" - import warnings - - warnings.warn( - "'_login_disabled' is deprecated and will be removed in" - " Flask-Login 0.7. Use 'LOGIN_DISABLED' in 'app.config'" - " instead.", - DeprecationWarning, - stacklevel=2, - ) - - if has_app_context(): - return current_app.config.get("LOGIN_DISABLED", False) - return False - - @_login_disabled.setter - def _login_disabled(self, newvalue): - """Legacy property setter, use app.config['LOGIN_DISABLED'] instead.""" - import warnings - - warnings.warn( - "'_login_disabled' is deprecated and will be removed in" - " Flask-Login 0.7. Use 'LOGIN_DISABLED' in 'app.config'" - " instead.", - DeprecationWarning, - stacklevel=2, - ) - current_app.config["LOGIN_DISABLED"] = newvalue diff --git a/cps/render_template.py b/cps/render_template.py index 0f0a2894c..03230ecd8 100644 --- a/cps/render_template.py +++ b/cps/render_template.py @@ -47,7 +47,7 @@ def get_sidebar_config(kwargs=None): if current_user.role_admin(): sidebar.append({"glyph": "glyphicon-download", "text": _('Downloaded Books'), "link": 'web.download_list', "id": "download", "visibility": constants.SIDEBAR_DOWNLOAD, 'public': (not current_user.is_anonymous), - "page": "download", "show_text": _('Show Downloaded Books'), + "page": "download", "show_text": _('Show Downloaded Books'), "no_param":True, "config_show": content}) else: sidebar.append({"glyph": "glyphicon-download", "text": _('Downloaded Books'), "link": 'web.books_list', @@ -69,27 +69,27 @@ def get_sidebar_config(kwargs=None): "visibility": constants.SIDEBAR_RANDOM, 'public': True, "page": "discover", "show_text": _('Show Random Books'), "config_show": True}) sidebar.append({"glyph": "glyphicon-inbox", "text": _('Categories'), "link": 'web.category_list', "id": "cat", - "visibility": constants.SIDEBAR_CATEGORY, 'public': True, "page": "category", + "visibility": constants.SIDEBAR_CATEGORY, 'public': True, "page": "category", "no_param":True, "show_text": _('Show Category Section'), "config_show": True}) sidebar.append({"glyph": "glyphicon-bookmark", "text": _('Series'), "link": 'web.series_list', "id": "serie", - "visibility": constants.SIDEBAR_SERIES, 'public': True, "page": "series", + "visibility": constants.SIDEBAR_SERIES, 'public': True, "page": "series", "no_param":True, "show_text": _('Show Series Section'), "config_show": True}) sidebar.append({"glyph": "glyphicon-user", "text": _('Authors'), "link": 'web.author_list', "id": "author", - "visibility": constants.SIDEBAR_AUTHOR, 'public': True, "page": "author", + "visibility": constants.SIDEBAR_AUTHOR, 'public': True, "page": "author", "no_param":True, "show_text": _('Show Author Section'), "config_show": True}) sidebar.append( {"glyph": "glyphicon-text-size", "text": _('Publishers'), "link": 'web.publisher_list', "id": "publisher", - "visibility": constants.SIDEBAR_PUBLISHER, 'public': True, "page": "publisher", + "visibility": constants.SIDEBAR_PUBLISHER, 'public': True, "page": "publisher", "no_param":True, "show_text": _('Show Publisher Section'), "config_show":True}) sidebar.append({"glyph": "glyphicon-flag", "text": _('Languages'), "link": 'web.language_overview', "id": "lang", "visibility": constants.SIDEBAR_LANGUAGE, 'public': (current_user.filter_language() == 'all'), - "page": "language", + "page": "language", "no_param":True, "show_text": _('Show Language Section'), "config_show": True}) sidebar.append({"glyph": "glyphicon-star-empty", "text": _('Ratings'), "link": 'web.ratings_list', "id": "rate", - "visibility": constants.SIDEBAR_RATING, 'public': True, + "visibility": constants.SIDEBAR_RATING, 'public': True, "no_param":True, "page": "rating", "show_text": _('Show Ratings Section'), "config_show": True}) sidebar.append({"glyph": "glyphicon-file", "text": _('File formats'), "link": 'web.formats_list', "id": "format", - "visibility": constants.SIDEBAR_FORMAT, 'public': True, + "visibility": constants.SIDEBAR_FORMAT, 'public': True, "no_param":True, "page": "format", "show_text": _('Show File Formats Section'), "config_show": True}) sidebar.append( {"glyph": "glyphicon-folder-open", "text": _('Archived Books'), "link": 'web.books_list', "id": "archived", @@ -98,8 +98,8 @@ def get_sidebar_config(kwargs=None): if not simple: sidebar.append( {"glyph": "glyphicon-th-list", "text": _('Books List'), "link": 'web.books_table', "id": "list", - "visibility": constants.SIDEBAR_LIST, 'public': (not current_user.is_anonymous), "page": "list", - "show_text": _('Show Books List'), "config_show": content}) + "visibility": constants.SIDEBAR_LIST, 'public': (not current_user.is_anonymous), + "show_text": _('Show Books List'), "config_show": content, "no_param":True}) g.shelves_access = ub.session.query(ub.Shelf).filter( or_(ub.Shelf.is_public == 1, ub.Shelf.user_id == current_user.id)).order_by(ub.Shelf.name).all() diff --git a/cps/reverseproxy.py b/cps/reverseproxy.py index 887590bf1..1c98c5d1a 100644 --- a/cps/reverseproxy.py +++ b/cps/reverseproxy.py @@ -61,11 +61,13 @@ class ReverseProxied(object): def __call__(self, environ, start_response): self.proxied = False + self.script_name = "/" script_name = environ.get('HTTP_X_SCRIPT_NAME', '') if script_name: self.proxied = True environ['SCRIPT_NAME'] = script_name path_info = environ.get('PATH_INFO', '') + self.script_name = script_name if path_info and path_info.startswith(script_name): environ['PATH_INFO'] = path_info[len(script_name):] diff --git a/cps/shelf.py b/cps/shelf.py index e4f93909e..e5fb46707 100644 --- a/cps/shelf.py +++ b/cps/shelf.py @@ -102,6 +102,52 @@ def add_to_shelf(shelf_id, book_id): return "", 204 +@shelf.route("/shelf/massremove/", methods=["POST"]) +@user_login_required +def search_from_shelf(shelf_id): + shelf = ub.session.query(ub.Shelf).filter(ub.Shelf.id == shelf_id).first() + if shelf is None: + log.error("Invalid shelf specified: {}".format(shelf_id)) + flash(_("Invalid shelf specified"), category="error") + return redirect(url_for('web.index')) + + if not check_shelf_edit_permissions(shelf): + log.warning("You are not allowed to remove a book from the shelf".format(shelf.name)) + flash(_("You are not allowed to remove a book from the shelf"), category="error") + return redirect(url_for('web.index')) + + if current_user.id in ub.searched_ids and ub.searched_ids[current_user.id]: + books_from_shelf = list() + books_in_shelf = ub.session.query(ub.BookShelf).filter(ub.BookShelf.shelf == shelf_id).all() + if books_in_shelf: + book_ids = [book_id.book_id for book_id in books_in_shelf] + for searchid in ub.searched_ids[current_user.id]: + if searchid in book_ids: + books_from_shelf.append(searchid) + else: + log.error("No Books are part of {}".format(shelf.name)) + flash(_("No Books are part of the shelf: %(name)s", name=shelf.name), category="error") + return redirect(url_for('web.index')) + + # maxOrder = ub.session.query(func.max(ub.BookShelf.order)).filter(ub.BookShelf.shelf == shelf_id).first()[0] or 0 + + for book in books_from_shelf: + ub.session.delete(ub.session.query(ub.BookShelf).filter(ub.BookShelf.shelf == shelf_id).filter( + ub.BookShelf.book_id == book).first()) + shelf.last_modified = datetime.now(timezone.utc) + try: + ub.session.commit() + flash(_("Books have been removed from shelf: %(sname)s", sname=shelf.name), category="success") + except (OperationalError, InvalidRequestError) as e: + ub.session.rollback() + log.error_or_exception("Settings Database error: {}".format(e)) + flash(_("Oops! Database Error: %(error)s.", error=e.orig), category="error") + else: + log.error("Could not remove books from shelf: {}".format(shelf.name)) + flash(_("Could not remove books from shelf: %(sname)s", sname=shelf.name), category="error") + return redirect(url_for('web.index')) + + @shelf.route("/shelf/massadd/", methods=["POST"]) @user_login_required def search_to_shelf(shelf_id): @@ -120,9 +166,7 @@ def search_to_shelf(shelf_id): books_for_shelf = list() books_in_shelf = ub.session.query(ub.BookShelf).filter(ub.BookShelf.shelf == shelf_id).all() if books_in_shelf: - book_ids = list() - for book_id in books_in_shelf: - book_ids.append(book_id.book_id) + book_ids = [book_id.book_id for book_id in books_in_shelf] for searchid in ub.searched_ids[current_user.id]: if searchid not in book_ids: books_for_shelf.append(searchid) diff --git a/cps/templates/layout.html b/cps/templates/layout.html index 5a0849b22..dfb347ae3 100644 --- a/cps/templates/layout.html +++ b/cps/templates/layout.html @@ -147,7 +147,7 @@ {% for element in sidebar %} {% if current_user.check_visibility(element['visibility']) and element['public'] %} - + {% endif %} {% endfor %} {% if current_user.is_authenticated or g.allow_anonymous %} diff --git a/cps/templates/search.html b/cps/templates/search.html index b406ebe3f..803c7dd47 100644 --- a/cps/templates/search.html +++ b/cps/templates/search.html @@ -24,6 +24,20 @@ {%endfor%} +
+ + +
+ {% endif %} {% endif %}