|
|
|
#!/usr/bin/env python
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
|
|
# This file is part of the Calibre-Web (https://github.com/janeczku/calibre-web)
|
|
|
|
# Copyright (C) 2018-2019 OzzieIsaacs, cervinko, jkrehm, bodybybuddha, ok11,
|
|
|
|
# andy29485, idalin, Kyosfonica, wuqi, Kennyl, lemmsh,
|
|
|
|
# falgh1, grunjol, csitko, ytils, xybydy, trasba, vrabe,
|
|
|
|
# ruben-herold, marblepebble, JackED42, SiphonSquirrel,
|
|
|
|
# apetresc, nanu-c, mutschler
|
|
|
|
#
|
|
|
|
# This program is free software: you can redistribute it and/or modify
|
|
|
|
# it under the terms of the GNU General Public License as published by
|
|
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
|
|
# (at your option) any later version.
|
|
|
|
#
|
|
|
|
# This program is distributed in the hope that it will be useful,
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
# GNU General Public License for more details.
|
|
|
|
#
|
|
|
|
# You should have received a copy of the GNU General Public License
|
|
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
import os
|
|
|
|
from flask import Blueprint, flash, redirect, url_for
|
|
|
|
from flask import abort, request, make_response
|
|
|
|
from flask_login import login_required, current_user, logout_user
|
|
|
|
from web import admin_required, render_title_template, before_request, unconfigured, \
|
|
|
|
login_required_if_no_ano
|
|
|
|
from cps import db, ub, Server, get_locale, config, app, updater_thread, babel
|
|
|
|
import json
|
|
|
|
from datetime import datetime, timedelta
|
|
|
|
import time
|
|
|
|
from babel.dates import format_datetime
|
|
|
|
from flask_babel import gettext as _
|
|
|
|
from babel import Locale as LC
|
|
|
|
from sqlalchemy.exc import IntegrityError
|
|
|
|
from gdriveutils import is_gdrive_ready, gdrive_support, downloadFile, deleteDatabaseOnChange, listRootFolders
|
|
|
|
import helper
|
|
|
|
from helper import speaking_language, check_valid_domain
|
|
|
|
from werkzeug.security import generate_password_hash
|
|
|
|
try:
|
|
|
|
from imp import reload
|
|
|
|
except ImportError:
|
|
|
|
pass
|
|
|
|
|
|
|
|
feature_support = dict()
|
|
|
|
try:
|
|
|
|
from goodreads.client import GoodreadsClient
|
|
|
|
feature_support['goodreads'] = True
|
|
|
|
except ImportError:
|
|
|
|
feature_support['goodreads'] = False
|
|
|
|
|
|
|
|
try:
|
|
|
|
import rarfile
|
|
|
|
feature_support['rar'] = True
|
|
|
|
except ImportError:
|
|
|
|
feature_support['rar'] = False
|
|
|
|
|
|
|
|
try:
|
|
|
|
import ldap
|
|
|
|
feature_support['ldap'] = True
|
|
|
|
except ImportError:
|
|
|
|
feature_support['ldap'] = False
|
|
|
|
|
|
|
|
try:
|
|
|
|
from oauth_bb import oauth_check
|
|
|
|
feature_support['oauth'] = True
|
|
|
|
except ImportError:
|
|
|
|
feature_support['oauth'] = False
|
|
|
|
oauth_check = {}
|
|
|
|
|
|
|
|
feature_support['gdrive'] = gdrive_support
|
|
|
|
admi = Blueprint('admin', __name__)
|
|
|
|
|
|
|
|
|
|
|
|
@admi.route("/admin")
|
|
|
|
@login_required
|
|
|
|
def admin_forbidden():
|
|
|
|
abort(403)
|
|
|
|
|
|
|
|
|
|
|
|
@admi.route("/shutdown")
|
|
|
|
@login_required
|
|
|
|
@admin_required
|
|
|
|
def shutdown():
|
|
|
|
task = int(request.args.get("parameter").strip())
|
|
|
|
if task == 1 or task == 0: # valid commandos received
|
|
|
|
# close all database connections
|
|
|
|
db.session.close()
|
|
|
|
db.engine.dispose()
|
|
|
|
ub.session.close()
|
|
|
|
ub.engine.dispose()
|
|
|
|
|
|
|
|
showtext = {}
|
|
|
|
if task == 0:
|
|
|
|
showtext['text'] = _(u'Server restarted, please reload page')
|
|
|
|
Server.setRestartTyp(True)
|
|
|
|
else:
|
|
|
|
showtext['text'] = _(u'Performing shutdown of server, please close window')
|
|
|
|
Server.setRestartTyp(False)
|
|
|
|
# stop gevent/tornado server
|
|
|
|
Server.stopServer()
|
|
|
|
return json.dumps(showtext)
|
|
|
|
else:
|
|
|
|
if task == 2:
|
|
|
|
db.session.close()
|
|
|
|
db.engine.dispose()
|
|
|
|
db.setup_db()
|
|
|
|
return json.dumps({})
|
|
|
|
abort(404)
|
|
|
|
|
|
|
|
|
|
|
|
@admi.route("/admin/view")
|
|
|
|
@login_required
|
|
|
|
@admin_required
|
|
|
|
def admin():
|
|
|
|
version = updater_thread.get_current_version_info()
|
|
|
|
if version is False:
|
|
|
|
commit = _(u'Unknown')
|
|
|
|
else:
|
|
|
|
if 'datetime' in version:
|
|
|
|
commit = version['datetime']
|
|
|
|
|
|
|
|
tz = timedelta(seconds=time.timezone if (time.localtime().tm_isdst == 0) else time.altzone)
|
|
|
|
form_date = datetime.strptime(commit[:19], "%Y-%m-%dT%H:%M:%S")
|
|
|
|
if len(commit) > 19: # check if string has timezone
|
|
|
|
if commit[19] == '+':
|
|
|
|
form_date -= timedelta(hours=int(commit[20:22]), minutes=int(commit[23:]))
|
|
|
|
elif commit[19] == '-':
|
|
|
|
form_date += timedelta(hours=int(commit[20:22]), minutes=int(commit[23:]))
|
|
|
|
commit = format_datetime(form_date - tz, format='short', locale=get_locale())
|
|
|
|
else:
|
|
|
|
commit = version['version']
|
|
|
|
|
|
|
|
allUser = ub.session.query(ub.User).all()
|
|
|
|
settings = ub.session.query(ub.Settings).first()
|
|
|
|
return render_title_template("admin.html", allUser=allUser, email=settings, config=config, commit=commit,
|
|
|
|
title=_(u"Admin page"), page="admin")
|
|
|
|
|
|
|
|
|
|
|
|
@admi.route("/admin/config", methods=["GET", "POST"])
|
|
|
|
@login_required
|
|
|
|
@admin_required
|
|
|
|
def configuration():
|
|
|
|
return configuration_helper(0)
|
|
|
|
|
|
|
|
|
|
|
|
@admi.route("/admin/viewconfig", methods=["GET", "POST"])
|
|
|
|
@login_required
|
|
|
|
@admin_required
|
|
|
|
def view_configuration():
|
|
|
|
reboot_required = False
|
|
|
|
if request.method == "POST":
|
|
|
|
to_save = request.form.to_dict()
|
|
|
|
content = ub.session.query(ub.Settings).first()
|
|
|
|
if "config_calibre_web_title" in to_save:
|
|
|
|
content.config_calibre_web_title = to_save["config_calibre_web_title"]
|
|
|
|
if "config_columns_to_ignore" in to_save:
|
|
|
|
content.config_columns_to_ignore = to_save["config_columns_to_ignore"]
|
|
|
|
if "config_read_column" in to_save:
|
|
|
|
content.config_read_column = int(to_save["config_read_column"])
|
|
|
|
if "config_theme" in to_save:
|
|
|
|
content.config_theme = int(to_save["config_theme"])
|
|
|
|
if "config_title_regex" in to_save:
|
|
|
|
if content.config_title_regex != to_save["config_title_regex"]:
|
|
|
|
content.config_title_regex = to_save["config_title_regex"]
|
|
|
|
reboot_required = True
|
|
|
|
if "config_random_books" in to_save:
|
|
|
|
content.config_random_books = int(to_save["config_random_books"])
|
|
|
|
if "config_books_per_page" in to_save:
|
|
|
|
content.config_books_per_page = int(to_save["config_books_per_page"])
|
|
|
|
# Mature Content configuration
|
|
|
|
if "config_mature_content_tags" in to_save:
|
|
|
|
content.config_mature_content_tags = to_save["config_mature_content_tags"].strip()
|
|
|
|
if "Show_mature_content" in to_save:
|
|
|
|
content.config_default_show = content.config_default_show + ub.MATURE_CONTENT
|
|
|
|
|
|
|
|
if "config_authors_max" in to_save:
|
|
|
|
content.config_authors_max = int(to_save["config_authors_max"])
|
|
|
|
|
|
|
|
# Default user configuration
|
|
|
|
content.config_default_role = 0
|
|
|
|
if "admin_role" in to_save:
|
|
|
|
content.config_default_role = content.config_default_role + ub.ROLE_ADMIN
|
|
|
|
if "download_role" in to_save:
|
|
|
|
content.config_default_role = content.config_default_role + ub.ROLE_DOWNLOAD
|
|
|
|
if "upload_role" in to_save:
|
|
|
|
content.config_default_role = content.config_default_role + ub.ROLE_UPLOAD
|
|
|
|
if "edit_role" in to_save:
|
|
|
|
content.config_default_role = content.config_default_role + ub.ROLE_EDIT
|
|
|
|
if "delete_role" in to_save:
|
|
|
|
content.config_default_role = content.config_default_role + ub.ROLE_DELETE_BOOKS
|
|
|
|
if "passwd_role" in to_save:
|
|
|
|
content.config_default_role = content.config_default_role + ub.ROLE_PASSWD
|
|
|
|
if "edit_shelf_role" in to_save:
|
|
|
|
content.config_default_role = content.config_default_role + ub.ROLE_EDIT_SHELFS
|
|
|
|
|
|
|
|
val = 0
|
|
|
|
for key,v in to_save.items():
|
|
|
|
if key.startswith('show'):
|
|
|
|
val += int(key[5:])
|
|
|
|
content.config_default_show = val
|
|
|
|
|
|
|
|
ub.session.commit()
|
|
|
|
flash(_(u"Calibre-Web configuration updated"), category="success")
|
|
|
|
config.loadSettings()
|
|
|
|
before_request()
|
|
|
|
if reboot_required:
|
|
|
|
# db.engine.dispose() # ToDo verify correct
|
|
|
|
# ub.session.close()
|
|
|
|
# ub.engine.dispose()
|
|
|
|
# stop Server
|
|
|
|
Server.setRestartTyp(True)
|
|
|
|
Server.stopServer()
|
|
|
|
app.logger.info('Reboot required, restarting')
|
|
|
|
readColumn = db.session.query(db.Custom_Columns)\
|
|
|
|
.filter(db.and_(db.Custom_Columns.datatype == 'bool',db.Custom_Columns.mark_for_delete == 0)).all()
|
|
|
|
return render_title_template("config_view_edit.html", conf=config, readColumns=readColumn,
|
|
|
|
title=_(u"UI Configuration"), page="uiconfig")
|
|
|
|
|
|
|
|
|
|
|
|
@admi.route("/ajax/editdomain", methods=['POST'])
|
|
|
|
@login_required
|
|
|
|
@admin_required
|
|
|
|
def edit_domain():
|
|
|
|
# POST /post
|
|
|
|
# name: 'username', //name of field (column in db)
|
|
|
|
# pk: 1 //primary key (record id)
|
|
|
|
# value: 'superuser!' //new value
|
|
|
|
vals = request.form.to_dict()
|
|
|
|
answer = ub.session.query(ub.Registration).filter(ub.Registration.id == vals['pk']).first()
|
|
|
|
# domain_name = request.args.get('domain')
|
|
|
|
answer.domain = vals['value'].replace('*', '%').replace('?', '_').lower()
|
|
|
|
ub.session.commit()
|
|
|
|
return ""
|
|
|
|
|
|
|
|
|
|
|
|
@admi.route("/ajax/adddomain", methods=['POST'])
|
|
|
|
@login_required
|
|
|
|
@admin_required
|
|
|
|
def add_domain():
|
|
|
|
domain_name = request.form.to_dict()['domainname'].replace('*', '%').replace('?', '_').lower()
|
|
|
|
check = ub.session.query(ub.Registration).filter(ub.Registration.domain == domain_name).first()
|
|
|
|
if not check:
|
|
|
|
new_domain = ub.Registration(domain=domain_name)
|
|
|
|
ub.session.add(new_domain)
|
|
|
|
ub.session.commit()
|
|
|
|
return ""
|
|
|
|
|
|
|
|
|
|
|
|
@admi.route("/ajax/deletedomain", methods=['POST'])
|
|
|
|
@login_required
|
|
|
|
@admin_required
|
|
|
|
def delete_domain():
|
|
|
|
domain_id = request.form.to_dict()['domainid'].replace('*', '%').replace('?', '_').lower()
|
|
|
|
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="%.%")
|
|
|
|
ub.session.add(new_domain)
|
|
|
|
ub.session.commit()
|
|
|
|
return ""
|
|
|
|
|
|
|
|
|
|
|
|
@admi.route("/ajax/domainlist")
|
|
|
|
@login_required
|
|
|
|
@admin_required
|
|
|
|
def list_domain():
|
|
|
|
answer = ub.session.query(ub.Registration).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("'", '"'))
|
|
|
|
response.headers["Content-Type"] = "application/json; charset=utf-8"
|
|
|
|
return response
|
|
|
|
|
|
|
|
|
|
|
|
@admi.route("/config", methods=["GET", "POST"])
|
|
|
|
@unconfigured
|
|
|
|
def basic_configuration():
|
|
|
|
logout_user()
|
|
|
|
return configuration_helper(1)
|
|
|
|
|
|
|
|
|
|
|
|
def configuration_helper(origin):
|
|
|
|
reboot_required = False
|
|
|
|
gdriveError = None
|
|
|
|
db_change = False
|
|
|
|
success = False
|
|
|
|
filedata = None
|
|
|
|
if not feature_support['gdrive']:
|
|
|
|
gdriveError = _('Import of optional Google Drive requirements missing')
|
|
|
|
else:
|
|
|
|
if not os.path.isfile(os.path.join(config.get_main_dir, 'client_secrets.json')):
|
|
|
|
gdriveError = _('client_secrets.json is missing or not readable')
|
|
|
|
else:
|
|
|
|
with open(os.path.join(config.get_main_dir, 'client_secrets.json'), 'r') as settings:
|
|
|
|
filedata = json.load(settings)
|
|
|
|
if 'web' not in filedata:
|
|
|
|
gdriveError = _('client_secrets.json is not configured for web application')
|
|
|
|
if request.method == "POST":
|
|
|
|
to_save = request.form.to_dict()
|
|
|
|
content = ub.session.query(ub.Settings).first() # type: ub.Settings
|
|
|
|
if "config_calibre_dir" in to_save:
|
|
|
|
if content.config_calibre_dir != to_save["config_calibre_dir"]:
|
|
|
|
content.config_calibre_dir = to_save["config_calibre_dir"]
|
|
|
|
db_change = True
|
|
|
|
# Google drive setup
|
|
|
|
if not os.path.isfile(os.path.join(config.get_main_dir, 'settings.yaml')):
|
|
|
|
content.config_use_google_drive = False
|
|
|
|
if "config_use_google_drive" in to_save and not content.config_use_google_drive and not gdriveError:
|
|
|
|
if filedata:
|
|
|
|
if filedata['web']['redirect_uris'][0].endswith('/'):
|
|
|
|
filedata['web']['redirect_uris'][0] = filedata['web']['redirect_uris'][0][:-1]
|
|
|
|
with open(os.path.join(config.get_main_dir, 'settings.yaml'), 'w') as f:
|
|
|
|
yaml = "client_config_backend: settings\nclient_config_file: %(client_file)s\n" \
|
|
|
|
"client_config:\n" \
|
|
|
|
" client_id: %(client_id)s\n client_secret: %(client_secret)s\n" \
|
|
|
|
" redirect_uri: %(redirect_uri)s\n\nsave_credentials: True\n" \
|
|
|
|
"save_credentials_backend: file\nsave_credentials_file: %(credential)s\n\n" \
|
|
|
|
"get_refresh_token: True\n\noauth_scope:\n" \
|
|
|
|
" - https://www.googleapis.com/auth/drive\n"
|
|
|
|
f.write(yaml % {'client_file': os.path.join(config.get_main_dir, 'client_secrets.json'),
|
|
|
|
'client_id': filedata['web']['client_id'],
|
|
|
|
'client_secret': filedata['web']['client_secret'],
|
|
|
|
'redirect_uri': filedata['web']['redirect_uris'][0],
|
|
|
|
'credential': os.path.join(config.get_main_dir, 'gdrive_credentials')})
|
|
|
|
else:
|
|
|
|
flash(_(u'client_secrets.json is not configured for web application'), category="error")
|
|
|
|
return render_title_template("config_edit.html", config=config, origin=origin,
|
|
|
|
gdriveError=gdriveError,
|
|
|
|
gfeature_support=feature_support, title=_(u"Basic Configuration"),
|
|
|
|
page="config")
|
|
|
|
# always show google drive settings, but in case of error deny support
|
|
|
|
if "config_use_google_drive" in to_save and not gdriveError:
|
|
|
|
content.config_use_google_drive = "config_use_google_drive" in to_save
|
|
|
|
else:
|
|
|
|
content.config_use_google_drive = 0
|
|
|
|
if "config_google_drive_folder" in to_save:
|
|
|
|
if content.config_google_drive_folder != to_save["config_google_drive_folder"]:
|
|
|
|
content.config_google_drive_folder = to_save["config_google_drive_folder"]
|
|
|
|
deleteDatabaseOnChange()
|
|
|
|
|
|
|
|
if "config_port" in to_save:
|
|
|
|
if content.config_port != int(to_save["config_port"]):
|
|
|
|
content.config_port = int(to_save["config_port"])
|
|
|
|
reboot_required = True
|
|
|
|
if "config_keyfile" in to_save:
|
|
|
|
if content.config_keyfile != to_save["config_keyfile"]:
|
|
|
|
if os.path.isfile(to_save["config_keyfile"]) or to_save["config_keyfile"] is u"":
|
|
|
|
content.config_keyfile = to_save["config_keyfile"]
|
|
|
|
reboot_required = True
|
|
|
|
else:
|
|
|
|
ub.session.commit()
|
|
|
|
flash(_(u'Keyfile location is not valid, please enter correct path'), category="error")
|
|
|
|
return render_title_template("config_edit.html", config=config, origin=origin,
|
|
|
|
gdriveError=gdriveError,
|
|
|
|
feature_support=feature_support, title=_(u"Basic Configuration"),
|
|
|
|
page="config")
|
|
|
|
if "config_certfile" in to_save:
|
|
|
|
if content.config_certfile != to_save["config_certfile"]:
|
|
|
|
if os.path.isfile(to_save["config_certfile"]) or to_save["config_certfile"] is u"":
|
|
|
|
content.config_certfile = to_save["config_certfile"]
|
|
|
|
reboot_required = True
|
|
|
|
else:
|
|
|
|
ub.session.commit()
|
|
|
|
flash(_(u'Certfile location is not valid, please enter correct path'), category="error")
|
|
|
|
return render_title_template("config_edit.html", config=config, origin=origin,
|
|
|
|
gdriveError=gdriveError, feature_support=feature_support,
|
|
|
|
title=_(u"Basic Configuration"), page="config")
|
|
|
|
content.config_uploading = 0
|
|
|
|
content.config_anonbrowse = 0
|
|
|
|
content.config_public_reg = 0
|
|
|
|
if "config_uploading" in to_save and to_save["config_uploading"] == "on":
|
|
|
|
content.config_uploading = 1
|
|
|
|
if "config_anonbrowse" in to_save and to_save["config_anonbrowse"] == "on":
|
|
|
|
content.config_anonbrowse = 1
|
|
|
|
if "config_public_reg" in to_save and to_save["config_public_reg"] == "on":
|
|
|
|
content.config_public_reg = 1
|
|
|
|
|
|
|
|
if "config_converterpath" in to_save:
|
|
|
|
content.config_converterpath = to_save["config_converterpath"].strip()
|
|
|
|
if "config_calibre" in to_save:
|
|
|
|
content.config_calibre = to_save["config_calibre"].strip()
|
|
|
|
if "config_ebookconverter" in to_save:
|
|
|
|
content.config_ebookconverter = int(to_save["config_ebookconverter"])
|
|
|
|
|
|
|
|
#LDAP configurator,
|
|
|
|
if "config_login_type" in to_save and to_save["config_login_type"] == "1":
|
|
|
|
if to_save["config_ldap_provider_url"] == u'' or to_save["config_ldap_dn"] == u'':
|
|
|
|
ub.session.commit()
|
|
|
|
flash(_(u'Please enter a LDAP provider and a DN'), category="error")
|
|
|
|
return render_title_template("config_edit.html", config=config, origin=origin,
|
|
|
|
gdriveError=gdriveError, feature_support=feature_support,
|
|
|
|
title=_(u"Basic Configuration"), page="config")
|
|
|
|
else:
|
|
|
|
content.config_login_type = ub.LOGIN_LDAP
|
|
|
|
content.config_ldap_provider_url = to_save["config_ldap_provider_url"]
|
|
|
|
content.config_ldap_dn = to_save["config_ldap_dn"]
|
|
|
|
db_change = True
|
|
|
|
|
|
|
|
# Remote login configuration
|
|
|
|
content.config_remote_login = ("config_remote_login" in to_save and to_save["config_remote_login"] == "on")
|
|
|
|
if not content.config_remote_login:
|
|
|
|
ub.session.query(ub.RemoteAuthToken).delete()
|
|
|
|
|
|
|
|
# Goodreads configuration
|
|
|
|
content.config_use_goodreads = ("config_use_goodreads" in to_save and to_save["config_use_goodreads"] == "on")
|
|
|
|
if "config_goodreads_api_key" in to_save:
|
|
|
|
content.config_goodreads_api_key = to_save["config_goodreads_api_key"]
|
|
|
|
if "config_goodreads_api_secret" in to_save:
|
|
|
|
content.config_goodreads_api_secret = to_save["config_goodreads_api_secret"]
|
|
|
|
if "config_updater" in to_save:
|
|
|
|
content.config_updatechannel = int(to_save["config_updater"])
|
|
|
|
|
|
|
|
# GitHub OAuth configuration
|
|
|
|
if "config_login_type" in to_save and to_save["config_login_type"] == "2":
|
|
|
|
if to_save["config_github_oauth_client_id"] == u'' or to_save["config_github_oauth_client_secret"] == u'':
|
|
|
|
ub.session.commit()
|
|
|
|
flash(_(u'Please enter Github oauth credentials'), category="error")
|
|
|
|
return render_title_template("config_edit.html", config=config, origin=origin,
|
|
|
|
gdriveError=gdriveError, feature_support=feature_support,
|
|
|
|
title=_(u"Basic Configuration"), page="config")
|
|
|
|
else:
|
|
|
|
content.config_login_type = ub.LOGIN_OAUTH_GITHUB
|
|
|
|
content.config_github_oauth_client_id = to_save["config_github_oauth_client_id"]
|
|
|
|
content.config_github_oauth_client_secret = to_save["config_github_oauth_client_secret"]
|
|
|
|
reboot_required = True
|
|
|
|
|
|
|
|
# Google OAuth configuration
|
|
|
|
if "config_login_type" in to_save and to_save["config_login_type"] == "3":
|
|
|
|
if to_save["config_google_oauth_client_id"] == u'' or to_save["config_google_oauth_client_secret"] == u'':
|
|
|
|
ub.session.commit()
|
|
|
|
flash(_(u'Please enter Google oauth credentials'), category="error")
|
|
|
|
return render_title_template("config_edit.html", config=config, origin=origin,
|
|
|
|
gdriveError=gdriveError, feature_support=feature_support,
|
|
|
|
title=_(u"Basic Configuration"), page="config")
|
|
|
|
else:
|
|
|
|
content.config_login_type = ub.LOGIN_OAUTH_GOOGLE
|
|
|
|
content.config_google_oauth_client_id = to_save["config_google_oauth_client_id"]
|
|
|
|
content.config_google_oauth_client_secret = to_save["config_google_oauth_client_secret"]
|
|
|
|
reboot_required = True
|
|
|
|
|
|
|
|
if "config_login_type" in to_save and to_save["config_login_type"] == "0":
|
|
|
|
content.config_login_type = ub.LOGIN_STANDARD
|
|
|
|
|
|
|
|
if "config_log_level" in to_save:
|
|
|
|
content.config_log_level = int(to_save["config_log_level"])
|
|
|
|
if content.config_logfile != to_save["config_logfile"]:
|
|
|
|
# check valid path, only path or file
|
|
|
|
if os.path.dirname(to_save["config_logfile"]):
|
|
|
|
if os.path.exists(os.path.dirname(to_save["config_logfile"])) and \
|
|
|
|
os.path.basename(to_save["config_logfile"]) and not os.path.isdir(to_save["config_logfile"]):
|
|
|
|
content.config_logfile = to_save["config_logfile"]
|
|
|
|
else:
|
|
|
|
ub.session.commit()
|
|
|
|
flash(_(u'Logfile location is not valid, please enter correct path'), category="error")
|
|
|
|
return render_title_template("config_edit.html", config=config, origin=origin,
|
|
|
|
gdriveError=gdriveError, feature_support=feature_support,
|
|
|
|
title=_(u"Basic Configuration"), page="config")
|
|
|
|
else:
|
|
|
|
content.config_logfile = to_save["config_logfile"]
|
|
|
|
reboot_required = True
|
|
|
|
|
|
|
|
# Rarfile Content configuration
|
|
|
|
if "config_rarfile_location" in to_save and to_save['config_rarfile_location'] is not u"":
|
|
|
|
check = helper.check_unrar(to_save["config_rarfile_location"].strip())
|
|
|
|
if not check[0] :
|
|
|
|
content.config_rarfile_location = to_save["config_rarfile_location"].strip()
|
|
|
|
else:
|
|
|
|
flash(check[1], category="error")
|
|
|
|
return render_title_template("config_edit.html", config=config, origin=origin,
|
|
|
|
feature_support=feature_support, title=_(u"Basic Configuration"))
|
|
|
|
try:
|
|
|
|
if content.config_use_google_drive and is_gdrive_ready() and not \
|
|
|
|
os.path.exists(os.path.join(content.config_calibre_dir, "metadata.db")):
|
|
|
|
downloadFile(None, "metadata.db", config.config_calibre_dir + "/metadata.db")
|
|
|
|
if db_change:
|
|
|
|
if config.db_configured:
|
|
|
|
db.session.close()
|
|
|
|
db.engine.dispose()
|
|
|
|
ub.session.commit()
|
|
|
|
flash(_(u"Calibre-Web configuration updated"), category="success")
|
|
|
|
config.loadSettings()
|
|
|
|
app.logger.setLevel(config.config_log_level)
|
|
|
|
# logging.getLogger("uploader").setLevel(config.config_log_level)
|
|
|
|
except Exception as e:
|
|
|
|
flash(e, category="error")
|
|
|
|
return render_title_template("config_edit.html", config=config, origin=origin,
|
|
|
|
gdriveError=gdriveError, feature_support=feature_support,
|
|
|
|
title=_(u"Basic Configuration"), page="config")
|
|
|
|
if db_change:
|
|
|
|
reload(db)
|
|
|
|
if not db.setup_db():
|
|
|
|
flash(_(u'DB location is not valid, please enter correct path'), category="error")
|
|
|
|
return render_title_template("config_edit.html", config=config, origin=origin,
|
|
|
|
gdriveError=gdriveError, feature_support=feature_support,
|
|
|
|
title=_(u"Basic Configuration"), page="config")
|
|
|
|
if reboot_required:
|
|
|
|
# stop Server
|
|
|
|
Server.setRestartTyp(True)
|
|
|
|
Server.stopServer()
|
|
|
|
app.logger.info('Reboot required, restarting')
|
|
|
|
if origin:
|
|
|
|
success = True
|
|
|
|
if is_gdrive_ready() and feature_support['gdrive'] is True: # and config.config_use_google_drive == True:
|
|
|
|
gdrivefolders = listRootFolders()
|
|
|
|
else:
|
|
|
|
gdrivefolders = list()
|
|
|
|
return render_title_template("config_edit.html", origin=origin, success=success, config=config,
|
|
|
|
show_authenticate_google_drive=not is_gdrive_ready(),
|
|
|
|
gdriveError=gdriveError, gdrivefolders=gdrivefolders, feature_support=feature_support,
|
|
|
|
title=_(u"Basic Configuration"), page="config")
|
|
|
|
|
|
|
|
|
|
|
|
@admi.route("/admin/user/new", methods=["GET", "POST"])
|
|
|
|
@login_required
|
|
|
|
@admin_required
|
|
|
|
def new_user():
|
|
|
|
content = ub.User()
|
|
|
|
languages = speaking_language()
|
|
|
|
translations = [LC('en')] + babel.list_translations()
|
|
|
|
if request.method == "POST":
|
|
|
|
to_save = request.form.to_dict()
|
|
|
|
content.default_language = to_save["default_language"]
|
|
|
|
content.mature_content = "Show_mature_content" in to_save
|
|
|
|
if "locale" in to_save:
|
|
|
|
content.locale = to_save["locale"]
|
|
|
|
|
|
|
|
val = 0
|
|
|
|
for key, __ in to_save.items():
|
|
|
|
if key.startswith('show'):
|
|
|
|
val += int(key[5:])
|
|
|
|
content.sidebar_view = val
|
|
|
|
|
|
|
|
if "show_detail_random" in to_save:
|
|
|
|
content.sidebar_view += ub.DETAIL_RANDOM
|
|
|
|
|
|
|
|
content.role = 0
|
|
|
|
if "admin_role" in to_save:
|
|
|
|
content.role = content.role + ub.ROLE_ADMIN
|
|
|
|
if "download_role" in to_save:
|
|
|
|
content.role = content.role + ub.ROLE_DOWNLOAD
|
|
|
|
if "upload_role" in to_save:
|
|
|
|
content.role = content.role + ub.ROLE_UPLOAD
|
|
|
|
if "edit_role" in to_save:
|
|
|
|
content.role = content.role + ub.ROLE_EDIT
|
|
|
|
if "delete_role" in to_save:
|
|
|
|
content.role = content.role + ub.ROLE_DELETE_BOOKS
|
|
|
|
if "passwd_role" in to_save:
|
|
|
|
content.role = content.role + ub.ROLE_PASSWD
|
|
|
|
if "edit_shelf_role" in to_save:
|
|
|
|
content.role = content.role + ub.ROLE_EDIT_SHELFS
|
|
|
|
if not to_save["nickname"] or not to_save["email"] or not to_save["password"]:
|
|
|
|
flash(_(u"Please fill out all fields!"), category="error")
|
|
|
|
return render_title_template("user_edit.html", new_user=1, content=content, translations=translations,
|
|
|
|
registered_oauth=oauth_check, title=_(u"Add new user"))
|
|
|
|
content.password = generate_password_hash(to_save["password"])
|
|
|
|
content.nickname = to_save["nickname"]
|
|
|
|
if config.config_public_reg and not check_valid_domain(to_save["email"]):
|
|
|
|
flash(_(u"E-mail is not from valid domain"), category="error")
|
|
|
|
return render_title_template("user_edit.html", new_user=1, content=content, translations=translations,
|
|
|
|
registered_oauth=oauth_check, title=_(u"Add new user"))
|
|
|
|
else:
|
|
|
|
content.email = to_save["email"]
|
|
|
|
try:
|
|
|
|
ub.session.add(content)
|
|
|
|
ub.session.commit()
|
|
|
|
flash(_(u"User '%(user)s' created", user=content.nickname), category="success")
|
|
|
|
return redirect(url_for('admin.admin'))
|
|
|
|
except IntegrityError:
|
|
|
|
ub.session.rollback()
|
|
|
|
flash(_(u"Found an existing account for this e-mail address or nickname."), category="error")
|
|
|
|
else:
|
|
|
|
content.role = config.config_default_role
|
|
|
|
content.sidebar_view = config.config_default_show
|
|
|
|
content.mature_content = bool(config.config_default_show & ub.MATURE_CONTENT)
|
|
|
|
return render_title_template("user_edit.html", new_user=1, content=content, translations=translations,
|
|
|
|
languages=languages, title=_(u"Add new user"), page="newuser",
|
|
|
|
registered_oauth=oauth_check)
|
|
|
|
|
|
|
|
|
|
|
|
@admi.route("/admin/mailsettings", methods=["GET", "POST"])
|
|
|
|
@login_required
|
|
|
|
@admin_required
|
|
|
|
def edit_mailsettings():
|
|
|
|
content = ub.session.query(ub.Settings).first()
|
|
|
|
if request.method == "POST":
|
|
|
|
to_save = request.form.to_dict()
|
|
|
|
content.mail_server = to_save["mail_server"]
|
|
|
|
content.mail_port = int(to_save["mail_port"])
|
|
|
|
content.mail_login = to_save["mail_login"]
|
|
|
|
content.mail_password = to_save["mail_password"]
|
|
|
|
content.mail_from = to_save["mail_from"]
|
|
|
|
content.mail_use_ssl = int(to_save["mail_use_ssl"])
|
|
|
|
try:
|
|
|
|
ub.session.commit()
|
|
|
|
except Exception as e:
|
|
|
|
flash(e, category="error")
|
|
|
|
if "test" in to_save and to_save["test"]:
|
|
|
|
if current_user.kindle_mail:
|
|
|
|
result = helper.send_test_mail(current_user.kindle_mail, current_user.nickname)
|
|
|
|
if result is None:
|
|
|
|
flash(_(u"Test e-mail successfully send to %(kindlemail)s", kindlemail=current_user.kindle_mail),
|
|
|
|
category="success")
|
|
|
|
else:
|
|
|
|
flash(_(u"There was an error sending the Test e-mail: %(res)s", res=result), category="error")
|
|
|
|
else:
|
|
|
|
flash(_(u"Please configure your kindle e-mail address first..."), category="error")
|
|
|
|
else:
|
|
|
|
flash(_(u"E-mail server settings updated"), category="success")
|
|
|
|
return render_title_template("email_edit.html", content=content, title=_(u"Edit e-mail server settings"),
|
|
|
|
page="mailset")
|
|
|
|
|
|
|
|
|
|
|
|
@admi.route("/admin/user/<int:user_id>", methods=["GET", "POST"])
|
|
|
|
@login_required
|
|
|
|
@admin_required
|
|
|
|
def edit_user(user_id):
|
|
|
|
content = ub.session.query(ub.User).filter(ub.User.id == int(user_id)).first() # type: ub.User
|
|
|
|
downloads = list()
|
|
|
|
languages = speaking_language()
|
|
|
|
translations = babel.list_translations() + [LC('en')]
|
|
|
|
for book in content.downloads:
|
|
|
|
downloadbook = db.session.query(db.Books).filter(db.Books.id == book.book_id).first()
|
|
|
|
if downloadbook:
|
|
|
|
downloads.append(downloadbook)
|
|
|
|
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()
|
|
|
|
if "delete" in to_save:
|
|
|
|
ub.session.query(ub.User).filter(ub.User.id == content.id).delete()
|
|
|
|
ub.session.commit()
|
|
|
|
flash(_(u"User '%(nick)s' deleted", nick=content.nickname), category="success")
|
|
|
|
return redirect(url_for('admin.admin'))
|
|
|
|
else:
|
|
|
|
if "password" in to_save and to_save["password"]:
|
|
|
|
content.password = generate_password_hash(to_save["password"])
|
|
|
|
|
|
|
|
if "admin_role" in to_save and not content.role_admin():
|
|
|
|
content.role = content.role + ub.ROLE_ADMIN
|
|
|
|
elif "admin_role" not in to_save and content.role_admin():
|
|
|
|
content.role = content.role - ub.ROLE_ADMIN
|
|
|
|
|
|
|
|
if "download_role" in to_save and not content.role_download():
|
|
|
|
content.role = content.role + ub.ROLE_DOWNLOAD
|
|
|
|
elif "download_role" not in to_save and content.role_download():
|
|
|
|
content.role = content.role - ub.ROLE_DOWNLOAD
|
|
|
|
|
|
|
|
if "upload_role" in to_save and not content.role_upload():
|
|
|
|
content.role = content.role + ub.ROLE_UPLOAD
|
|
|
|
elif "upload_role" not in to_save and content.role_upload():
|
|
|
|
content.role = content.role - ub.ROLE_UPLOAD
|
|
|
|
|
|
|
|
if "edit_role" in to_save and not content.role_edit():
|
|
|
|
content.role = content.role + ub.ROLE_EDIT
|
|
|
|
elif "edit_role" not in to_save and content.role_edit():
|
|
|
|
content.role = content.role - ub.ROLE_EDIT
|
|
|
|
|
|
|
|
if "delete_role" in to_save and not content.role_delete_books():
|
|
|
|
content.role = content.role + ub.ROLE_DELETE_BOOKS
|
|
|
|
elif "delete_role" not in to_save and content.role_delete_books():
|
|
|
|
content.role = content.role - ub.ROLE_DELETE_BOOKS
|
|
|
|
|
|
|
|
if "passwd_role" in to_save and not content.role_passwd():
|
|
|
|
content.role = content.role + ub.ROLE_PASSWD
|
|
|
|
elif "passwd_role" not in to_save and content.role_passwd():
|
|
|
|
content.role = content.role - ub.ROLE_PASSWD
|
|
|
|
|
|
|
|
if "edit_shelf_role" in to_save and not content.role_edit_shelfs():
|
|
|
|
content.role = content.role + ub.ROLE_EDIT_SHELFS
|
|
|
|
elif "edit_shelf_role" not in to_save and content.role_edit_shelfs():
|
|
|
|
content.role = content.role - ub.ROLE_EDIT_SHELFS
|
|
|
|
|
|
|
|
val = [int(k[5:]) for k, __ in to_save.items() if k.startswith('show')]
|
|
|
|
sidebar = ub.get_sidebar_config()
|
|
|
|
for element in sidebar:
|
|
|
|
if element['visibility'] in val and not content.check_visibility(element['visibility']):
|
|
|
|
content.sidebar_view += element['visibility']
|
|
|
|
elif not element['visibility'] in val and content.check_visibility(element['visibility']):
|
|
|
|
content.sidebar_view -= element['visibility']
|
|
|
|
|
|
|
|
if "Show_detail_random" in to_save and not content.show_detail_random():
|
|
|
|
content.sidebar_view += ub.DETAIL_RANDOM
|
|
|
|
elif "Show_detail_random" not in to_save and content.show_detail_random():
|
|
|
|
content.sidebar_view -= ub.DETAIL_RANDOM
|
|
|
|
|
|
|
|
content.mature_content = "Show_mature_content" in to_save
|
|
|
|
|
|
|
|
if "default_language" in to_save:
|
|
|
|
content.default_language = to_save["default_language"]
|
|
|
|
if "locale" in to_save and to_save["locale"]:
|
|
|
|
content.locale = to_save["locale"]
|
|
|
|
if to_save["email"] and to_save["email"] != content.email:
|
|
|
|
content.email = to_save["email"]
|
|
|
|
if "kindle_mail" in to_save and to_save["kindle_mail"] != content.kindle_mail:
|
|
|
|
content.kindle_mail = to_save["kindle_mail"]
|
|
|
|
try:
|
|
|
|
ub.session.commit()
|
|
|
|
flash(_(u"User '%(nick)s' updated", nick=content.nickname), category="success")
|
|
|
|
except IntegrityError:
|
|
|
|
ub.session.rollback()
|
|
|
|
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,
|
|
|
|
title=_(u"Edit User %(nick)s", nick=content.nickname), page="edituser")
|
|
|
|
|
|
|
|
|
|
|
|
@admi.route("/admin/resetpassword/<int:user_id>")
|
|
|
|
@login_required
|
|
|
|
@admin_required
|
|
|
|
def reset_password(user_id):
|
|
|
|
if not config.config_public_reg:
|
|
|
|
abort(404)
|
|
|
|
if current_user is not None and current_user.is_authenticated:
|
|
|
|
existing_user = ub.session.query(ub.User).filter(ub.User.id == user_id).first()
|
|
|
|
password = helper.generate_random_password()
|
|
|
|
existing_user.password = generate_password_hash(password)
|
|
|
|
try:
|
|
|
|
ub.session.commit()
|
|
|
|
helper.send_registration_mail(existing_user.email, existing_user.nickname, password, True)
|
|
|
|
flash(_(u"Password for user %(user)s reset", user=existing_user.nickname), category="success")
|
|
|
|
except Exception:
|
|
|
|
ub.session.rollback()
|
|
|
|
flash(_(u"An unknown error occurred. Please try again later."), category="error")
|
|
|
|
return redirect(url_for('admin.admin'))
|
|
|
|
|
|
|
|
|
|
|
|
@admi.route("/get_update_status", methods=['GET'])
|
|
|
|
@login_required_if_no_ano
|
|
|
|
def get_update_status():
|
|
|
|
return updater_thread.get_available_updates(request.method)
|
|
|
|
|
|
|
|
|
|
|
|
@admi.route("/get_updater_status", methods=['GET', 'POST'])
|
|
|
|
@login_required
|
|
|
|
@admin_required
|
|
|
|
def get_updater_status():
|
|
|
|
status = {}
|
|
|
|
if request.method == "POST":
|
|
|
|
commit = request.form.to_dict()
|
|
|
|
if "start" in commit and commit['start'] == 'True':
|
|
|
|
text = {
|
|
|
|
"1": _(u'Requesting update package'),
|
|
|
|
"2": _(u'Downloading update package'),
|
|
|
|
"3": _(u'Unzipping update package'),
|
|
|
|
"4": _(u'Replacing files'),
|
|
|
|
"5": _(u'Database connections are closed'),
|
|
|
|
"6": _(u'Stopping server'),
|
|
|
|
"7": _(u'Update finished, please press okay and reload page'),
|
|
|
|
"8": _(u'Update failed:') + u' ' + _(u'HTTP Error'),
|
|
|
|
"9": _(u'Update failed:') + u' ' + _(u'Connection error'),
|
|
|
|
"10": _(u'Update failed:') + u' ' + _(u'Timeout while establishing connection'),
|
|
|
|
"11": _(u'Update failed:') + u' ' + _(u'General error')
|
|
|
|
}
|
|
|
|
status['text'] = text
|
|
|
|
updater_thread.status = 0
|
|
|
|
updater_thread.start()
|
|
|
|
status['status'] = updater_thread.get_update_status()
|
|
|
|
elif request.method == "GET":
|
|
|
|
try:
|
|
|
|
status['status'] = updater_thread.get_update_status()
|
|
|
|
if status['status'] == -1:
|
|
|
|
status['status'] = 7
|
|
|
|
except Exception:
|
|
|
|
status['status'] = 11
|
|
|
|
return json.dumps(status)
|