diff --git a/cps.py b/cps.py index 737b0d97..46eb1440 100755 --- a/cps.py +++ b/cps.py @@ -70,7 +70,6 @@ def main(): app.register_blueprint(shelf) app.register_blueprint(admi) app.register_blueprint(remotelogin) - # if config.config_use_google_drive: app.register_blueprint(gdrive) app.register_blueprint(editbook) if kobo_available: diff --git a/cps/__init__.py b/cps/__init__.py index 9b004640..3b100907 100644 --- a/cps/__init__.py +++ b/cps/__init__.py @@ -37,6 +37,11 @@ from . import config_sql, logger, cache_buster, cli, ub, db from .reverseproxy import ReverseProxied from .server import WebServer +try: + import lxml + lxml_present = True +except ImportError: + lxml_present = False mimetypes.init() mimetypes.add_type('application/xhtml+xml', '.xhtml') @@ -90,6 +95,16 @@ db.CalibreDB.setup_db(config.config_calibre_dir, cli.settingspath) calibre_db = db.CalibreDB() def create_app(): + if sys.version_info < (3, 0): + log.info( + '*** Python2 is EOL since end of 2019, this version of Calibre-Web is no longer supporting Python2, please update your installation to Python3 ***') + print( + '*** Python2 is EOL since end of 2019, this version of Calibre-Web is no longer supporting Python2, please update your installation to Python3 ***') + sys.exit(5) + if not lxml_present: + log.info('*** "lxml" is needed for calibre-web to run. Please install it using pip: "pip install lxml" ***') + print('*** "lxml" is needed for calibre-web to run. Please install it using pip: "pip install lxml" ***') + sys.exit(6) app.wsgi_app = ReverseProxied(app.wsgi_app) # For python2 convert path to unicode if sys.version_info < (3, 0): @@ -99,12 +114,8 @@ def create_app(): if os.environ.get('FLASK_DEBUG'): cache_buster.init_cache_busting(app) - log.info('Starting Calibre Web...') - if sys.version_info < (3, 0): - log.info('*** Python2 is EOL since end of 2019, this version of Calibre-Web is no longer supporting Python2, please update your installation to Python3 ***') - print('*** Python2 is EOL since end of 2019, this version of Calibre-Web is no longer supporting Python2, please update your installation to Python3 ***') - sys.exit(5) + Principal(app) lm.init_app(app) app.secret_key = os.getenv('SECRET_KEY', config_sql.get_flask_session_key(ub.session)) diff --git a/cps/editbooks.py b/cps/editbooks.py index afc07513..d8e16e4c 100644 --- a/cps/editbooks.py +++ b/cps/editbooks.py @@ -26,7 +26,11 @@ from datetime import datetime import json from shutil import copyfile from uuid import uuid4 -from lxml.html.clean import clean_html +try: + from lxml.html.clean import clean_html +except ImportError: + pass + # Improve this to check if scholarly is available in a global way, like other pythonic libraries try: diff --git a/cps/helper.py b/cps/helper.py index b6ea6760..8c0f1656 100644 --- a/cps/helper.py +++ b/cps/helper.py @@ -38,6 +38,7 @@ from flask_login import current_user from sqlalchemy.sql.expression import true, false, and_, text, func from werkzeug.datastructures import Headers from werkzeug.security import generate_password_hash +from markupsafe import escape try: from urllib.parse import quote @@ -97,10 +98,11 @@ def convert_book_format(book_id, calibrepath, old_book_format, new_book_format, settings['body'] = _(u'This e-mail has been sent via Calibre-Web.') else: settings = dict() - txt = (u"%s -> %s: %s" % ( + link = '{}"'.format(url_for('web.show_book', book_id=book.id), escape(book.title)) # prevent xss + txt = u"{} -> {}: {}".format( old_book_format, new_book_format, - "" + book.title + "")) + link) settings['old_book_format'] = old_book_format settings['new_book_format'] = new_book_format WorkerThread.add(user_id, TaskConvert(file_path, book.id, txt, settings, kindle_mail, user_id)) @@ -778,7 +780,7 @@ def render_task_status(tasklist): ret['taskMessage'] = "{}: {}".format(_(task.name), task.message) ret['progress'] = "{} %".format(int(task.progress * 100)) - ret['user'] = user + ret['user'] = escape(user) # prevent xss renderedtasklist.append(ret) return renderedtasklist diff --git a/cps/jinjia.py b/cps/jinjia.py index 2d18c6f0..554bc791 100644 --- a/cps/jinjia.py +++ b/cps/jinjia.py @@ -28,7 +28,6 @@ import mimetypes from uuid import uuid4 from babel.dates import format_date -from flask_babel import gettext as _ from flask import Blueprint, request, url_for from flask_babel import get_locale from flask_login import current_user diff --git a/cps/static/js/main.js b/cps/static/js/main.js index fe8b498d..d8641c3e 100644 --- a/cps/static/js/main.js +++ b/cps/static/js/main.js @@ -20,6 +20,15 @@ function getPath() { var jsFileLocation = $("script[src*=jquery]").attr("src"); // the js file path return jsFileLocation.substr(0, jsFileLocation.search("/static/js/libs/jquery.min.js")); // the js folder path } + +function elementSorter(a, b) { + a = +a.slice(0, -2); + b = +b.slice(0, -2); + if (a > b) return 1; + if (a < b) return -1; + return 0; +} + // Generic control/related handler to show/hide fields based on a checkbox' value // e.g. // @@ -714,3 +723,4 @@ $(function() { }); }); }); + diff --git a/cps/static/js/table.js b/cps/static/js/table.js index 9d5c4fa7..60d1a957 100644 --- a/cps/static/js/table.js +++ b/cps/static/js/table.js @@ -22,6 +22,26 @@ var selections = []; var reload = false; $(function() { + $('#tasktable').bootstrapTable({ + formatNoMatches: function () { + return ''; + }, + striped: true + }); + if ($('#tasktable').length) { + setInterval(function () { + $.ajax({ + method: "get", + url: getPath() + "/ajax/emailstat", + async: true, + timeout: 900, + success: function (data) { + $('#table').bootstrapTable("load", data); + } + }); + }, 2000); + } + $("#books-table").on("check.bs.table check-all.bs.table uncheck.bs.table uncheck-all.bs.table", function (e, rowsAfter, rowsBefore) { var rows = rowsAfter; diff --git a/cps/templates/tasks.html b/cps/templates/tasks.html index 4a755c82..76f23dfe 100644 --- a/cps/templates/tasks.html +++ b/cps/templates/tasks.html @@ -5,7 +5,7 @@ {% block body %}

{{_('Tasks')}}

- +
{% if g.user.role_admin() %} @@ -27,32 +27,9 @@ {% endblock %} {% block js %} - + + {% endblock %} diff --git a/cps/web.py b/cps/web.py index f7291e0a..bf95be50 100644 --- a/cps/web.py +++ b/cps/web.py @@ -84,7 +84,7 @@ except ImportError: @app.after_request def add_security_headers(resp): - # resp.headers['Content-Security-Policy']= "script-src 'self' https://www.googleapis.com https://api.douban.com https://comicvine.gamespot.com;" + resp.headers['Content-Security-Policy']= "script-src 'self'" # https://www.googleapis.com https://api.douban.com https://comicvine.gamespot.com;" resp.headers['X-Content-Type-Options'] = 'nosniff' resp.headers['X-Frame-Options'] = 'SAMEORIGIN' resp.headers['X-XSS-Protection'] = '1; mode=block'