mirror of
https://github.com/janeczku/calibre-web
synced 2024-12-25 09:30:31 +00:00
Link fixes
Fixes reader button visible in detail view Fix formats to convert (added htmlz) Fix logger in updater Added request "v3" of github api on update Fix quotes parameter on external calls E-Mail logger working more stable (also on python3) Routing fixes Change import in ub
This commit is contained in:
parent
1dc6f44828
commit
4230226716
73
cps/admin.py
73
cps/admin.py
@ -21,6 +21,7 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
from flask import Blueprint, flash, redirect, url_for
|
from flask import Blueprint, flash, redirect, url_for
|
||||||
from flask import abort, request, make_response
|
from flask import abort, request, make_response
|
||||||
@ -39,20 +40,26 @@ from gdriveutils import is_gdrive_ready, gdrive_support, downloadFile, deleteDat
|
|||||||
import helper
|
import helper
|
||||||
from werkzeug.security import generate_password_hash
|
from werkzeug.security import generate_password_hash
|
||||||
from oauth_bb import oauth_check
|
from oauth_bb import oauth_check
|
||||||
|
try:
|
||||||
|
from urllib.parse import quote
|
||||||
|
from imp import reload
|
||||||
|
except ImportError:
|
||||||
|
from urllib import quote
|
||||||
|
|
||||||
|
feature_support = dict()
|
||||||
try:
|
try:
|
||||||
from goodreads.client import GoodreadsClient
|
from goodreads.client import GoodreadsClient
|
||||||
goodreads_support = True
|
feature_support['goodreads'] = True
|
||||||
except ImportError:
|
except ImportError:
|
||||||
goodreads_support = False
|
feature_support['goodreads'] = False
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import rarfile
|
import rarfile
|
||||||
rar_support = True
|
feature_support['rar'] = True
|
||||||
except ImportError:
|
except ImportError:
|
||||||
rar_support = False
|
feature_support['rar'] = False
|
||||||
|
|
||||||
|
|
||||||
|
feature_support['gdrive'] = gdrive_support
|
||||||
admi = Blueprint('admin', __name__)
|
admi = Blueprint('admin', __name__)
|
||||||
|
|
||||||
|
|
||||||
@ -287,7 +294,7 @@ def configuration_helper(origin):
|
|||||||
db_change = False
|
db_change = False
|
||||||
success = False
|
success = False
|
||||||
filedata = None
|
filedata = None
|
||||||
if gdrive_support is False:
|
if not feature_support['gdrive']:
|
||||||
gdriveError = _('Import of optional Google Drive requirements missing')
|
gdriveError = _('Import of optional Google Drive requirements missing')
|
||||||
else:
|
else:
|
||||||
if not os.path.isfile(os.path.join(config.get_main_dir, 'client_secrets.json')):
|
if not os.path.isfile(os.path.join(config.get_main_dir, 'client_secrets.json')):
|
||||||
@ -327,7 +334,7 @@ def configuration_helper(origin):
|
|||||||
else:
|
else:
|
||||||
flash(_(u'client_secrets.json is not configured for web application'), category="error")
|
flash(_(u'client_secrets.json is not configured for web application'), category="error")
|
||||||
return render_title_template("config_edit.html", content=config, origin=origin,
|
return render_title_template("config_edit.html", content=config, origin=origin,
|
||||||
gdrive=gdrive_support, gdriveError=gdriveError,
|
gdriveError=gdriveError,
|
||||||
goodreads=goodreads_support, title=_(u"Basic Configuration"),
|
goodreads=goodreads_support, title=_(u"Basic Configuration"),
|
||||||
page="config")
|
page="config")
|
||||||
# always show google drive settings, but in case of error deny support
|
# always show google drive settings, but in case of error deny support
|
||||||
@ -353,7 +360,7 @@ def configuration_helper(origin):
|
|||||||
ub.session.commit()
|
ub.session.commit()
|
||||||
flash(_(u'Keyfile location is not valid, please enter correct path'), category="error")
|
flash(_(u'Keyfile location is not valid, please enter correct path'), category="error")
|
||||||
return render_title_template("config_edit.html", content=config, origin=origin,
|
return render_title_template("config_edit.html", content=config, origin=origin,
|
||||||
gdrive=gdrive_support, gdriveError=gdriveError,
|
gdriveError=gdriveError,
|
||||||
goodreads=goodreads_support, title=_(u"Basic Configuration"),
|
goodreads=goodreads_support, title=_(u"Basic Configuration"),
|
||||||
page="config")
|
page="config")
|
||||||
if "config_certfile" in to_save:
|
if "config_certfile" in to_save:
|
||||||
@ -365,9 +372,8 @@ def configuration_helper(origin):
|
|||||||
ub.session.commit()
|
ub.session.commit()
|
||||||
flash(_(u'Certfile location is not valid, please enter correct path'), category="error")
|
flash(_(u'Certfile location is not valid, please enter correct path'), category="error")
|
||||||
return render_title_template("config_edit.html", content=config, origin=origin,
|
return render_title_template("config_edit.html", content=config, origin=origin,
|
||||||
gdrive=gdrive_support, gdriveError=gdriveError,
|
gdriveError=gdriveError, feature_support=feature_support,
|
||||||
goodreads=goodreads_support, title=_(u"Basic Configuration"),
|
title=_(u"Basic Configuration"), page="config")
|
||||||
page="config")
|
|
||||||
content.config_uploading = 0
|
content.config_uploading = 0
|
||||||
content.config_anonbrowse = 0
|
content.config_anonbrowse = 0
|
||||||
content.config_public_reg = 0
|
content.config_public_reg = 0
|
||||||
@ -391,9 +397,8 @@ def configuration_helper(origin):
|
|||||||
ub.session.commit()
|
ub.session.commit()
|
||||||
flash(_(u'Please enter a LDAP provider and a DN'), category="error")
|
flash(_(u'Please enter a LDAP provider and a DN'), category="error")
|
||||||
return render_title_template("config_edit.html", content=config, origin=origin,
|
return render_title_template("config_edit.html", content=config, origin=origin,
|
||||||
gdrive=gdrive_support, gdriveError=gdriveError,
|
gdriveError=gdriveError, feature_support=feature_support,
|
||||||
goodreads=goodreads_support, title=_(u"Basic Configuration"),
|
title=_(u"Basic Configuration"), page="config")
|
||||||
page="config")
|
|
||||||
else:
|
else:
|
||||||
content.config_use_ldap = 1
|
content.config_use_ldap = 1
|
||||||
content.config_ldap_provider_url = to_save["config_ldap_provider_url"]
|
content.config_ldap_provider_url = to_save["config_ldap_provider_url"]
|
||||||
@ -450,9 +455,8 @@ def configuration_helper(origin):
|
|||||||
ub.session.commit()
|
ub.session.commit()
|
||||||
flash(_(u'Logfile location is not valid, please enter correct path'), category="error")
|
flash(_(u'Logfile location is not valid, please enter correct path'), category="error")
|
||||||
return render_title_template("config_edit.html", content=config, origin=origin,
|
return render_title_template("config_edit.html", content=config, origin=origin,
|
||||||
gdrive=gdrive_support, gdriveError=gdriveError,
|
gdriveError=gdriveError, feature_support=feature_support,
|
||||||
goodreads=goodreads_support, title=_(u"Basic Configuration"),
|
title=_(u"Basic Configuration"), page="config")
|
||||||
page="config")
|
|
||||||
else:
|
else:
|
||||||
content.config_logfile = to_save["config_logfile"]
|
content.config_logfile = to_save["config_logfile"]
|
||||||
reboot_required = True
|
reboot_required = True
|
||||||
@ -465,8 +469,7 @@ def configuration_helper(origin):
|
|||||||
else:
|
else:
|
||||||
flash(check[1], category="error")
|
flash(check[1], category="error")
|
||||||
return render_title_template("config_edit.html", content=config, origin=origin,
|
return render_title_template("config_edit.html", content=config, origin=origin,
|
||||||
gdrive=gdrive_support, goodreads=goodreads_support,
|
feature_support=feature_support, title=_(u"Basic Configuration"))
|
||||||
rarfile_support=rar_support, title=_(u"Basic Configuration"))
|
|
||||||
try:
|
try:
|
||||||
if content.config_use_google_drive and is_gdrive_ready() and not \
|
if content.config_use_google_drive and is_gdrive_ready() and not \
|
||||||
os.path.exists(os.path.join(content.config_calibre_dir, "metadata.db")):
|
os.path.exists(os.path.join(content.config_calibre_dir, "metadata.db")):
|
||||||
@ -479,20 +482,18 @@ def configuration_helper(origin):
|
|||||||
flash(_(u"Calibre-Web configuration updated"), category="success")
|
flash(_(u"Calibre-Web configuration updated"), category="success")
|
||||||
config.loadSettings()
|
config.loadSettings()
|
||||||
app.logger.setLevel(config.config_log_level)
|
app.logger.setLevel(config.config_log_level)
|
||||||
logging.getLogger("book_formats").setLevel(config.config_log_level)
|
logging.getLogger("uploader").setLevel(config.config_log_level)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
flash(e, category="error")
|
flash(e, category="error")
|
||||||
return render_title_template("config_edit.html", content=config, origin=origin,
|
return render_title_template("config_edit.html", content=config, origin=origin,
|
||||||
gdrive=gdrive_support, gdriveError=gdriveError,
|
gdriveError=gdriveError, feature_support=feature_support,
|
||||||
goodreads=goodreads_support, rarfile_support=rar_support,
|
|
||||||
title=_(u"Basic Configuration"), page="config")
|
title=_(u"Basic Configuration"), page="config")
|
||||||
if db_change:
|
if db_change:
|
||||||
reload(db)
|
reload(db)
|
||||||
if not db.setup_db():
|
if not db.setup_db():
|
||||||
flash(_(u'DB location is not valid, please enter correct path'), category="error")
|
flash(_(u'DB location is not valid, please enter correct path'), category="error")
|
||||||
return render_title_template("config_edit.html", content=config, origin=origin,
|
return render_title_template("config_edit.html", content=config, origin=origin,
|
||||||
gdrive=gdrive_support, gdriveError=gdriveError,
|
gdriveError=gdriveError, feature_support=feature_support,
|
||||||
goodreads=goodreads_support, rarfile_support=rar_support,
|
|
||||||
title=_(u"Basic Configuration"), page="config")
|
title=_(u"Basic Configuration"), page="config")
|
||||||
if reboot_required:
|
if reboot_required:
|
||||||
# stop Server
|
# stop Server
|
||||||
@ -501,15 +502,14 @@ def configuration_helper(origin):
|
|||||||
app.logger.info('Reboot required, restarting')
|
app.logger.info('Reboot required, restarting')
|
||||||
if origin:
|
if origin:
|
||||||
success = True
|
success = True
|
||||||
if is_gdrive_ready() and gdrive_support is True: # and config.config_use_google_drive == True:
|
if is_gdrive_ready() and feature_support['gdrive'] is True: # and config.config_use_google_drive == True:
|
||||||
gdrivefolders = listRootFolders()
|
gdrivefolders = listRootFolders()
|
||||||
else:
|
else:
|
||||||
gdrivefolders = list()
|
gdrivefolders = list()
|
||||||
return render_title_template("config_edit.html", origin=origin, success=success, content=config,
|
return render_title_template("config_edit.html", origin=origin, success=success, content=config,
|
||||||
show_authenticate_google_drive=not is_gdrive_ready(),
|
show_authenticate_google_drive=not is_gdrive_ready(),
|
||||||
gdrive=gdrive_support, gdriveError=gdriveError,
|
gdriveError=gdriveError, gdrivefolders=gdrivefolders, feature_support=feature_support,
|
||||||
gdrivefolders=gdrivefolders, rarfile_support=rar_support,
|
title=_(u"Basic Configuration"), page="config")
|
||||||
goodreads=goodreads_support, title=_(u"Basic Configuration"), page="config")
|
|
||||||
|
|
||||||
|
|
||||||
@admi.route("/admin/user/new", methods=["GET", "POST"])
|
@admi.route("/admin/user/new", methods=["GET", "POST"])
|
||||||
@ -569,20 +569,20 @@ def new_user():
|
|||||||
if not to_save["nickname"] or not to_save["email"] or not to_save["password"]:
|
if not to_save["nickname"] or not to_save["email"] or not to_save["password"]:
|
||||||
flash(_(u"Please fill out all fields!"), category="error")
|
flash(_(u"Please fill out all fields!"), category="error")
|
||||||
return render_title_template("user_edit.html", new_user=1, content=content, translations=translations,
|
return render_title_template("user_edit.html", new_user=1, content=content, translations=translations,
|
||||||
title=_(u"Add new user"))
|
registered_oauth=oauth_check, title=_(u"Add new user"))
|
||||||
content.password = generate_password_hash(to_save["password"])
|
content.password = generate_password_hash(to_save["password"])
|
||||||
content.nickname = to_save["nickname"]
|
content.nickname = to_save["nickname"]
|
||||||
if config.config_public_reg and not check_valid_domain(to_save["email"]):
|
if config.config_public_reg and not check_valid_domain(to_save["email"]):
|
||||||
flash(_(u"E-mail is not from valid domain"), category="error")
|
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,
|
return render_title_template("user_edit.html", new_user=1, content=content, translations=translations,
|
||||||
title=_(u"Add new user"))
|
registered_oauth=oauth_check, title=_(u"Add new user"))
|
||||||
else:
|
else:
|
||||||
content.email = to_save["email"]
|
content.email = to_save["email"]
|
||||||
try:
|
try:
|
||||||
ub.session.add(content)
|
ub.session.add(content)
|
||||||
ub.session.commit()
|
ub.session.commit()
|
||||||
flash(_(u"User '%(user)s' created", user=content.nickname), category="success")
|
flash(_(u"User '%(user)s' created", user=content.nickname), category="success")
|
||||||
return redirect(url_for('admin'))
|
return redirect(url_for('admin.admin'))
|
||||||
except IntegrityError:
|
except IntegrityError:
|
||||||
ub.session.rollback()
|
ub.session.rollback()
|
||||||
flash(_(u"Found an existing account for this e-mail address or nickname."), category="error")
|
flash(_(u"Found an existing account for this e-mail address or nickname."), category="error")
|
||||||
@ -591,7 +591,8 @@ def new_user():
|
|||||||
content.sidebar_view = config.config_default_show
|
content.sidebar_view = config.config_default_show
|
||||||
content.mature_content = bool(config.config_default_show & ub.MATURE_CONTENT)
|
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,
|
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)
|
languages=languages, title=_(u"Add new user"), page="newuser",
|
||||||
|
registered_oauth=oauth_check)
|
||||||
|
|
||||||
|
|
||||||
@admi.route("/admin/mailsettings", methods=["GET", "POST"])
|
@admi.route("/admin/mailsettings", methods=["GET", "POST"])
|
||||||
@ -649,7 +650,7 @@ def edit_user(user_id):
|
|||||||
ub.session.query(ub.User).filter(ub.User.id == content.id).delete()
|
ub.session.query(ub.User).filter(ub.User.id == content.id).delete()
|
||||||
ub.session.commit()
|
ub.session.commit()
|
||||||
flash(_(u"User '%(nick)s' deleted", nick=content.nickname), category="success")
|
flash(_(u"User '%(nick)s' deleted", nick=content.nickname), category="success")
|
||||||
return redirect(url_for('admin'))
|
return redirect(url_for('admin.admin'))
|
||||||
else:
|
else:
|
||||||
if "password" in to_save and to_save["password"]:
|
if "password" in to_save and to_save["password"]:
|
||||||
content.password = generate_password_hash(to_save["password"])
|
content.password = generate_password_hash(to_save["password"])
|
||||||
@ -766,8 +767,8 @@ def edit_user(user_id):
|
|||||||
ub.session.rollback()
|
ub.session.rollback()
|
||||||
flash(_(u"An unknown error occured."), category="error")
|
flash(_(u"An unknown error occured."), category="error")
|
||||||
return render_title_template("user_edit.html", translations=translations, languages=languages, new_user=0,
|
return render_title_template("user_edit.html", translations=translations, languages=languages, new_user=0,
|
||||||
content=content, downloads=downloads, title=_(u"Edit User %(nick)s",
|
content=content, downloads=downloads, registered_oauth=oauth_check,
|
||||||
nick=content.nickname), page="edituser", registered_oauth=oauth_check)
|
title=_(u"Edit User %(nick)s", nick=content.nickname), page="edituser")
|
||||||
|
|
||||||
|
|
||||||
@admi.route("/admin/resetpassword/<int:user_id>")
|
@admi.route("/admin/resetpassword/<int:user_id>")
|
||||||
@ -787,7 +788,7 @@ def reset_password(user_id):
|
|||||||
except Exception:
|
except Exception:
|
||||||
ub.session.rollback()
|
ub.session.rollback()
|
||||||
flash(_(u"An unknown error occurred. Please try again later."), category="error")
|
flash(_(u"An unknown error occurred. Please try again later."), category="error")
|
||||||
return redirect(url_for('admin'))
|
return redirect(url_for('admin.admin'))
|
||||||
|
|
||||||
|
|
||||||
@admi.route("/get_update_status", methods=['GET'])
|
@admi.route("/get_update_status", methods=['GET'])
|
||||||
|
@ -42,7 +42,7 @@ from iso639 import languages as isoLanguages
|
|||||||
|
|
||||||
editbook = Blueprint('editbook', __name__)
|
editbook = Blueprint('editbook', __name__)
|
||||||
|
|
||||||
EXTENSIONS_CONVERT = {'pdf', 'epub', 'mobi', 'azw3', 'docx', 'rtf', 'fb2', 'lit', 'lrf', 'txt', 'html', 'rtf', 'odt'}
|
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',
|
EXTENSIONS_UPLOAD = {'txt', 'pdf', 'epub', 'mobi', 'azw', 'azw3', 'cbr', 'cbz', 'cbt', 'djvu', 'prc', 'doc', 'docx',
|
||||||
'fb2', 'html', 'rtf', 'odt', 'mp3', 'm4a', 'm4b'}
|
'fb2', 'html', 'rtf', 'odt', 'mp3', 'm4a', 'm4b'}
|
||||||
@ -380,7 +380,7 @@ def upload_single_file(request, book, book_id):
|
|||||||
# Queue uploader info
|
# Queue uploader info
|
||||||
uploadText=_(u"File format %(ext)s added to %(book)s", ext=file_ext.upper(), book=book.title)
|
uploadText=_(u"File format %(ext)s added to %(book)s", ext=file_ext.upper(), book=book.title)
|
||||||
global_WorkerThread.add_upload(current_user.nickname,
|
global_WorkerThread.add_upload(current_user.nickname,
|
||||||
"<a href=\"" + url_for('show_book', book_id=book.id) + "\">" + uploadText + "</a>")
|
"<a href=\"" + url_for('web.show_book', book_id=book.id) + "\">" + uploadText + "</a>")
|
||||||
|
|
||||||
def upload_cover(request, book):
|
def upload_cover(request, book):
|
||||||
if 'btn-upload-cover' in request.files:
|
if 'btn-upload-cover' in request.files:
|
||||||
@ -589,10 +589,10 @@ def upload():
|
|||||||
flash(
|
flash(
|
||||||
_("File extension '%(ext)s' is not allowed to be uploaded to this server",
|
_("File extension '%(ext)s' is not allowed to be uploaded to this server",
|
||||||
ext=file_ext), category="error")
|
ext=file_ext), category="error")
|
||||||
return redirect(url_for('index'))
|
return redirect(url_for('web.index'))
|
||||||
else:
|
else:
|
||||||
flash(_('File to be uploaded must have an extension'), category="error")
|
flash(_('File to be uploaded must have an extension'), category="error")
|
||||||
return redirect(url_for('index'))
|
return redirect(url_for('web.index'))
|
||||||
|
|
||||||
# extract metadata from file
|
# extract metadata from file
|
||||||
meta = uploader.upload(requested_file)
|
meta = uploader.upload(requested_file)
|
||||||
@ -612,12 +612,12 @@ def upload():
|
|||||||
os.makedirs(filepath)
|
os.makedirs(filepath)
|
||||||
except OSError:
|
except OSError:
|
||||||
flash(_(u"Failed to create path %(path)s (Permission denied).", path=filepath), category="error")
|
flash(_(u"Failed to create path %(path)s (Permission denied).", path=filepath), category="error")
|
||||||
return redirect(url_for('index'))
|
return redirect(url_for('web.index'))
|
||||||
try:
|
try:
|
||||||
copyfile(meta.file_path, saved_filename)
|
copyfile(meta.file_path, saved_filename)
|
||||||
except OSError:
|
except OSError:
|
||||||
flash(_(u"Failed to store file %(file)s (Permission denied).", file=saved_filename), category="error")
|
flash(_(u"Failed to store file %(file)s (Permission denied).", file=saved_filename), category="error")
|
||||||
return redirect(url_for('index'))
|
return redirect(url_for('web.index'))
|
||||||
try:
|
try:
|
||||||
os.unlink(meta.file_path)
|
os.unlink(meta.file_path)
|
||||||
except OSError:
|
except OSError:
|
||||||
|
@ -70,7 +70,7 @@ def google_drive_callback():
|
|||||||
f.write(credentials.to_json())
|
f.write(credentials.to_json())
|
||||||
except ValueError as error:
|
except ValueError as error:
|
||||||
app.logger.error(error)
|
app.logger.error(error)
|
||||||
return redirect(url_for('configuration'))
|
return redirect(url_for('admin.configuration'))
|
||||||
|
|
||||||
|
|
||||||
@gdrive.route("/gdrive/watch/subscribe")
|
@gdrive.route("/gdrive/watch/subscribe")
|
||||||
@ -102,7 +102,7 @@ def watch_gdrive():
|
|||||||
else:
|
else:
|
||||||
flash(reason['message'], category="error")
|
flash(reason['message'], category="error")
|
||||||
|
|
||||||
return redirect(url_for('configuration'))
|
return redirect(url_for('admin.configuration'))
|
||||||
|
|
||||||
|
|
||||||
@gdrive.route("/gdrive/watch/revoke")
|
@gdrive.route("/gdrive/watch/revoke")
|
||||||
@ -121,7 +121,7 @@ def revoke_watch_gdrive():
|
|||||||
ub.session.merge(settings)
|
ub.session.merge(settings)
|
||||||
ub.session.commit()
|
ub.session.commit()
|
||||||
config.loadSettings()
|
config.loadSettings()
|
||||||
return redirect(url_for('configuration'))
|
return redirect(url_for('admin.configuration'))
|
||||||
|
|
||||||
|
|
||||||
@gdrive.route("/gdrive/watch/callback", methods=['GET', 'POST'])
|
@gdrive.route("/gdrive/watch/callback", methods=['GET', 'POST'])
|
||||||
|
@ -19,13 +19,13 @@
|
|||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
import db
|
from cps import config, global_WorkerThread, get_locale
|
||||||
from cps import config
|
|
||||||
from flask import current_app as app
|
from flask import current_app as app
|
||||||
from tempfile import gettempdir
|
from tempfile import gettempdir
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
import db
|
||||||
import unicodedata
|
import unicodedata
|
||||||
import worker
|
import worker
|
||||||
import time
|
import time
|
||||||
@ -40,7 +40,6 @@ try:
|
|||||||
import gdriveutils as gd
|
import gdriveutils as gd
|
||||||
except ImportError:
|
except ImportError:
|
||||||
pass
|
pass
|
||||||
# import web
|
|
||||||
import random
|
import random
|
||||||
from subproc_wrapper import process_open
|
from subproc_wrapper import process_open
|
||||||
import ub
|
import ub
|
||||||
@ -244,7 +243,7 @@ def get_sorted_author(value):
|
|||||||
else:
|
else:
|
||||||
value2 = value
|
value2 = value
|
||||||
except Exception:
|
except Exception:
|
||||||
web.app.logger.error("Sorting author " + str(value) + "failed")
|
app.logger.error("Sorting author " + str(value) + "failed")
|
||||||
value2 = value
|
value2 = value
|
||||||
return value2
|
return value2
|
||||||
|
|
||||||
@ -261,13 +260,13 @@ def delete_book_file(book, calibrepath, book_format=None):
|
|||||||
else:
|
else:
|
||||||
if os.path.isdir(path):
|
if os.path.isdir(path):
|
||||||
if len(next(os.walk(path))[1]):
|
if len(next(os.walk(path))[1]):
|
||||||
web.app.logger.error(
|
app.logger.error(
|
||||||
"Deleting book " + str(book.id) + " failed, path has subfolders: " + book.path)
|
"Deleting book " + str(book.id) + " failed, path has subfolders: " + book.path)
|
||||||
return False
|
return False
|
||||||
shutil.rmtree(path, ignore_errors=True)
|
shutil.rmtree(path, ignore_errors=True)
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
web.app.logger.error("Deleting book " + str(book.id) + " failed, book path not valid: " + book.path)
|
app.logger.error("Deleting book " + str(book.id) + " failed, book path not valid: " + book.path)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
@ -290,7 +289,7 @@ def update_dir_structure_file(book_id, calibrepath, first_author):
|
|||||||
if not os.path.exists(new_title_path):
|
if not os.path.exists(new_title_path):
|
||||||
os.renames(path, new_title_path)
|
os.renames(path, new_title_path)
|
||||||
else:
|
else:
|
||||||
web.app.logger.info("Copying title: " + path + " into existing: " + new_title_path)
|
app.logger.info("Copying title: " + path + " into existing: " + new_title_path)
|
||||||
for dir_name, subdir_list, file_list in os.walk(path):
|
for dir_name, subdir_list, file_list in os.walk(path):
|
||||||
for file in file_list:
|
for file in file_list:
|
||||||
os.renames(os.path.join(dir_name, file),
|
os.renames(os.path.join(dir_name, file),
|
||||||
@ -298,8 +297,8 @@ def update_dir_structure_file(book_id, calibrepath, first_author):
|
|||||||
path = new_title_path
|
path = new_title_path
|
||||||
localbook.path = localbook.path.split('/')[0] + '/' + new_titledir
|
localbook.path = localbook.path.split('/')[0] + '/' + new_titledir
|
||||||
except OSError as ex:
|
except OSError as ex:
|
||||||
web.app.logger.error("Rename title from: " + path + " to " + new_title_path + ": " + str(ex))
|
app.logger.error("Rename title from: " + path + " to " + new_title_path + ": " + str(ex))
|
||||||
web.app.logger.debug(ex, exc_info=True)
|
app.logger.debug(ex, exc_info=True)
|
||||||
return _("Rename title from: '%(src)s' to '%(dest)s' failed with error: %(error)s",
|
return _("Rename title from: '%(src)s' to '%(dest)s' failed with error: %(error)s",
|
||||||
src=path, dest=new_title_path, error=str(ex))
|
src=path, dest=new_title_path, error=str(ex))
|
||||||
if authordir != new_authordir:
|
if authordir != new_authordir:
|
||||||
@ -308,8 +307,8 @@ def update_dir_structure_file(book_id, calibrepath, first_author):
|
|||||||
os.renames(path, new_author_path)
|
os.renames(path, new_author_path)
|
||||||
localbook.path = new_authordir + '/' + localbook.path.split('/')[1]
|
localbook.path = new_authordir + '/' + localbook.path.split('/')[1]
|
||||||
except OSError as ex:
|
except OSError as ex:
|
||||||
web.app.logger.error("Rename author from: " + path + " to " + new_author_path + ": " + str(ex))
|
app.logger.error("Rename author from: " + path + " to " + new_author_path + ": " + str(ex))
|
||||||
web.app.logger.debug(ex, exc_info=True)
|
app.logger.debug(ex, exc_info=True)
|
||||||
return _("Rename author from: '%(src)s' to '%(dest)s' failed with error: %(error)s",
|
return _("Rename author from: '%(src)s' to '%(dest)s' failed with error: %(error)s",
|
||||||
src=path, dest=new_author_path, error=str(ex))
|
src=path, dest=new_author_path, error=str(ex))
|
||||||
# Rename all files from old names to new names
|
# Rename all files from old names to new names
|
||||||
@ -322,8 +321,8 @@ def update_dir_structure_file(book_id, calibrepath, first_author):
|
|||||||
os.path.join(path_name,new_name + '.' + file_format.format.lower()))
|
os.path.join(path_name,new_name + '.' + file_format.format.lower()))
|
||||||
file_format.name = new_name
|
file_format.name = new_name
|
||||||
except OSError as ex:
|
except OSError as ex:
|
||||||
web.app.logger.error("Rename file in path " + path + " to " + new_name + ": " + str(ex))
|
app.logger.error("Rename file in path " + path + " to " + new_name + ": " + str(ex))
|
||||||
web.app.logger.debug(ex, exc_info=True)
|
app.logger.debug(ex, exc_info=True)
|
||||||
return _("Rename file in path '%(src)s' to '%(dest)s' failed with error: %(error)s",
|
return _("Rename file in path '%(src)s' to '%(dest)s' failed with error: %(error)s",
|
||||||
src=path, dest=new_name, error=str(ex))
|
src=path, dest=new_name, error=str(ex))
|
||||||
return False
|
return False
|
||||||
@ -418,17 +417,17 @@ def delete_book(book, calibrepath, book_format):
|
|||||||
def get_book_cover(cover_path):
|
def get_book_cover(cover_path):
|
||||||
if config.config_use_google_drive:
|
if config.config_use_google_drive:
|
||||||
try:
|
try:
|
||||||
if not web.is_gdrive_ready():
|
if not gd.is_gdrive_ready():
|
||||||
return send_from_directory(os.path.join(os.path.dirname(__file__), "static"), "generic_cover.jpg")
|
return send_from_directory(os.path.join(os.path.dirname(__file__), "static"), "generic_cover.jpg")
|
||||||
path=gd.get_cover_via_gdrive(cover_path)
|
path=gd.get_cover_via_gdrive(cover_path)
|
||||||
if path:
|
if path:
|
||||||
return redirect(path)
|
return redirect(path)
|
||||||
else:
|
else:
|
||||||
web.app.logger.error(cover_path + '/cover.jpg not found on Google Drive')
|
app.logger.error(cover_path + '/cover.jpg not found on Google Drive')
|
||||||
return send_from_directory(os.path.join(os.path.dirname(__file__), "static"), "generic_cover.jpg")
|
return send_from_directory(os.path.join(os.path.dirname(__file__), "static"), "generic_cover.jpg")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
web.app.logger.error("Error Message: " + e.message)
|
app.logger.error("Error Message: " + e.message)
|
||||||
web.app.logger.exception(e)
|
app.logger.exception(e)
|
||||||
# traceback.print_exc()
|
# traceback.print_exc()
|
||||||
return send_from_directory(os.path.join(os.path.dirname(__file__), "static"),"generic_cover.jpg")
|
return send_from_directory(os.path.join(os.path.dirname(__file__), "static"),"generic_cover.jpg")
|
||||||
else:
|
else:
|
||||||
@ -439,7 +438,7 @@ def get_book_cover(cover_path):
|
|||||||
def save_cover(url, book_path):
|
def save_cover(url, book_path):
|
||||||
img = requests.get(url)
|
img = requests.get(url)
|
||||||
if img.headers.get('content-type') != 'image/jpeg':
|
if img.headers.get('content-type') != 'image/jpeg':
|
||||||
web.app.logger.error("Cover is no jpg file, can't save")
|
app.logger.error("Cover is no jpg file, can't save")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if config.config_use_google_drive:
|
if config.config_use_google_drive:
|
||||||
@ -448,13 +447,13 @@ def save_cover(url, book_path):
|
|||||||
f.write(img.content)
|
f.write(img.content)
|
||||||
f.close()
|
f.close()
|
||||||
gd.uploadFileToEbooksFolder(os.path.join(book_path, 'cover.jpg'), os.path.join(tmpDir, f.name))
|
gd.uploadFileToEbooksFolder(os.path.join(book_path, 'cover.jpg'), os.path.join(tmpDir, f.name))
|
||||||
web.app.logger.info("Cover is saved on Google Drive")
|
app.logger.info("Cover is saved on Google Drive")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
f = open(os.path.join(config.config_calibre_dir, book_path, "cover.jpg"), "wb")
|
f = open(os.path.join(config.config_calibre_dir, book_path, "cover.jpg"), "wb")
|
||||||
f.write(img.content)
|
f.write(img.content)
|
||||||
f.close()
|
f.close()
|
||||||
web.app.logger.info("Cover is saved")
|
app.logger.info("Cover is saved")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
@ -462,7 +461,7 @@ def do_download_file(book, book_format, data, headers):
|
|||||||
if config.config_use_google_drive:
|
if config.config_use_google_drive:
|
||||||
startTime = time.time()
|
startTime = time.time()
|
||||||
df = gd.getFileFromEbooksFolder(book.path, data.name + "." + book_format)
|
df = gd.getFileFromEbooksFolder(book.path, data.name + "." + book_format)
|
||||||
web.app.logger.debug(time.time() - startTime)
|
app.logger.debug(time.time() - startTime)
|
||||||
if df:
|
if df:
|
||||||
return gd.do_gdrive_download(df, headers)
|
return gd.do_gdrive_download(df, headers)
|
||||||
else:
|
else:
|
||||||
@ -471,7 +470,7 @@ def do_download_file(book, book_format, data, headers):
|
|||||||
filename = os.path.join(config.config_calibre_dir, book.path)
|
filename = os.path.join(config.config_calibre_dir, book.path)
|
||||||
if not os.path.isfile(os.path.join(filename, data.name + "." + book_format)):
|
if not os.path.isfile(os.path.join(filename, data.name + "." + book_format)):
|
||||||
# ToDo: improve error handling
|
# ToDo: improve error handling
|
||||||
web.app.logger.error('File not found: %s' % os.path.join(filename, data.name + "." + book_format))
|
app.logger.error('File not found: %s' % os.path.join(filename, data.name + "." + book_format))
|
||||||
response = make_response(send_from_directory(filename, data.name + "." + book_format))
|
response = make_response(send_from_directory(filename, data.name + "." + book_format))
|
||||||
response.headers = headers
|
response.headers = headers
|
||||||
return response
|
return response
|
||||||
@ -497,7 +496,7 @@ def check_unrar(unrarLocation):
|
|||||||
version = value.group(1)
|
version = value.group(1)
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
error = True
|
error = True
|
||||||
web.app.logger.exception(e)
|
app.logger.exception(e)
|
||||||
version =_(u'Error excecuting UnRar')
|
version =_(u'Error excecuting UnRar')
|
||||||
else:
|
else:
|
||||||
version = _(u'Unrar binary file not found')
|
version = _(u'Unrar binary file not found')
|
||||||
@ -522,7 +521,7 @@ def render_task_status(tasklist):
|
|||||||
if task['user'] == current_user.nickname or current_user.role_admin():
|
if task['user'] == current_user.nickname or current_user.role_admin():
|
||||||
# task2 = copy.deepcopy(task) # = task
|
# task2 = copy.deepcopy(task) # = task
|
||||||
if task['formStarttime']:
|
if task['formStarttime']:
|
||||||
task['starttime'] = format_datetime(task['formStarttime'], format='short', locale=web.get_locale())
|
task['starttime'] = format_datetime(task['formStarttime'], format='short', locale=get_locale())
|
||||||
# task2['formStarttime'] = ""
|
# task2['formStarttime'] = ""
|
||||||
else:
|
else:
|
||||||
if 'starttime' not in task:
|
if 'starttime' not in task:
|
||||||
|
243
cps/oauth.py
243
cps/oauth.py
@ -2,133 +2,136 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from flask import session
|
from flask import session
|
||||||
from flask_dance.consumer.backend.sqla import SQLAlchemyBackend, first, _get_real_user
|
try:
|
||||||
from sqlalchemy.orm.exc import NoResultFound
|
from flask_dance.consumer.backend.sqla import SQLAlchemyBackend, first, _get_real_user
|
||||||
|
from sqlalchemy.orm.exc import NoResultFound
|
||||||
|
|
||||||
|
class OAuthBackend(SQLAlchemyBackend):
|
||||||
|
"""
|
||||||
|
Stores and retrieves OAuth tokens using a relational database through
|
||||||
|
the `SQLAlchemy`_ ORM.
|
||||||
|
|
||||||
class OAuthBackend(SQLAlchemyBackend):
|
.. _SQLAlchemy: http://www.sqlalchemy.org/
|
||||||
"""
|
"""
|
||||||
Stores and retrieves OAuth tokens using a relational database through
|
def __init__(self, model, session,
|
||||||
the `SQLAlchemy`_ ORM.
|
user=None, user_id=None, user_required=None, anon_user=None,
|
||||||
|
cache=None):
|
||||||
|
super(OAuthBackend, self).__init__(model, session, user, user_id, user_required, anon_user, cache)
|
||||||
|
|
||||||
.. _SQLAlchemy: http://www.sqlalchemy.org/
|
def get(self, blueprint, user=None, user_id=None):
|
||||||
"""
|
if blueprint.name + '_oauth_token' in session and session[blueprint.name + '_oauth_token'] != '':
|
||||||
def __init__(self, model, session,
|
return session[blueprint.name + '_oauth_token']
|
||||||
user=None, user_id=None, user_required=None, anon_user=None,
|
# check cache
|
||||||
cache=None):
|
cache_key = self.make_cache_key(blueprint=blueprint, user=user, user_id=user_id)
|
||||||
super(OAuthBackend, self).__init__(model, session, user, user_id, user_required, anon_user, cache)
|
token = self.cache.get(cache_key)
|
||||||
|
if token:
|
||||||
|
return token
|
||||||
|
|
||||||
def get(self, blueprint, user=None, user_id=None):
|
# if not cached, make database queries
|
||||||
if blueprint.name + '_oauth_token' in session and session[blueprint.name + '_oauth_token'] != '':
|
query = (
|
||||||
return session[blueprint.name + '_oauth_token']
|
self.session.query(self.model)
|
||||||
# check cache
|
.filter_by(provider=blueprint.name)
|
||||||
cache_key = self.make_cache_key(blueprint=blueprint, user=user, user_id=user_id)
|
)
|
||||||
token = self.cache.get(cache_key)
|
uid = first([user_id, self.user_id, blueprint.config.get("user_id")])
|
||||||
if token:
|
u = first(_get_real_user(ref, self.anon_user)
|
||||||
return token
|
|
||||||
|
|
||||||
# if not cached, make database queries
|
|
||||||
query = (
|
|
||||||
self.session.query(self.model)
|
|
||||||
.filter_by(provider=blueprint.name)
|
|
||||||
)
|
|
||||||
uid = first([user_id, self.user_id, blueprint.config.get("user_id")])
|
|
||||||
u = first(_get_real_user(ref, self.anon_user)
|
|
||||||
for ref in (user, self.user, blueprint.config.get("user")))
|
|
||||||
|
|
||||||
use_provider_user_id = False
|
|
||||||
if blueprint.name + '_oauth_user_id' in session and session[blueprint.name + '_oauth_user_id'] != '':
|
|
||||||
query = query.filter_by(provider_user_id=session[blueprint.name + '_oauth_user_id'])
|
|
||||||
use_provider_user_id = True
|
|
||||||
|
|
||||||
if self.user_required and not u and not uid and not use_provider_user_id:
|
|
||||||
#raise ValueError("Cannot get OAuth token without an associated user")
|
|
||||||
return None
|
|
||||||
# check for user ID
|
|
||||||
if hasattr(self.model, "user_id") and uid:
|
|
||||||
query = query.filter_by(user_id=uid)
|
|
||||||
# check for user (relationship property)
|
|
||||||
elif hasattr(self.model, "user") and u:
|
|
||||||
query = query.filter_by(user=u)
|
|
||||||
# if we have the property, but not value, filter by None
|
|
||||||
elif hasattr(self.model, "user_id"):
|
|
||||||
query = query.filter_by(user_id=None)
|
|
||||||
# run query
|
|
||||||
try:
|
|
||||||
token = query.one().token
|
|
||||||
except NoResultFound:
|
|
||||||
token = None
|
|
||||||
|
|
||||||
# cache the result
|
|
||||||
self.cache.set(cache_key, token)
|
|
||||||
|
|
||||||
return token
|
|
||||||
|
|
||||||
def set(self, blueprint, token, user=None, user_id=None):
|
|
||||||
uid = first([user_id, self.user_id, blueprint.config.get("user_id")])
|
|
||||||
u = first(_get_real_user(ref, self.anon_user)
|
|
||||||
for ref in (user, self.user, blueprint.config.get("user")))
|
for ref in (user, self.user, blueprint.config.get("user")))
|
||||||
|
|
||||||
if self.user_required and not u and not uid:
|
use_provider_user_id = False
|
||||||
raise ValueError("Cannot set OAuth token without an associated user")
|
if blueprint.name + '_oauth_user_id' in session and session[blueprint.name + '_oauth_user_id'] != '':
|
||||||
|
query = query.filter_by(provider_user_id=session[blueprint.name + '_oauth_user_id'])
|
||||||
|
use_provider_user_id = True
|
||||||
|
|
||||||
# if there was an existing model, delete it
|
if self.user_required and not u and not uid and not use_provider_user_id:
|
||||||
existing_query = (
|
#raise ValueError("Cannot get OAuth token without an associated user")
|
||||||
self.session.query(self.model)
|
return None
|
||||||
.filter_by(provider=blueprint.name)
|
# check for user ID
|
||||||
)
|
if hasattr(self.model, "user_id") and uid:
|
||||||
# check for user ID
|
query = query.filter_by(user_id=uid)
|
||||||
has_user_id = hasattr(self.model, "user_id")
|
# check for user (relationship property)
|
||||||
if has_user_id and uid:
|
elif hasattr(self.model, "user") and u:
|
||||||
existing_query = existing_query.filter_by(user_id=uid)
|
query = query.filter_by(user=u)
|
||||||
# check for user (relationship property)
|
# if we have the property, but not value, filter by None
|
||||||
has_user = hasattr(self.model, "user")
|
elif hasattr(self.model, "user_id"):
|
||||||
if has_user and u:
|
query = query.filter_by(user_id=None)
|
||||||
existing_query = existing_query.filter_by(user=u)
|
# run query
|
||||||
# queue up delete query -- won't be run until commit()
|
try:
|
||||||
existing_query.delete()
|
token = query.one().token
|
||||||
# create a new model for this token
|
except NoResultFound:
|
||||||
kwargs = {
|
token = None
|
||||||
"provider": blueprint.name,
|
|
||||||
"token": token,
|
|
||||||
}
|
|
||||||
if has_user_id and uid:
|
|
||||||
kwargs["user_id"] = uid
|
|
||||||
if has_user and u:
|
|
||||||
kwargs["user"] = u
|
|
||||||
self.session.add(self.model(**kwargs))
|
|
||||||
# commit to delete and add simultaneously
|
|
||||||
self.session.commit()
|
|
||||||
# invalidate cache
|
|
||||||
self.cache.delete(self.make_cache_key(
|
|
||||||
blueprint=blueprint, user=user, user_id=user_id
|
|
||||||
))
|
|
||||||
|
|
||||||
def delete(self, blueprint, user=None, user_id=None):
|
# cache the result
|
||||||
query = (
|
self.cache.set(cache_key, token)
|
||||||
self.session.query(self.model)
|
|
||||||
.filter_by(provider=blueprint.name)
|
|
||||||
)
|
|
||||||
uid = first([user_id, self.user_id, blueprint.config.get("user_id")])
|
|
||||||
u = first(_get_real_user(ref, self.anon_user)
|
|
||||||
for ref in (user, self.user, blueprint.config.get("user")))
|
|
||||||
|
|
||||||
if self.user_required and not u and not uid:
|
return token
|
||||||
raise ValueError("Cannot delete OAuth token without an associated user")
|
|
||||||
|
|
||||||
# check for user ID
|
def set(self, blueprint, token, user=None, user_id=None):
|
||||||
if hasattr(self.model, "user_id") and uid:
|
uid = first([user_id, self.user_id, blueprint.config.get("user_id")])
|
||||||
query = query.filter_by(user_id=uid)
|
u = first(_get_real_user(ref, self.anon_user)
|
||||||
# check for user (relationship property)
|
for ref in (user, self.user, blueprint.config.get("user")))
|
||||||
elif hasattr(self.model, "user") and u:
|
|
||||||
query = query.filter_by(user=u)
|
if self.user_required and not u and not uid:
|
||||||
# if we have the property, but not value, filter by None
|
raise ValueError("Cannot set OAuth token without an associated user")
|
||||||
elif hasattr(self.model, "user_id"):
|
|
||||||
query = query.filter_by(user_id=None)
|
# if there was an existing model, delete it
|
||||||
# run query
|
existing_query = (
|
||||||
query.delete()
|
self.session.query(self.model)
|
||||||
self.session.commit()
|
.filter_by(provider=blueprint.name)
|
||||||
# invalidate cache
|
)
|
||||||
self.cache.delete(self.make_cache_key(
|
# check for user ID
|
||||||
blueprint=blueprint, user=user, user_id=user_id,
|
has_user_id = hasattr(self.model, "user_id")
|
||||||
))
|
if has_user_id and uid:
|
||||||
|
existing_query = existing_query.filter_by(user_id=uid)
|
||||||
|
# check for user (relationship property)
|
||||||
|
has_user = hasattr(self.model, "user")
|
||||||
|
if has_user and u:
|
||||||
|
existing_query = existing_query.filter_by(user=u)
|
||||||
|
# queue up delete query -- won't be run until commit()
|
||||||
|
existing_query.delete()
|
||||||
|
# create a new model for this token
|
||||||
|
kwargs = {
|
||||||
|
"provider": blueprint.name,
|
||||||
|
"token": token,
|
||||||
|
}
|
||||||
|
if has_user_id and uid:
|
||||||
|
kwargs["user_id"] = uid
|
||||||
|
if has_user and u:
|
||||||
|
kwargs["user"] = u
|
||||||
|
self.session.add(self.model(**kwargs))
|
||||||
|
# commit to delete and add simultaneously
|
||||||
|
self.session.commit()
|
||||||
|
# invalidate cache
|
||||||
|
self.cache.delete(self.make_cache_key(
|
||||||
|
blueprint=blueprint, user=user, user_id=user_id
|
||||||
|
))
|
||||||
|
|
||||||
|
def delete(self, blueprint, user=None, user_id=None):
|
||||||
|
query = (
|
||||||
|
self.session.query(self.model)
|
||||||
|
.filter_by(provider=blueprint.name)
|
||||||
|
)
|
||||||
|
uid = first([user_id, self.user_id, blueprint.config.get("user_id")])
|
||||||
|
u = first(_get_real_user(ref, self.anon_user)
|
||||||
|
for ref in (user, self.user, blueprint.config.get("user")))
|
||||||
|
|
||||||
|
if self.user_required and not u and not uid:
|
||||||
|
raise ValueError("Cannot delete OAuth token without an associated user")
|
||||||
|
|
||||||
|
# check for user ID
|
||||||
|
if hasattr(self.model, "user_id") and uid:
|
||||||
|
query = query.filter_by(user_id=uid)
|
||||||
|
# check for user (relationship property)
|
||||||
|
elif hasattr(self.model, "user") and u:
|
||||||
|
query = query.filter_by(user=u)
|
||||||
|
# if we have the property, but not value, filter by None
|
||||||
|
elif hasattr(self.model, "user_id"):
|
||||||
|
query = query.filter_by(user_id=None)
|
||||||
|
# run query
|
||||||
|
query.delete()
|
||||||
|
self.session.commit()
|
||||||
|
# invalidate cache
|
||||||
|
self.cache.delete(self.make_cache_key(
|
||||||
|
blueprint=blueprint, user=user, user_id=user_id,
|
||||||
|
))
|
||||||
|
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
417
cps/oauth_bb.py
417
cps/oauth_bb.py
@ -20,11 +20,14 @@
|
|||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>
|
# along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
from flask_dance.contrib.github import make_github_blueprint, github
|
try:
|
||||||
from flask_dance.contrib.google import make_google_blueprint, google
|
from flask_dance.contrib.github import make_github_blueprint, github
|
||||||
from flask_dance.consumer import oauth_authorized, oauth_error
|
from flask_dance.contrib.google import make_google_blueprint, google
|
||||||
|
from flask_dance.consumer import oauth_authorized, oauth_error
|
||||||
|
from oauth import OAuthBackend
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
from sqlalchemy.orm.exc import NoResultFound
|
from sqlalchemy.orm.exc import NoResultFound
|
||||||
from oauth import OAuthBackend
|
|
||||||
from flask import flash, session, redirect, url_for, request, make_response, abort
|
from flask import flash, session, redirect, url_for, request, make_response, abort
|
||||||
import json
|
import json
|
||||||
from cps import config, app
|
from cps import config, app
|
||||||
@ -91,226 +94,226 @@ def logout_oauth_user():
|
|||||||
if oauth + '_oauth_user_id' in session:
|
if oauth + '_oauth_user_id' in session:
|
||||||
session.pop(oauth + '_oauth_user_id')
|
session.pop(oauth + '_oauth_user_id')
|
||||||
|
|
||||||
|
if ub.oauth_support:
|
||||||
|
github_blueprint = make_github_blueprint(
|
||||||
|
client_id=config.config_github_oauth_client_id,
|
||||||
|
client_secret=config.config_github_oauth_client_secret,
|
||||||
|
redirect_to="github_login",)
|
||||||
|
|
||||||
github_blueprint = make_github_blueprint(
|
google_blueprint = make_google_blueprint(
|
||||||
client_id=config.config_github_oauth_client_id,
|
client_id=config.config_google_oauth_client_id,
|
||||||
client_secret=config.config_github_oauth_client_secret,
|
client_secret=config.config_google_oauth_client_secret,
|
||||||
redirect_to="github_login",)
|
redirect_to="google_login",
|
||||||
|
scope=[
|
||||||
google_blueprint = make_google_blueprint(
|
"https://www.googleapis.com/auth/plus.me",
|
||||||
client_id=config.config_google_oauth_client_id,
|
"https://www.googleapis.com/auth/userinfo.email",
|
||||||
client_secret=config.config_google_oauth_client_secret,
|
]
|
||||||
redirect_to="google_login",
|
|
||||||
scope=[
|
|
||||||
"https://www.googleapis.com/auth/plus.me",
|
|
||||||
"https://www.googleapis.com/auth/userinfo.email",
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
app.register_blueprint(google_blueprint, url_prefix="/login")
|
|
||||||
app.register_blueprint(github_blueprint, url_prefix='/login')
|
|
||||||
|
|
||||||
github_blueprint.backend = OAuthBackend(ub.OAuth, ub.session, user=current_user, user_required=True)
|
|
||||||
google_blueprint.backend = OAuthBackend(ub.OAuth, ub.session, user=current_user, user_required=True)
|
|
||||||
|
|
||||||
|
|
||||||
if config.config_use_github_oauth:
|
|
||||||
register_oauth_blueprint(github_blueprint, 'GitHub')
|
|
||||||
if config.config_use_google_oauth:
|
|
||||||
register_oauth_blueprint(google_blueprint, 'Google')
|
|
||||||
|
|
||||||
|
|
||||||
@oauth_authorized.connect_via(github_blueprint)
|
|
||||||
def github_logged_in(blueprint, token):
|
|
||||||
if not token:
|
|
||||||
flash(_("Failed to log in with GitHub."), category="error")
|
|
||||||
return False
|
|
||||||
|
|
||||||
resp = blueprint.session.get("/user")
|
|
||||||
if not resp.ok:
|
|
||||||
flash(_("Failed to fetch user info from GitHub."), category="error")
|
|
||||||
return False
|
|
||||||
|
|
||||||
github_info = resp.json()
|
|
||||||
github_user_id = str(github_info["id"])
|
|
||||||
return oauth_update_token(blueprint, token, github_user_id)
|
|
||||||
|
|
||||||
|
|
||||||
@oauth_authorized.connect_via(google_blueprint)
|
|
||||||
def google_logged_in(blueprint, token):
|
|
||||||
if not token:
|
|
||||||
flash(_("Failed to log in with Google."), category="error")
|
|
||||||
return False
|
|
||||||
|
|
||||||
resp = blueprint.session.get("/oauth2/v2/userinfo")
|
|
||||||
if not resp.ok:
|
|
||||||
flash(_("Failed to fetch user info from Google."), category="error")
|
|
||||||
return False
|
|
||||||
|
|
||||||
google_info = resp.json()
|
|
||||||
google_user_id = str(google_info["id"])
|
|
||||||
|
|
||||||
return oauth_update_token(blueprint, token, google_user_id)
|
|
||||||
|
|
||||||
|
|
||||||
def oauth_update_token(blueprint, token, provider_user_id):
|
|
||||||
session[blueprint.name + "_oauth_user_id"] = provider_user_id
|
|
||||||
session[blueprint.name + "_oauth_token"] = token
|
|
||||||
|
|
||||||
# Find this OAuth token in the database, or create it
|
|
||||||
query = ub.session.query(ub.OAuth).filter_by(
|
|
||||||
provider=blueprint.name,
|
|
||||||
provider_user_id=provider_user_id,
|
|
||||||
)
|
)
|
||||||
try:
|
|
||||||
oauth = query.one()
|
app.register_blueprint(google_blueprint, url_prefix="/login")
|
||||||
# update token
|
app.register_blueprint(github_blueprint, url_prefix='/login')
|
||||||
oauth.token = token
|
|
||||||
except NoResultFound:
|
github_blueprint.backend = OAuthBackend(ub.OAuth, ub.session, user=current_user, user_required=True)
|
||||||
oauth = ub.OAuth(
|
google_blueprint.backend = OAuthBackend(ub.OAuth, ub.session, user=current_user, user_required=True)
|
||||||
|
|
||||||
|
|
||||||
|
if config.config_use_github_oauth:
|
||||||
|
register_oauth_blueprint(github_blueprint, 'GitHub')
|
||||||
|
if config.config_use_google_oauth:
|
||||||
|
register_oauth_blueprint(google_blueprint, 'Google')
|
||||||
|
|
||||||
|
|
||||||
|
@oauth_authorized.connect_via(github_blueprint)
|
||||||
|
def github_logged_in(blueprint, token):
|
||||||
|
if not token:
|
||||||
|
flash(_("Failed to log in with GitHub."), category="error")
|
||||||
|
return False
|
||||||
|
|
||||||
|
resp = blueprint.session.get("/user")
|
||||||
|
if not resp.ok:
|
||||||
|
flash(_("Failed to fetch user info from GitHub."), category="error")
|
||||||
|
return False
|
||||||
|
|
||||||
|
github_info = resp.json()
|
||||||
|
github_user_id = str(github_info["id"])
|
||||||
|
return oauth_update_token(blueprint, token, github_user_id)
|
||||||
|
|
||||||
|
|
||||||
|
@oauth_authorized.connect_via(google_blueprint)
|
||||||
|
def google_logged_in(blueprint, token):
|
||||||
|
if not token:
|
||||||
|
flash(_("Failed to log in with Google."), category="error")
|
||||||
|
return False
|
||||||
|
|
||||||
|
resp = blueprint.session.get("/oauth2/v2/userinfo")
|
||||||
|
if not resp.ok:
|
||||||
|
flash(_("Failed to fetch user info from Google."), category="error")
|
||||||
|
return False
|
||||||
|
|
||||||
|
google_info = resp.json()
|
||||||
|
google_user_id = str(google_info["id"])
|
||||||
|
|
||||||
|
return oauth_update_token(blueprint, token, google_user_id)
|
||||||
|
|
||||||
|
|
||||||
|
def oauth_update_token(blueprint, token, provider_user_id):
|
||||||
|
session[blueprint.name + "_oauth_user_id"] = provider_user_id
|
||||||
|
session[blueprint.name + "_oauth_token"] = token
|
||||||
|
|
||||||
|
# Find this OAuth token in the database, or create it
|
||||||
|
query = ub.session.query(ub.OAuth).filter_by(
|
||||||
provider=blueprint.name,
|
provider=blueprint.name,
|
||||||
provider_user_id=provider_user_id,
|
provider_user_id=provider_user_id,
|
||||||
token=token,
|
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
ub.session.add(oauth)
|
oauth = query.one()
|
||||||
ub.session.commit()
|
# update token
|
||||||
except Exception as e:
|
oauth.token = token
|
||||||
app.logger.exception(e)
|
except NoResultFound:
|
||||||
ub.session.rollback()
|
oauth = ub.OAuth(
|
||||||
|
provider=blueprint.name,
|
||||||
|
provider_user_id=provider_user_id,
|
||||||
|
token=token,
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
ub.session.add(oauth)
|
||||||
|
ub.session.commit()
|
||||||
|
except Exception as e:
|
||||||
|
app.logger.exception(e)
|
||||||
|
ub.session.rollback()
|
||||||
|
|
||||||
# Disable Flask-Dance's default behavior for saving the OAuth token
|
# Disable Flask-Dance's default behavior for saving the OAuth token
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def bind_oauth_or_register(provider, provider_user_id, redirect_url):
|
def bind_oauth_or_register(provider, provider_user_id, redirect_url):
|
||||||
query = ub.session.query(ub.OAuth).filter_by(
|
query = ub.session.query(ub.OAuth).filter_by(
|
||||||
provider=provider,
|
provider=provider,
|
||||||
provider_user_id=provider_user_id,
|
provider_user_id=provider_user_id,
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
oauth = query.one()
|
oauth = query.one()
|
||||||
# already bind with user, just login
|
# already bind with user, just login
|
||||||
if oauth.user:
|
if oauth.user:
|
||||||
login_user(oauth.user)
|
login_user(oauth.user)
|
||||||
return redirect(url_for('index'))
|
return redirect(url_for('web.index'))
|
||||||
else:
|
else:
|
||||||
# bind to current user
|
# bind to current user
|
||||||
|
if current_user and current_user.is_authenticated:
|
||||||
|
oauth.user = current_user
|
||||||
|
try:
|
||||||
|
ub.session.add(oauth)
|
||||||
|
ub.session.commit()
|
||||||
|
except Exception as e:
|
||||||
|
app.logger.exception(e)
|
||||||
|
ub.session.rollback()
|
||||||
|
return redirect(url_for('web.register'))
|
||||||
|
except NoResultFound:
|
||||||
|
return redirect(url_for(redirect_url))
|
||||||
|
|
||||||
|
|
||||||
|
def get_oauth_status():
|
||||||
|
status = []
|
||||||
|
query = ub.session.query(ub.OAuth).filter_by(
|
||||||
|
user_id=current_user.id,
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
oauths = query.all()
|
||||||
|
for oauth in oauths:
|
||||||
|
status.append(oauth.provider)
|
||||||
|
return status
|
||||||
|
except NoResultFound:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def unlink_oauth(provider):
|
||||||
|
if request.host_url + 'me' != request.referrer:
|
||||||
|
pass
|
||||||
|
query = ub.session.query(ub.OAuth).filter_by(
|
||||||
|
provider=provider,
|
||||||
|
user_id=current_user.id,
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
oauth = query.one()
|
||||||
if current_user and current_user.is_authenticated:
|
if current_user and current_user.is_authenticated:
|
||||||
oauth.user = current_user
|
oauth.user = current_user
|
||||||
try:
|
try:
|
||||||
ub.session.add(oauth)
|
ub.session.delete(oauth)
|
||||||
ub.session.commit()
|
ub.session.commit()
|
||||||
|
logout_oauth_user()
|
||||||
|
flash(_("Unlink to %(oauth)s success.", oauth=oauth_check[provider]), category="success")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
app.logger.exception(e)
|
app.logger.exception(e)
|
||||||
ub.session.rollback()
|
ub.session.rollback()
|
||||||
return redirect(url_for('web.register'))
|
flash(_("Unlink to %(oauth)s failed.", oauth=oauth_check[provider]), category="error")
|
||||||
except NoResultFound:
|
except NoResultFound:
|
||||||
return redirect(url_for(redirect_url))
|
app.logger.warning("oauth %s for user %d not fount" % (provider, current_user.id))
|
||||||
|
flash(_("Not linked to %(oauth)s.", oauth=oauth_check[provider]), category="error")
|
||||||
|
return redirect(url_for('profile'))
|
||||||
|
|
||||||
|
|
||||||
def get_oauth_status():
|
# notify on OAuth provider error
|
||||||
status = []
|
@oauth_error.connect_via(github_blueprint)
|
||||||
query = ub.session.query(ub.OAuth).filter_by(
|
def github_error(blueprint, error, error_description=None, error_uri=None):
|
||||||
user_id=current_user.id,
|
msg = (
|
||||||
)
|
"OAuth error from {name}! "
|
||||||
try:
|
"error={error} description={description} uri={uri}"
|
||||||
oauths = query.all()
|
).format(
|
||||||
for oauth in oauths:
|
name=blueprint.name,
|
||||||
status.append(oauth.provider)
|
error=error,
|
||||||
return status
|
description=error_description,
|
||||||
except NoResultFound:
|
uri=error_uri,
|
||||||
return None
|
)
|
||||||
|
flash(msg, category="error")
|
||||||
|
|
||||||
|
'''
|
||||||
|
@oauth.route('/github')
|
||||||
|
@github_oauth_required
|
||||||
|
def github_login():
|
||||||
|
if not github.authorized:
|
||||||
|
return redirect(url_for('github.login'))
|
||||||
|
account_info = github.get('/user')
|
||||||
|
if account_info.ok:
|
||||||
|
account_info_json = account_info.json()
|
||||||
|
return bind_oauth_or_register(github_blueprint.name, account_info_json['id'], 'github.login')
|
||||||
|
flash(_(u"GitHub Oauth error, please retry later."), category="error")
|
||||||
|
return redirect(url_for('web.login'))
|
||||||
|
|
||||||
|
|
||||||
|
@oauth.route('/unlink/github', methods=["GET"])
|
||||||
|
@login_required
|
||||||
|
def github_login_unlink():
|
||||||
|
return unlink_oauth(github_blueprint.name)
|
||||||
|
|
||||||
|
|
||||||
|
@oauth.route('/google')
|
||||||
|
@google_oauth_required
|
||||||
|
def google_login():
|
||||||
|
if not google.authorized:
|
||||||
|
return redirect(url_for("google.login"))
|
||||||
|
resp = google.get("/oauth2/v2/userinfo")
|
||||||
|
if resp.ok:
|
||||||
|
account_info_json = resp.json()
|
||||||
|
return bind_oauth_or_register(google_blueprint.name, account_info_json['id'], 'google.login')
|
||||||
|
flash(_(u"Google Oauth error, please retry later."), category="error")
|
||||||
|
return redirect(url_for('web.login'))
|
||||||
|
'''
|
||||||
|
|
||||||
def unlink_oauth(provider):
|
@oauth_error.connect_via(google_blueprint)
|
||||||
if request.host_url + 'me' != request.referrer:
|
def google_error(blueprint, error, error_description=None, error_uri=None):
|
||||||
pass
|
msg = (
|
||||||
query = ub.session.query(ub.OAuth).filter_by(
|
"OAuth error from {name}! "
|
||||||
provider=provider,
|
"error={error} description={description} uri={uri}"
|
||||||
user_id=current_user.id,
|
).format(
|
||||||
)
|
name=blueprint.name,
|
||||||
try:
|
error=error,
|
||||||
oauth = query.one()
|
description=error_description,
|
||||||
if current_user and current_user.is_authenticated:
|
uri=error_uri,
|
||||||
oauth.user = current_user
|
)
|
||||||
try:
|
flash(msg, category="error")
|
||||||
ub.session.delete(oauth)
|
|
||||||
ub.session.commit()
|
|
||||||
logout_oauth_user()
|
|
||||||
flash(_("Unlink to %(oauth)s success.", oauth=oauth_check[provider]), category="success")
|
|
||||||
except Exception as e:
|
|
||||||
app.logger.exception(e)
|
|
||||||
ub.session.rollback()
|
|
||||||
flash(_("Unlink to %(oauth)s failed.", oauth=oauth_check[provider]), category="error")
|
|
||||||
except NoResultFound:
|
|
||||||
app.logger.warning("oauth %s for user %d not fount" % (provider, current_user.id))
|
|
||||||
flash(_("Not linked to %(oauth)s.", oauth=oauth_check[provider]), category="error")
|
|
||||||
return redirect(url_for('profile'))
|
|
||||||
|
|
||||||
|
'''
|
||||||
# notify on OAuth provider error
|
@oauth.route('/unlink/google', methods=["GET"])
|
||||||
@oauth_error.connect_via(github_blueprint)
|
@login_required
|
||||||
def github_error(blueprint, error, error_description=None, error_uri=None):
|
def google_login_unlink():
|
||||||
msg = (
|
return unlink_oauth(google_blueprint.name)'''
|
||||||
"OAuth error from {name}! "
|
|
||||||
"error={error} description={description} uri={uri}"
|
|
||||||
).format(
|
|
||||||
name=blueprint.name,
|
|
||||||
error=error,
|
|
||||||
description=error_description,
|
|
||||||
uri=error_uri,
|
|
||||||
)
|
|
||||||
flash(msg, category="error")
|
|
||||||
|
|
||||||
'''
|
|
||||||
@oauth.route('/github')
|
|
||||||
@github_oauth_required
|
|
||||||
def github_login():
|
|
||||||
if not github.authorized:
|
|
||||||
return redirect(url_for('github.login'))
|
|
||||||
account_info = github.get('/user')
|
|
||||||
if account_info.ok:
|
|
||||||
account_info_json = account_info.json()
|
|
||||||
return bind_oauth_or_register(github_blueprint.name, account_info_json['id'], 'github.login')
|
|
||||||
flash(_(u"GitHub Oauth error, please retry later."), category="error")
|
|
||||||
return redirect(url_for('login'))
|
|
||||||
|
|
||||||
|
|
||||||
@oauth.route('/unlink/github', methods=["GET"])
|
|
||||||
@login_required
|
|
||||||
def github_login_unlink():
|
|
||||||
return unlink_oauth(github_blueprint.name)
|
|
||||||
|
|
||||||
|
|
||||||
@oauth.route('/google')
|
|
||||||
@google_oauth_required
|
|
||||||
def google_login():
|
|
||||||
if not google.authorized:
|
|
||||||
return redirect(url_for("google.login"))
|
|
||||||
resp = google.get("/oauth2/v2/userinfo")
|
|
||||||
if resp.ok:
|
|
||||||
account_info_json = resp.json()
|
|
||||||
return bind_oauth_or_register(google_blueprint.name, account_info_json['id'], 'google.login')
|
|
||||||
flash(_(u"Google Oauth error, please retry later."), category="error")
|
|
||||||
return redirect(url_for('login'))
|
|
||||||
'''
|
|
||||||
|
|
||||||
@oauth_error.connect_via(google_blueprint)
|
|
||||||
def google_error(blueprint, error, error_description=None, error_uri=None):
|
|
||||||
msg = (
|
|
||||||
"OAuth error from {name}! "
|
|
||||||
"error={error} description={description} uri={uri}"
|
|
||||||
).format(
|
|
||||||
name=blueprint.name,
|
|
||||||
error=error,
|
|
||||||
description=error_description,
|
|
||||||
uri=error_uri,
|
|
||||||
)
|
|
||||||
flash(msg, category="error")
|
|
||||||
|
|
||||||
'''
|
|
||||||
@oauth.route('/unlink/google', methods=["GET"])
|
|
||||||
@login_required
|
|
||||||
def google_login_unlink():
|
|
||||||
return unlink_oauth(google_blueprint.name)'''
|
|
||||||
|
@ -38,7 +38,6 @@ from werkzeug.security import check_password_hash
|
|||||||
from werkzeug.datastructures import Headers
|
from werkzeug.datastructures import Headers
|
||||||
try:
|
try:
|
||||||
from urllib.parse import quote
|
from urllib.parse import quote
|
||||||
from imp import reload
|
|
||||||
except ImportError:
|
except ImportError:
|
||||||
from urllib import quote
|
from urllib import quote
|
||||||
|
|
||||||
@ -315,7 +314,7 @@ def authenticate():
|
|||||||
def render_xml_template(*args, **kwargs):
|
def render_xml_template(*args, **kwargs):
|
||||||
#ToDo: return time in current timezone similar to %z
|
#ToDo: return time in current timezone similar to %z
|
||||||
currtime = datetime.datetime.now().strftime("%Y-%m-%dT%H:%M:%S+00:00")
|
currtime = datetime.datetime.now().strftime("%Y-%m-%dT%H:%M:%S+00:00")
|
||||||
xml = render_template(current_time=currtime, *args, **kwargs)
|
xml = render_template(current_time=currtime, instance=config.config_calibre_web_title, *args, **kwargs)
|
||||||
response = make_response(xml)
|
response = make_response(xml)
|
||||||
response.headers["Content-Type"] = "application/atom+xml; charset=utf-8"
|
response.headers["Content-Type"] = "application/atom+xml; charset=utf-8"
|
||||||
return response
|
return response
|
||||||
|
24
cps/shelf.py
24
cps/shelf.py
@ -40,7 +40,7 @@ def add_to_shelf(shelf_id, book_id):
|
|||||||
app.logger.info("Invalid shelf specified")
|
app.logger.info("Invalid shelf specified")
|
||||||
if not request.is_xhr:
|
if not request.is_xhr:
|
||||||
flash(_(u"Invalid shelf specified"), category="error")
|
flash(_(u"Invalid shelf specified"), category="error")
|
||||||
return redirect(url_for('index'))
|
return redirect(url_for('web.index'))
|
||||||
return "Invalid shelf specified", 400
|
return "Invalid shelf specified", 400
|
||||||
|
|
||||||
if not shelf.is_public and not shelf.user_id == int(current_user.id):
|
if not shelf.is_public and not shelf.user_id == int(current_user.id):
|
||||||
@ -48,14 +48,14 @@ def add_to_shelf(shelf_id, book_id):
|
|||||||
if not request.is_xhr:
|
if not request.is_xhr:
|
||||||
flash(_(u"Sorry you are not allowed to add a book to the the shelf: %(shelfname)s", shelfname=shelf.name),
|
flash(_(u"Sorry you are not allowed to add a book to the the shelf: %(shelfname)s", shelfname=shelf.name),
|
||||||
category="error")
|
category="error")
|
||||||
return redirect(url_for('index'))
|
return redirect(url_for('web.index'))
|
||||||
return "Sorry you are not allowed to add a book to the the shelf: %s" % shelf.name, 403
|
return "Sorry you are not allowed to add a book to the the shelf: %s" % shelf.name, 403
|
||||||
|
|
||||||
if shelf.is_public and not current_user.role_edit_shelfs():
|
if shelf.is_public and not current_user.role_edit_shelfs():
|
||||||
app.logger.info("User is not allowed to edit public shelves")
|
app.logger.info("User is not allowed to edit public shelves")
|
||||||
if not request.is_xhr:
|
if not request.is_xhr:
|
||||||
flash(_(u"You are not allowed to edit public shelves"), category="error")
|
flash(_(u"You are not allowed to edit public shelves"), category="error")
|
||||||
return redirect(url_for('index'))
|
return redirect(url_for('web.index'))
|
||||||
return "User is not allowed to edit public shelves", 403
|
return "User is not allowed to edit public shelves", 403
|
||||||
|
|
||||||
book_in_shelf = ub.session.query(ub.BookShelf).filter(ub.BookShelf.shelf == shelf_id,
|
book_in_shelf = ub.session.query(ub.BookShelf).filter(ub.BookShelf.shelf == shelf_id,
|
||||||
@ -64,7 +64,7 @@ def add_to_shelf(shelf_id, book_id):
|
|||||||
app.logger.info("Book is already part of the shelf: %s" % shelf.name)
|
app.logger.info("Book is already part of the shelf: %s" % shelf.name)
|
||||||
if not request.is_xhr:
|
if not request.is_xhr:
|
||||||
flash(_(u"Book is already part of the shelf: %(shelfname)s", shelfname=shelf.name), category="error")
|
flash(_(u"Book is already part of the shelf: %(shelfname)s", shelfname=shelf.name), category="error")
|
||||||
return redirect(url_for('index'))
|
return redirect(url_for('web.index'))
|
||||||
return "Book is already part of the shelf: %s" % shelf.name, 400
|
return "Book is already part of the shelf: %s" % shelf.name, 400
|
||||||
|
|
||||||
maxOrder = ub.session.query(func.max(ub.BookShelf.order)).filter(ub.BookShelf.shelf == shelf_id).first()
|
maxOrder = ub.session.query(func.max(ub.BookShelf.order)).filter(ub.BookShelf.shelf == shelf_id).first()
|
||||||
@ -81,7 +81,7 @@ def add_to_shelf(shelf_id, book_id):
|
|||||||
if "HTTP_REFERER" in request.environ:
|
if "HTTP_REFERER" in request.environ:
|
||||||
return redirect(request.environ["HTTP_REFERER"])
|
return redirect(request.environ["HTTP_REFERER"])
|
||||||
else:
|
else:
|
||||||
return redirect(url_for('index'))
|
return redirect(url_for('web.index'))
|
||||||
return "", 204
|
return "", 204
|
||||||
|
|
||||||
|
|
||||||
@ -92,17 +92,17 @@ def search_to_shelf(shelf_id):
|
|||||||
if shelf is None:
|
if shelf is None:
|
||||||
app.logger.info("Invalid shelf specified")
|
app.logger.info("Invalid shelf specified")
|
||||||
flash(_(u"Invalid shelf specified"), category="error")
|
flash(_(u"Invalid shelf specified"), category="error")
|
||||||
return redirect(url_for('index'))
|
return redirect(url_for('web.index'))
|
||||||
|
|
||||||
if not shelf.is_public and not shelf.user_id == int(current_user.id):
|
if not shelf.is_public and not shelf.user_id == int(current_user.id):
|
||||||
app.logger.info("You are not allowed to add a book to the the shelf: %s" % shelf.name)
|
app.logger.info("You are not allowed to add a book to the the shelf: %s" % shelf.name)
|
||||||
flash(_(u"You are not allowed to add a book to the the shelf: %(name)s", name=shelf.name), category="error")
|
flash(_(u"You are not allowed to add a book to the the shelf: %(name)s", name=shelf.name), category="error")
|
||||||
return redirect(url_for('index'))
|
return redirect(url_for('web.index'))
|
||||||
|
|
||||||
if shelf.is_public and not current_user.role_edit_shelfs():
|
if shelf.is_public and not current_user.role_edit_shelfs():
|
||||||
app.logger.info("User is not allowed to edit public shelves")
|
app.logger.info("User is not allowed to edit public shelves")
|
||||||
flash(_(u"User is not allowed to edit public shelves"), category="error")
|
flash(_(u"User is not allowed to edit public shelves"), category="error")
|
||||||
return redirect(url_for('index'))
|
return redirect(url_for('web.index'))
|
||||||
|
|
||||||
if current_user.id in searched_ids and searched_ids[current_user.id]:
|
if current_user.id in searched_ids and searched_ids[current_user.id]:
|
||||||
books_for_shelf = list()
|
books_for_shelf = list()
|
||||||
@ -120,7 +120,7 @@ def search_to_shelf(shelf_id):
|
|||||||
if not books_for_shelf:
|
if not books_for_shelf:
|
||||||
app.logger.info("Books are already part of the shelf: %s" % shelf.name)
|
app.logger.info("Books are already part of the shelf: %s" % shelf.name)
|
||||||
flash(_(u"Books are already part of the shelf: %(name)s", name=shelf.name), category="error")
|
flash(_(u"Books are already part of the shelf: %(name)s", name=shelf.name), category="error")
|
||||||
return redirect(url_for('index'))
|
return redirect(url_for('web.index'))
|
||||||
|
|
||||||
maxOrder = ub.session.query(func.max(ub.BookShelf.order)).filter(ub.BookShelf.shelf == shelf_id).first()
|
maxOrder = ub.session.query(func.max(ub.BookShelf.order)).filter(ub.BookShelf.shelf == shelf_id).first()
|
||||||
if maxOrder[0] is None:
|
if maxOrder[0] is None:
|
||||||
@ -146,7 +146,7 @@ def remove_from_shelf(shelf_id, book_id):
|
|||||||
if shelf is None:
|
if shelf is None:
|
||||||
app.logger.info("Invalid shelf specified")
|
app.logger.info("Invalid shelf specified")
|
||||||
if not request.is_xhr:
|
if not request.is_xhr:
|
||||||
return redirect(url_for('index'))
|
return redirect(url_for('web.index'))
|
||||||
return "Invalid shelf specified", 400
|
return "Invalid shelf specified", 400
|
||||||
|
|
||||||
# if shelf is public and use is allowed to edit shelfs, or if shelf is private and user is owner
|
# if shelf is public and use is allowed to edit shelfs, or if shelf is private and user is owner
|
||||||
@ -165,7 +165,7 @@ def remove_from_shelf(shelf_id, book_id):
|
|||||||
if book_shelf is None:
|
if book_shelf is None:
|
||||||
app.logger.info("Book already removed from shelf")
|
app.logger.info("Book already removed from shelf")
|
||||||
if not request.is_xhr:
|
if not request.is_xhr:
|
||||||
return redirect(url_for('index'))
|
return redirect(url_for('web.index'))
|
||||||
return "Book already removed from shelf", 410
|
return "Book already removed from shelf", 410
|
||||||
|
|
||||||
ub.session.delete(book_shelf)
|
ub.session.delete(book_shelf)
|
||||||
@ -180,7 +180,7 @@ def remove_from_shelf(shelf_id, book_id):
|
|||||||
if not request.is_xhr:
|
if not request.is_xhr:
|
||||||
flash(_(u"Sorry you are not allowed to remove a book from this shelf: %(sname)s", sname=shelf.name),
|
flash(_(u"Sorry you are not allowed to remove a book from this shelf: %(sname)s", sname=shelf.name),
|
||||||
category="error")
|
category="error")
|
||||||
return redirect(url_for('index'))
|
return redirect(url_for('web.index'))
|
||||||
return "Sorry you are not allowed to remove a book from this shelf: %s" % shelf.name, 403
|
return "Sorry you are not allowed to remove a book from this shelf: %s" % shelf.name, 403
|
||||||
|
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
<div class="text-center more-stuff"><h4>{{_('Delete formats:')}}</h4>
|
<div class="text-center more-stuff"><h4>{{_('Delete formats:')}}</h4>
|
||||||
{% for file in book.data %}
|
{% for file in book.data %}
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<a href="{{ url_for('web.delete_book', book_id=book.id, book_format=file.format) }}" class="btn btn-danger" type="button">{{_('Delete')}} - {{file.format}}</a>
|
<a href="{{ url_for('editbook.delete_book', book_id=book.id, book_format=file.format) }}" class="btn btn-danger" type="button">{{_('Delete')}} - {{file.format}}</a>
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
@ -28,7 +28,7 @@
|
|||||||
|
|
||||||
{% if source_formats|length > 0 and conversion_formats|length > 0 %}
|
{% if source_formats|length > 0 and conversion_formats|length > 0 %}
|
||||||
<div class="text-center more-stuff"><h4>{{_('Convert book format:')}}</h4>
|
<div class="text-center more-stuff"><h4>{{_('Convert book format:')}}</h4>
|
||||||
<form class="padded-bottom" action="{{ url_for('web.convert_bookformat', book_id=book.id) }}" method="post" id="book_convert_frm">
|
<form class="padded-bottom" action="{{ url_for('editbook.convert_bookformat', book_id=book.id) }}" method="post" id="book_convert_frm">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="text-left">
|
<div class="text-left">
|
||||||
<label class="control-label" for="book_format_from">{{_('Convert from:')}}</label>
|
<label class="control-label" for="book_format_from">{{_('Convert from:')}}</label>
|
||||||
|
@ -159,7 +159,7 @@
|
|||||||
<input type="checkbox" id="config_remote_login" name="config_remote_login" {% if content.config_remote_login %}checked{% endif %}>
|
<input type="checkbox" id="config_remote_login" name="config_remote_login" {% if content.config_remote_login %}checked{% endif %}>
|
||||||
<label for="config_remote_login">{{_('Enable remote login ("magic link")')}}</label>
|
<label for="config_remote_login">{{_('Enable remote login ("magic link")')}}</label>
|
||||||
</div>
|
</div>
|
||||||
{% if goodreads %}
|
{% if feature_support['goodreads'] %}
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<input type="checkbox" id="config_use_goodreads" name="config_use_goodreads" data-control="goodreads-settings" {% if content.config_use_goodreads %}checked{% endif %}>
|
<input type="checkbox" id="config_use_goodreads" name="config_use_goodreads" data-control="goodreads-settings" {% if content.config_use_goodreads %}checked{% endif %}>
|
||||||
<label for="config_use_goodreads">{{_('Use')}} Goodreads</label>
|
<label for="config_use_goodreads">{{_('Use')}} Goodreads</label>
|
||||||
@ -176,6 +176,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% if feature_support['ldap'] %}
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<input type="checkbox" id="config_use_ldap" name="config_use_ldap" data-control="ldap-settings" {% if content.config_use_ldap %}checked{% endif %}>
|
<input type="checkbox" id="config_use_ldap" name="config_use_ldap" data-control="ldap-settings" {% if content.config_use_ldap %}checked{% endif %}>
|
||||||
<label for="config_use_ldap">{{_('Use')}} LDAP Authentication</label>
|
<label for="config_use_ldap">{{_('Use')}} LDAP Authentication</label>
|
||||||
@ -190,6 +191,8 @@
|
|||||||
<input type="text" class="form-control" id="config_ldap_dn" name="config_ldap_dn" value="{% if content.config_use_ldap != None %}{{ content.config_ldap_dn }}{% endif %}" autocomplete="off">
|
<input type="text" class="form-control" id="config_ldap_dn" name="config_ldap_dn" value="{% if content.config_use_ldap != None %}{{ content.config_ldap_dn }}{% endif %}" autocomplete="off">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% if feature_support['oauth'] %}
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<input type="checkbox" id="config_use_github_oauth" name="config_use_github_oauth" data-control="github-oauth-settings" {% if content.config_use_github_oauth %}checked{% endif %}>
|
<input type="checkbox" id="config_use_github_oauth" name="config_use_github_oauth" data-control="github-oauth-settings" {% if content.config_use_github_oauth %}checked{% endif %}>
|
||||||
<label for="config_use_github_oauth">{{_('Use')}} GitHub OAuth</label>
|
<label for="config_use_github_oauth">{{_('Use')}} GitHub OAuth</label>
|
||||||
@ -220,6 +223,7 @@
|
|||||||
<input type="text" class="form-control" id="config_google_oauth_client_secret" name="config_google_oauth_client_secret" value="{% if content.config_google_oauth_client_secret != None %}{{ content.config_google_oauth_client_secret }}{% endif %}" autocomplete="off">
|
<input type="text" class="form-control" id="config_google_oauth_client_secret" name="config_google_oauth_client_secret" value="{% if content.config_google_oauth_client_secret != None %}{{ content.config_google_oauth_client_secret }}{% endif %}" autocomplete="off">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -57,9 +57,22 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% if reader_list %}
|
||||||
|
<div class="btn-group" role="group">
|
||||||
|
<button id="read-in-browser" type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||||
|
<span class="glyphicon glyphicon-eye-open"></span> {{_('Read in browser')}}
|
||||||
|
<span class="caret"></span>
|
||||||
|
</button>
|
||||||
|
<ul class="dropdown-menu" aria-labelledby="read-in-browser">
|
||||||
|
{% for format in reader_list %}
|
||||||
|
<li><a target="_blank" href="{{ url_for('web.read_book', book_id=entry.id, book_format=format) }}">{{format}}</a></li>
|
||||||
|
{%endfor%}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
{% if reader_list %}
|
{% if reader_list %}
|
||||||
{% if audioentries|length %}
|
{% if audioentries|length %}
|
||||||
<div class="btn-group" role="group">
|
<!--div class="btn-group" role="group">
|
||||||
<button id="listen-in-browser" type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
<button id="listen-in-browser" type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||||
<span class="glyphicon glyphicon-music"></span> {{_('Listen in browser')}}
|
<span class="glyphicon glyphicon-music"></span> {{_('Listen in browser')}}
|
||||||
<span class="caret"></span>
|
<span class="caret"></span>
|
||||||
@ -77,7 +90,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div-->
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -2,13 +2,12 @@
|
|||||||
<feed xmlns="http://www.w3.org/2005/Atom">
|
<feed xmlns="http://www.w3.org/2005/Atom">
|
||||||
<id>urn:uuid:2853dacf-ed79-42f5-8e8a-a7bb3d1ae6a2</id>
|
<id>urn:uuid:2853dacf-ed79-42f5-8e8a-a7bb3d1ae6a2</id>
|
||||||
<updated>{{ current_time }}</updated>
|
<updated>{{ current_time }}</updated>
|
||||||
<link rel="self" href="{{url_for('feed_index')}}" type="application/atom+xml;profile=opds-catalog;kind=navigation"/>
|
<link rel="self" href="{{url_for('opds.feed_index')}}" type="application/atom+xml;profile=opds-catalog;kind=navigation"/>
|
||||||
<link rel="start" title="{{_('Start')}}" href="{{url_for('feed_index')}}"
|
<link rel="start" title="{{_('Start')}}" href="{{url_for('opds.feed_index')}}"
|
||||||
type="application/atom+xml;profile=opds-catalog;kind=navigation"/>
|
type="application/atom+xml;profile=opds-catalog;kind=navigation"/>
|
||||||
<link rel="search"
|
<link rel="search"
|
||||||
href="{{url_for('feed_osd')}}"
|
href="{{url_for('opds.feed_osd')}}"
|
||||||
type="application/opensearchdescription+xml"/>
|
type="application/opensearchdescription+xml"/>
|
||||||
<!--link title="{{_('Search')}}" type="application/atom+xml" href="{{url_for('feed_normal_search')}}?query={searchTerms}" rel="search"/-->
|
|
||||||
<title>{{instance}}</title>
|
<title>{{instance}}</title>
|
||||||
<author>
|
<author>
|
||||||
<name>{{instance}}</name>
|
<name>{{instance}}</name>
|
||||||
@ -16,88 +15,88 @@
|
|||||||
</author>
|
</author>
|
||||||
<entry>
|
<entry>
|
||||||
<title>{{_('Hot Books')}}</title>
|
<title>{{_('Hot Books')}}</title>
|
||||||
<link rel="http://opds-spec.org/sort/popular" href="{{url_for('feed_hot')}}" type="application/atom+xml;profile=opds-catalog"/>
|
<link rel="http://opds-spec.org/sort/popular" href="{{url_for('opds.feed_hot')}}" type="application/atom+xml;profile=opds-catalog"/>
|
||||||
<id>{{url_for('feed_hot')}}</id>
|
<id>{{url_for('opds.feed_hot')}}</id>
|
||||||
<updated>{{ current_time }}</updated>
|
<updated>{{ current_time }}</updated>
|
||||||
<content type="text">{{_('Popular publications from this catalog based on Downloads.')}}</content>
|
<content type="text">{{_('Popular publications from this catalog based on Downloads.')}}</content>
|
||||||
</entry>
|
</entry>
|
||||||
<entry>
|
<entry>
|
||||||
<title>{{_('Best rated Books')}}</title>
|
<title>{{_('Best rated Books')}}</title>
|
||||||
<link rel="http://opds-spec.org/recommended" href="{{url_for('feed_best_rated')}}" type="application/atom+xml;profile=opds-catalog"/>
|
<link rel="http://opds-spec.org/recommended" href="{{url_for('opds.feed_best_rated')}}" type="application/atom+xml;profile=opds-catalog"/>
|
||||||
<id>{{url_for('feed_best_rated')}}</id>
|
<id>{{url_for('opds.feed_best_rated')}}</id>
|
||||||
<updated>{{ current_time }}</updated>
|
<updated>{{ current_time }}</updated>
|
||||||
<content type="text">{{_('Popular publications from this catalog based on Rating.')}}</content>
|
<content type="text">{{_('Popular publications from this catalog based on Rating.')}}</content>
|
||||||
</entry>
|
</entry>
|
||||||
<entry>
|
<entry>
|
||||||
<title>{{_('New Books')}}</title>
|
<title>{{_('New Books')}}</title>
|
||||||
<link rel="http://opds-spec.org/sort/new" href="{{url_for('feed_new')}}" type="application/atom+xml;profile=opds-catalog"/>
|
<link rel="http://opds-spec.org/sort/new" href="{{url_for('opds.feed_new')}}" type="application/atom+xml;profile=opds-catalog"/>
|
||||||
<id>{{url_for('feed_new')}}</id>
|
<id>{{url_for('opds.feed_new')}}</id>
|
||||||
<updated>{{ current_time }}</updated>
|
<updated>{{ current_time }}</updated>
|
||||||
<content type="text">{{_('The latest Books')}}</content>
|
<content type="text">{{_('The latest Books')}}</content>
|
||||||
</entry>
|
</entry>
|
||||||
<entry>
|
<entry>
|
||||||
<title>{{_('Random Books')}}</title>
|
<title>{{_('Random Books')}}</title>
|
||||||
<link rel="http://opds-spec.org/featured" href="{{url_for('feed_discover')}}" type="application/atom+xml;profile=opds-catalog"/>
|
<link rel="http://opds-spec.org/featured" href="{{url_for('opds.feed_discover')}}" type="application/atom+xml;profile=opds-catalog"/>
|
||||||
<id>{{url_for('feed_discover')}}</id>
|
<id>{{url_for('opds.feed_discover')}}</id>
|
||||||
<updated>{{ current_time }}</updated>
|
<updated>{{ current_time }}</updated>
|
||||||
<content type="text">{{_('Show Random Books')}}</content>
|
<content type="text">{{_('Show Random Books')}}</content>
|
||||||
</entry>
|
</entry>
|
||||||
{% if not current_user.is_anonymous %}
|
{% if not current_user.is_anonymous %}
|
||||||
<entry>
|
<entry>
|
||||||
<title>{{_('Read Books')}}</title>
|
<title>{{_('Read Books')}}</title>
|
||||||
<link rel="subsection" href="{{url_for('feed_read_books')}}" type="application/atom+xml;profile=opds-catalog"/>
|
<link rel="subsection" href="{{url_for('opds.feed_read_books')}}" type="application/atom+xml;profile=opds-catalog"/>
|
||||||
<id>{{url_for('feed_read_books')}}</id>
|
<id>{{url_for('opds.feed_read_books')}}</id>
|
||||||
<updated>{{ current_time }}</updated>
|
<updated>{{ current_time }}</updated>
|
||||||
<content type="text">{{_('Read Books')}}</content>
|
<content type="text">{{_('Read Books')}}</content>
|
||||||
</entry>
|
</entry>
|
||||||
{% endif %}
|
|
||||||
<entry>
|
<entry>
|
||||||
<title>{{_('Unread Books')}}</title>
|
<title>{{_('Unread Books')}}</title>
|
||||||
<link rel="subsection" href="{{url_for('feed_unread_books')}}" type="application/atom+xml;profile=opds-catalog"/>
|
<link rel="subsection" href="{{url_for('opds.feed_unread_books')}}" type="application/atom+xml;profile=opds-catalog"/>
|
||||||
<id>{{url_for('feed_unread_books')}}</id>
|
<id>{{url_for('opds.feed_unread_books')}}</id>
|
||||||
<updated>{{ current_time }}</updated>
|
<updated>{{ current_time }}</updated>
|
||||||
<content type="text">{{_('Unread Books')}}</content>
|
<content type="text">{{_('Unread Books')}}</content>
|
||||||
</entry>
|
</entry>
|
||||||
|
{% endif %}
|
||||||
<entry>
|
<entry>
|
||||||
<title>{{_('Authors')}}</title>
|
<title>{{_('Authors')}}</title>
|
||||||
<link rel="subsection" href="{{url_for('feed_authorindex')}}" type="application/atom+xml;profile=opds-catalog"/>
|
<link rel="subsection" href="{{url_for('opds.feed_authorindex')}}" type="application/atom+xml;profile=opds-catalog"/>
|
||||||
<id>{{url_for('feed_authorindex')}}</id>
|
<id>{{url_for('opds.feed_authorindex')}}</id>
|
||||||
<updated>{{ current_time }}</updated>
|
<updated>{{ current_time }}</updated>
|
||||||
<content type="text">{{_('Books ordered by Author')}}</content>
|
<content type="text">{{_('Books ordered by Author')}}</content>
|
||||||
</entry>
|
</entry>
|
||||||
<entry>
|
<entry>
|
||||||
<title>{{_('Publishers')}}</title>
|
<title>{{_('Publishers')}}</title>
|
||||||
<link rel="subsection" href="{{url_for('feed_publisherindex')}}" type="application/atom+xml;profile=opds-catalog"/>
|
<link rel="subsection" href="{{url_for('opds.feed_publisherindex')}}" type="application/atom+xml;profile=opds-catalog"/>
|
||||||
<id>{{url_for('feed_publisherindex')}}</id>
|
<id>{{url_for('opds.feed_publisherindex')}}</id>
|
||||||
<updated>{{ current_time }}</updated>
|
<updated>{{ current_time }}</updated>
|
||||||
<content type="text">{{_('Books ordered by publisher')}}</content>
|
<content type="text">{{_('Books ordered by publisher')}}</content>
|
||||||
</entry>
|
</entry>
|
||||||
<entry>
|
<entry>
|
||||||
<title>{{_('Category list')}}</title>
|
<title>{{_('Category list')}}</title>
|
||||||
<link rel="subsection" href="{{url_for('feed_categoryindex')}}" type="application/atom+xml;profile=opds-catalog"/>
|
<link rel="subsection" href="{{url_for('opds.feed_categoryindex')}}" type="application/atom+xml;profile=opds-catalog"/>
|
||||||
<id>{{url_for('feed_categoryindex')}}</id>
|
<id>{{url_for('opds.feed_categoryindex')}}</id>
|
||||||
<updated>{{ current_time }}</updated>
|
<updated>{{ current_time }}</updated>
|
||||||
<content type="text">{{_('Books ordered by category')}}</content>
|
<content type="text">{{_('Books ordered by category')}}</content>
|
||||||
</entry>
|
</entry>
|
||||||
<entry>
|
<entry>
|
||||||
<title>{{_('Series list')}}</title>
|
<title>{{_('Series list')}}</title>
|
||||||
<link rel="subsection" href="{{url_for('feed_seriesindex')}}" type="application/atom+xml;profile=opds-catalog"/>
|
<link rel="subsection" href="{{url_for('opds.feed_seriesindex')}}" type="application/atom+xml;profile=opds-catalog"/>
|
||||||
<id>{{url_for('feed_seriesindex')}}</id>
|
<id>{{url_for('opds.feed_seriesindex')}}</id>
|
||||||
<updated>{{ current_time }}</updated>
|
<updated>{{ current_time }}</updated>
|
||||||
<content type="text">{{_('Books ordered by series')}}</content>
|
<content type="text">{{_('Books ordered by series')}}</content>
|
||||||
</entry>
|
</entry>
|
||||||
<entry>
|
<entry>
|
||||||
<title>{{_('Public Shelves')}}</title>
|
<title>{{_('Public Shelves')}}</title>
|
||||||
<link rel="subsection" href="{{url_for('feed_shelfindex', public="public")}}" type="application/atom+xml;profile=opds-catalog"/>
|
<link rel="subsection" href="{{url_for('opds.feed_shelfindex', public="public")}}" type="application/atom+xml;profile=opds-catalog"/>
|
||||||
<id>{{url_for('feed_shelfindex', public="public")}}</id>
|
<id>{{url_for('opds.feed_shelfindex', public="public")}}</id>
|
||||||
<updated>{{ current_time }}</updated>
|
<updated>{{ current_time }}</updated>
|
||||||
<content type="text">{{_('Books organized in public shelfs, visible to everyone')}}</content>
|
<content type="text">{{_('Books organized in public shelfs, visible to everyone')}}</content>
|
||||||
</entry>
|
</entry>
|
||||||
{% if not current_user.is_anonymous %}
|
{% if not current_user.is_anonymous %}
|
||||||
<entry>
|
<entry>
|
||||||
<title>{{_('Your Shelves')}}</title>
|
<title>{{_('Your Shelves')}}</title>
|
||||||
<link rel="subsection" href="{{url_for('feed_shelfindex')}}" type="application/atom+xml;profile=opds-catalog"/>
|
<link rel="subsection" href="{{url_for('opds.feed_shelfindex')}}" type="application/atom+xml;profile=opds-catalog"/>
|
||||||
<id>{{url_for('feed_shelfindex')}}</id>
|
<id>{{url_for('opds.feed_shelfindex')}}</id>
|
||||||
<updated>{{ current_time }}</updated>
|
<updated>{{ current_time }}</updated>
|
||||||
<content type="text">{{_("User's own shelfs, only visible to the current user himself")}}</content>
|
<content type="text">{{_("User's own shelfs, only visible to the current user himself")}}</content>
|
||||||
</entry>
|
</entry>
|
||||||
|
@ -1,53 +1,53 @@
|
|||||||
{
|
{
|
||||||
"pubdate": "{{entry.pubdate}}",
|
"pubdate": "{{entry.pubdate}}",
|
||||||
"title": "{{entry.title}}",
|
"title": "{{entry.title}}",
|
||||||
"format_metadata": {
|
"format_metadata": {
|
||||||
{% for format in entry.data %}
|
{% for format in entry.data %}
|
||||||
"{{format.format}}": {
|
"{{format.format}}": {
|
||||||
"mtime": "{{entry.last_modified}}",
|
"mtime": "{{entry.last_modified}}",
|
||||||
"size": {{format.uncompressed_size}},
|
"size": {{format.uncompressed_size}},
|
||||||
"path": ""
|
"path": ""
|
||||||
}{% if not loop.last %},{% endif %}
|
}{% if not loop.last %},{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
},
|
},
|
||||||
"formats": [
|
"formats": [
|
||||||
{% for format in entry.data %}
|
{% for format in entry.data %}
|
||||||
"{{format.format}}"{% if not loop.last %},{% endif %}
|
"{{format.format}}"{% if not loop.last %},{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
],
|
],
|
||||||
"series": null,
|
"series": null,
|
||||||
"cover": "{{url_for('feed_get_cover', book_id=entry.id)}}",
|
"cover": "{{url_for('opds.feed_get_cover', book_id=entry.id)}}",
|
||||||
"languages": [
|
"languages": [
|
||||||
{% for lang in entry.languages %}
|
{% for lang in entry.languages %}
|
||||||
"{{lang.lang_code}}"{% if not loop.last %},{% endif %}
|
"{{lang.lang_code}}"{% if not loop.last %},{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
],
|
],
|
||||||
"comments": "{% if entry.comments|length > 0 %}{{entry.comments[0].text.replace('"', '\\"')|safe}}{% endif %}",
|
"comments": "{% if entry.comments|length > 0 %}{{entry.comments[0].text.replace('"', '\\"')|safe}}{% endif %}",
|
||||||
"tags": [
|
"tags": [
|
||||||
{% for tag in entry.tags %}
|
{% for tag in entry.tags %}
|
||||||
"{{tag.name}}"{% if not loop.last %},{% endif %}
|
"{{tag.name}}"{% if not loop.last %},{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
],
|
],
|
||||||
"application_id": {{entry.id}},
|
"application_id": {{entry.id}},
|
||||||
"series_index": {% if entry.series|length > 0 %}"{{entry.series_index}}"{% else %}null{% endif %},
|
"series_index": {% if entry.series|length > 0 %}"{{entry.series_index}}"{% else %}null{% endif %},
|
||||||
"last_modified": "{{entry.last_modified}}",
|
"last_modified": "{{entry.last_modified}}",
|
||||||
"author_sort": "{{entry.author_sort}}",
|
"author_sort": "{{entry.author_sort}}",
|
||||||
"uuid": "{{entry.uuid}}",
|
"uuid": "{{entry.uuid}}",
|
||||||
"timestamp": "{{entry.timestamp}}",
|
"timestamp": "{{entry.timestamp}}",
|
||||||
"thumbnail": "{{url_for('feed_get_cover', book_id=entry.id)}}",
|
"thumbnail": "{{url_for('opds.feed_get_cover', book_id=entry.id)}}",
|
||||||
"main_format": {
|
"main_format": {
|
||||||
"{{entry.data[0].format|lower}}": "{{ url_for('get_opds_download_link', book_id=entry.id, book_format=entry.data[0].format|lower)}}"
|
"{{entry.data[0].format|lower}}": "{{ url_for('opds.get_opds_download_link', book_id=entry.id, book_format=entry.data[0].format|lower)}}"
|
||||||
},
|
},
|
||||||
"rating":{% if entry.ratings.__len__() > 0 %} "{{entry.ratings[0].rating}}.0"{% else %}0.0{% endif %},
|
"rating":{% if entry.ratings.__len__() > 0 %} "{{entry.ratings[0].rating}}.0"{% else %}0.0{% endif %},
|
||||||
"authors": [
|
"authors": [
|
||||||
{% for author in entry.authors %}
|
{% for author in entry.authors %}
|
||||||
"{{author.name.replace('|',',')}}"{% if not loop.last %},{% endif %}
|
"{{author.name.replace('|',',')}}"{% if not loop.last %},{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
],
|
],
|
||||||
"other_formats": {
|
"other_formats": {
|
||||||
{% if entry.data.__len__() > 1 %}
|
{% if entry.data.__len__() > 1 %}
|
||||||
{% for format in entry.data[1:] %}
|
{% for format in entry.data[1:] %}
|
||||||
"{{format.format|lower}}": "{{ url_for('get_opds_download_link', book_id=entry.id, book_format=format.format|lower)}}"{% if not loop.last %},{% endif %}
|
"{{format.format|lower}}": "{{ url_for('opds.get_opds_download_link', book_id=entry.id, book_format=format.format|lower)}}"{% if not loop.last %},{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endif %} },
|
{% endif %} },
|
||||||
"title_sort": "{{entry.sort}}"
|
"title_sort": "{{entry.sort}}"
|
||||||
|
@ -161,7 +161,7 @@
|
|||||||
{% if g.user.is_authenticated or g.user.is_anonymous %}
|
{% if g.user.is_authenticated or g.user.is_anonymous %}
|
||||||
<li class="nav-head hidden-xs public-shelves">{{_('Public Shelves')}}</li>
|
<li class="nav-head hidden-xs public-shelves">{{_('Public Shelves')}}</li>
|
||||||
{% for shelf in g.public_shelfes %}
|
{% for shelf in g.public_shelfes %}
|
||||||
<li><a href="{{url_for('web.show_shelf', shelf_id=shelf.id)}}"><span class="glyphicon glyphicon-list public_shelf"></span>{{shelf.name|shortentitle(40)}}</a></li>
|
<li><a href="{{url_for('shelf.show_shelf', shelf_id=shelf.id)}}"><span class="glyphicon glyphicon-list public_shelf"></span>{{shelf.name|shortentitle(40)}}</a></li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<li class="nav-head hidden-xs your-shelves">{{_('Your Shelves')}}</li>
|
<li class="nav-head hidden-xs your-shelves">{{_('Your Shelves')}}</li>
|
||||||
{% for shelf in g.user.shelf %}
|
{% for shelf in g.user.shelf %}
|
||||||
|
@ -6,9 +6,9 @@
|
|||||||
<Developer>Janeczku</Developer>
|
<Developer>Janeczku</Developer>
|
||||||
<Contact>https://github.com/janeczku/calibre-web</Contact>
|
<Contact>https://github.com/janeczku/calibre-web</Contact>
|
||||||
<Url type="text/html"
|
<Url type="text/html"
|
||||||
template="{{url_for('search')}}?query={searchTerms}"/>
|
template="{{url_for('opds.search')}}?query={searchTerms}"/>
|
||||||
<Url type="application/atom+xml"
|
<Url type="application/atom+xml"
|
||||||
template="{{url_for('feed_normal_search')}}?query={searchTerms}"/>
|
template="{{url_for('opds.feed_normal_search')}}?query={searchTerms}"/>
|
||||||
<SyndicationRight>open</SyndicationRight>
|
<SyndicationRight>open</SyndicationRight>
|
||||||
<Language>{{lang}}</Language>
|
<Language>{{lang}}</Language>
|
||||||
<OutputEncoding>UTF-8</OutputEncoding>
|
<OutputEncoding>UTF-8</OutputEncoding>
|
||||||
|
16
cps/ub.py
16
cps/ub.py
@ -23,7 +23,6 @@ from sqlalchemy import exc
|
|||||||
from sqlalchemy.ext.declarative import declarative_base
|
from sqlalchemy.ext.declarative import declarative_base
|
||||||
from sqlalchemy.orm import *
|
from sqlalchemy.orm import *
|
||||||
from flask_login import AnonymousUserMixin
|
from flask_login import AnonymousUserMixin
|
||||||
from flask_dance.consumer.backend.sqla import OAuthConsumerMixin
|
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
import logging
|
import logging
|
||||||
@ -34,8 +33,11 @@ from binascii import hexlify
|
|||||||
import cli
|
import cli
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
from flask_dance.consumer.backend.sqla import OAuthConsumerMixin
|
||||||
import ldap
|
import ldap
|
||||||
|
oauth_support = True
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
oauth_support = False
|
||||||
pass
|
pass
|
||||||
|
|
||||||
engine = create_engine('sqlite:///{0}'.format(cli.settingspath), echo=False)
|
engine = create_engine('sqlite:///{0}'.format(cli.settingspath), echo=False)
|
||||||
@ -207,11 +209,11 @@ class User(UserBase, Base):
|
|||||||
default_language = Column(String(3), default="all")
|
default_language = Column(String(3), default="all")
|
||||||
mature_content = Column(Boolean, default=True)
|
mature_content = Column(Boolean, default=True)
|
||||||
|
|
||||||
|
if oauth_support:
|
||||||
class OAuth(OAuthConsumerMixin, Base):
|
class OAuth(OAuthConsumerMixin, Base):
|
||||||
provider_user_id = Column(String(256))
|
provider_user_id = Column(String(256))
|
||||||
user_id = Column(Integer, ForeignKey(User.id))
|
user_id = Column(Integer, ForeignKey(User.id))
|
||||||
user = relationship(User)
|
user = relationship(User)
|
||||||
|
|
||||||
|
|
||||||
# Class for anonymous user is derived from User base and completly overrides methods and properties for the
|
# Class for anonymous user is derived from User base and completly overrides methods and properties for the
|
||||||
@ -834,7 +836,7 @@ def delete_download(book_id):
|
|||||||
session.commit()
|
session.commit()
|
||||||
|
|
||||||
# Generate user Guest (translated text), as anoymous user, no rights
|
# Generate user Guest (translated text), as anoymous user, no rights
|
||||||
def create_anonymous_user(session):
|
def create_anonymous_user():
|
||||||
user = User()
|
user = User()
|
||||||
user.nickname = "Guest"
|
user.nickname = "Guest"
|
||||||
user.email = 'no@email'
|
user.email = 'no@email'
|
||||||
|
@ -18,11 +18,10 @@
|
|||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
from cps import config, get_locale
|
from cps import config, get_locale, Server, app
|
||||||
import threading
|
import threading
|
||||||
import zipfile
|
import zipfile
|
||||||
import requests
|
import requests
|
||||||
import logging
|
|
||||||
import time
|
import time
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
import os
|
import os
|
||||||
@ -35,7 +34,6 @@ import json
|
|||||||
from flask_babel import gettext as _
|
from flask_babel import gettext as _
|
||||||
from babel.dates import format_datetime
|
from babel.dates import format_datetime
|
||||||
|
|
||||||
import server
|
|
||||||
|
|
||||||
def is_sha1(sha1):
|
def is_sha1(sha1):
|
||||||
if len(sha1) != 40:
|
if len(sha1) != 40:
|
||||||
@ -69,39 +67,45 @@ class Updater(threading.Thread):
|
|||||||
def run(self):
|
def run(self):
|
||||||
try:
|
try:
|
||||||
self.status = 1
|
self.status = 1
|
||||||
r = requests.get(self._get_request_path(), stream=True)
|
app.logger.debug(u'Download update file')
|
||||||
|
headers = {'Accept': 'application/vnd.github.v3+json'}
|
||||||
|
r = requests.get(self._get_request_path(), stream=True, headers=headers)
|
||||||
r.raise_for_status()
|
r.raise_for_status()
|
||||||
|
|
||||||
self.status = 2
|
self.status = 2
|
||||||
|
app.logger.debug(u'Opening zipfile')
|
||||||
z = zipfile.ZipFile(BytesIO(r.content))
|
z = zipfile.ZipFile(BytesIO(r.content))
|
||||||
self.status = 3
|
self.status = 3
|
||||||
|
app.logger.debug(u'Extracting zipfile')
|
||||||
tmp_dir = gettempdir()
|
tmp_dir = gettempdir()
|
||||||
z.extractall(tmp_dir)
|
z.extractall(tmp_dir)
|
||||||
foldername = os.path.join(tmp_dir, z.namelist()[0])[:-1]
|
foldername = os.path.join(tmp_dir, z.namelist()[0])[:-1]
|
||||||
if not os.path.isdir(foldername):
|
if not os.path.isdir(foldername):
|
||||||
self.status = 11
|
self.status = 11
|
||||||
logging.getLogger('cps.web').info(u'Extracted contents of zipfile not found in temp folder')
|
app.logger.info(u'Extracted contents of zipfile not found in temp folder')
|
||||||
return
|
return
|
||||||
self.status = 4
|
self.status = 4
|
||||||
|
app.logger.debug(u'Replacing files')
|
||||||
self.update_source(foldername, config.get_main_dir)
|
self.update_source(foldername, config.get_main_dir)
|
||||||
self.status = 6
|
self.status = 6
|
||||||
|
app.logger.debug(u'Preparing restart of server')
|
||||||
time.sleep(2)
|
time.sleep(2)
|
||||||
server.Server.setRestartTyp(True)
|
Server.setRestartTyp(True)
|
||||||
server.Server.stopServer()
|
Server.stopServer()
|
||||||
self.status = 7
|
self.status = 7
|
||||||
time.sleep(2)
|
time.sleep(2)
|
||||||
except requests.exceptions.HTTPError as ex:
|
except requests.exceptions.HTTPError as ex:
|
||||||
logging.getLogger('cps.web').info( u'HTTP Error' + ' ' + str(ex))
|
app.logger.info( u'HTTP Error' + ' ' + str(ex))
|
||||||
self.status = 8
|
self.status = 8
|
||||||
except requests.exceptions.ConnectionError:
|
except requests.exceptions.ConnectionError:
|
||||||
logging.getLogger('cps.web').info(u'Connection error')
|
app.logger.info(u'Connection error')
|
||||||
self.status = 9
|
self.status = 9
|
||||||
except requests.exceptions.Timeout:
|
except requests.exceptions.Timeout:
|
||||||
logging.getLogger('cps.web').info(u'Timeout while establishing connection')
|
app.logger.info(u'Timeout while establishing connection')
|
||||||
self.status = 10
|
self.status = 10
|
||||||
except requests.exceptions.RequestException:
|
except requests.exceptions.RequestException:
|
||||||
self.status = 11
|
self.status = 11
|
||||||
logging.getLogger('cps.web').info(u'General error')
|
app.logger.info(u'General error')
|
||||||
|
|
||||||
def get_update_status(self):
|
def get_update_status(self):
|
||||||
return self.status
|
return self.status
|
||||||
@ -149,14 +153,14 @@ class Updater(threading.Thread):
|
|||||||
if sys.platform == "win32" or sys.platform == "darwin":
|
if sys.platform == "win32" or sys.platform == "darwin":
|
||||||
change_permissions = False
|
change_permissions = False
|
||||||
else:
|
else:
|
||||||
logging.getLogger('cps.web').debug('Update on OS-System : ' + sys.platform)
|
app.logger.debug('Update on OS-System : ' + sys.platform)
|
||||||
new_permissions = os.stat(root_dst_dir)
|
new_permissions = os.stat(root_dst_dir)
|
||||||
# print new_permissions
|
# print new_permissions
|
||||||
for src_dir, __, files in os.walk(root_src_dir):
|
for src_dir, __, files in os.walk(root_src_dir):
|
||||||
dst_dir = src_dir.replace(root_src_dir, root_dst_dir, 1)
|
dst_dir = src_dir.replace(root_src_dir, root_dst_dir, 1)
|
||||||
if not os.path.exists(dst_dir):
|
if not os.path.exists(dst_dir):
|
||||||
os.makedirs(dst_dir)
|
os.makedirs(dst_dir)
|
||||||
logging.getLogger('cps.web').debug('Create-Dir: '+dst_dir)
|
app.logger.debug('Create-Dir: '+dst_dir)
|
||||||
if change_permissions:
|
if change_permissions:
|
||||||
# print('Permissions: User '+str(new_permissions.st_uid)+' Group '+str(new_permissions.st_uid))
|
# print('Permissions: User '+str(new_permissions.st_uid)+' Group '+str(new_permissions.st_uid))
|
||||||
os.chown(dst_dir, new_permissions.st_uid, new_permissions.st_gid)
|
os.chown(dst_dir, new_permissions.st_uid, new_permissions.st_gid)
|
||||||
@ -166,20 +170,20 @@ class Updater(threading.Thread):
|
|||||||
if os.path.exists(dst_file):
|
if os.path.exists(dst_file):
|
||||||
if change_permissions:
|
if change_permissions:
|
||||||
permission = os.stat(dst_file)
|
permission = os.stat(dst_file)
|
||||||
logging.getLogger('cps.web').debug('Remove file before copy: '+dst_file)
|
app.logger.debug('Remove file before copy: '+dst_file)
|
||||||
os.remove(dst_file)
|
os.remove(dst_file)
|
||||||
else:
|
else:
|
||||||
if change_permissions:
|
if change_permissions:
|
||||||
permission = new_permissions
|
permission = new_permissions
|
||||||
shutil.move(src_file, dst_dir)
|
shutil.move(src_file, dst_dir)
|
||||||
logging.getLogger('cps.web').debug('Move File '+src_file+' to '+dst_dir)
|
app.logger.debug('Move File '+src_file+' to '+dst_dir)
|
||||||
if change_permissions:
|
if change_permissions:
|
||||||
try:
|
try:
|
||||||
os.chown(dst_file, permission.st_uid, permission.st_gid)
|
os.chown(dst_file, permission.st_uid, permission.st_gid)
|
||||||
except (Exception) as e:
|
except (Exception) as e:
|
||||||
# ex = sys.exc_info()
|
# ex = sys.exc_info()
|
||||||
old_permissions = os.stat(dst_file)
|
old_permissions = os.stat(dst_file)
|
||||||
logging.getLogger('cps.web').debug('Fail change permissions of ' + str(dst_file) + '. Before: '
|
app.logger.debug('Fail change permissions of ' + str(dst_file) + '. Before: '
|
||||||
+ str(old_permissions.st_uid) + ':' + str(old_permissions.st_gid) + ' After: '
|
+ str(old_permissions.st_uid) + ':' + str(old_permissions.st_gid) + ' After: '
|
||||||
+ str(permission.st_uid) + ':' + str(permission.st_gid) + ' error: '+str(e))
|
+ str(permission.st_uid) + ':' + str(permission.st_gid) + ' error: '+str(e))
|
||||||
return
|
return
|
||||||
@ -215,15 +219,15 @@ class Updater(threading.Thread):
|
|||||||
for item in remove_items:
|
for item in remove_items:
|
||||||
item_path = os.path.join(destination, item[1:])
|
item_path = os.path.join(destination, item[1:])
|
||||||
if os.path.isdir(item_path):
|
if os.path.isdir(item_path):
|
||||||
logging.getLogger('cps.web').debug("Delete dir " + item_path)
|
app.logger.debug("Delete dir " + item_path)
|
||||||
shutil.rmtree(item_path, ignore_errors=True)
|
shutil.rmtree(item_path, ignore_errors=True)
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
logging.getLogger('cps.web').debug("Delete file " + item_path)
|
app.logger.debug("Delete file " + item_path)
|
||||||
# log_from_thread("Delete file " + item_path)
|
# log_from_thread("Delete file " + item_path)
|
||||||
os.remove(item_path)
|
os.remove(item_path)
|
||||||
except Exception:
|
except Exception:
|
||||||
logging.getLogger('cps.web').debug("Could not remove:" + item_path)
|
app.logger.debug("Could not remove:" + item_path)
|
||||||
shutil.rmtree(source, ignore_errors=True)
|
shutil.rmtree(source, ignore_errors=True)
|
||||||
|
|
||||||
def _nightly_version_info(self):
|
def _nightly_version_info(self):
|
||||||
@ -263,7 +267,8 @@ class Updater(threading.Thread):
|
|||||||
status['update'] = True
|
status['update'] = True
|
||||||
|
|
||||||
try:
|
try:
|
||||||
r = requests.get(repository_url + '/git/commits/' + commit['object']['sha'])
|
headers = {'Accept': 'application/vnd.github.v3+json'}
|
||||||
|
r = requests.get(repository_url + '/git/commits/' + commit['object']['sha'], headers=headers)
|
||||||
r.raise_for_status()
|
r.raise_for_status()
|
||||||
update_data = r.json()
|
update_data = r.json()
|
||||||
except requests.exceptions.HTTPError as e:
|
except requests.exceptions.HTTPError as e:
|
||||||
@ -310,7 +315,8 @@ class Updater(threading.Thread):
|
|||||||
# check if we are more than one update behind if so, go up the tree
|
# check if we are more than one update behind if so, go up the tree
|
||||||
if parent_commit['sha'] != status['current_commit_hash']:
|
if parent_commit['sha'] != status['current_commit_hash']:
|
||||||
try:
|
try:
|
||||||
r = requests.get(parent_commit['url'])
|
headers = {'Accept': 'application/vnd.github.v3+json'}
|
||||||
|
r = requests.get(parent_commit['url'], headers=headers)
|
||||||
r.raise_for_status()
|
r.raise_for_status()
|
||||||
parent_data = r.json()
|
parent_data = r.json()
|
||||||
|
|
||||||
@ -368,7 +374,8 @@ class Updater(threading.Thread):
|
|||||||
# check if we are more than one update behind if so, go up the tree
|
# check if we are more than one update behind if so, go up the tree
|
||||||
if commit['sha'] != status['current_commit_hash']:
|
if commit['sha'] != status['current_commit_hash']:
|
||||||
try:
|
try:
|
||||||
r = requests.get(parent_commit['url'])
|
headers = {'Accept': 'application/vnd.github.v3+json'}
|
||||||
|
r = requests.get(parent_commit['url'], headers=headers)
|
||||||
r.raise_for_status()
|
r.raise_for_status()
|
||||||
parent_data = r.json()
|
parent_data = r.json()
|
||||||
|
|
||||||
@ -492,7 +499,8 @@ class Updater(threading.Thread):
|
|||||||
else:
|
else:
|
||||||
status['current_commit_hash'] = version['version']
|
status['current_commit_hash'] = version['version']
|
||||||
try:
|
try:
|
||||||
r = requests.get(repository_url)
|
headers = {'Accept': 'application/vnd.github.v3+json'}
|
||||||
|
r = requests.get(repository_url, headers=headers)
|
||||||
commit = r.json()
|
commit = r.json()
|
||||||
r.raise_for_status()
|
r.raise_for_status()
|
||||||
except requests.exceptions.HTTPError as e:
|
except requests.exceptions.HTTPError as e:
|
||||||
|
73
cps/web.py
73
cps/web.py
@ -47,18 +47,19 @@ from cps import lm, babel, ub, config, get_locale, language_table, app
|
|||||||
from pagination import Pagination
|
from pagination import Pagination
|
||||||
from sqlalchemy.sql.expression import text
|
from sqlalchemy.sql.expression import text
|
||||||
|
|
||||||
from oauth_bb import oauth_check, register_user_with_oauth, logout_oauth_user, get_oauth_status
|
feature_support = dict()
|
||||||
|
try:
|
||||||
'''try:
|
from oauth_bb import oauth_check, register_user_with_oauth, logout_oauth_user, get_oauth_status
|
||||||
oauth_support = True
|
feature_support['oauth'] = True
|
||||||
except ImportError:
|
except ImportError:
|
||||||
oauth_support = False'''
|
feature_support['oauth'] = False
|
||||||
|
oauth_check = {}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import ldap
|
import ldap
|
||||||
ldap_support = True
|
feature_support['ldap'] = True
|
||||||
except ImportError:
|
except ImportError:
|
||||||
ldap_support = False
|
feature_support['ldap'] = False
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from googleapiclient.errors import HttpError
|
from googleapiclient.errors import HttpError
|
||||||
@ -67,15 +68,15 @@ except ImportError:
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
from goodreads.client import GoodreadsClient
|
from goodreads.client import GoodreadsClient
|
||||||
goodreads_support = True
|
feature_support['goodreads'] = True
|
||||||
except ImportError:
|
except ImportError:
|
||||||
goodreads_support = False
|
feature_support['goodreads'] = False
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import Levenshtein
|
import Levenshtein
|
||||||
levenshtein_support = True
|
feature_support['levenshtein'] = True
|
||||||
except ImportError:
|
except ImportError:
|
||||||
levenshtein_support = False
|
feature_support['levenshtein'] = False
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from functools import reduce, wraps
|
from functools import reduce, wraps
|
||||||
@ -84,9 +85,9 @@ except ImportError:
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
import rarfile
|
import rarfile
|
||||||
rar_support=True
|
feature_support['rar'] = True
|
||||||
except ImportError:
|
except ImportError:
|
||||||
rar_support=False
|
feature_support['rar'] = False
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from natsort import natsorted as sort
|
from natsort import natsorted as sort
|
||||||
@ -95,18 +96,17 @@ except ImportError:
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
from urllib.parse import quote
|
from urllib.parse import quote
|
||||||
from imp import reload
|
|
||||||
except ImportError:
|
except ImportError:
|
||||||
from urllib import quote
|
from urllib import quote
|
||||||
|
|
||||||
|
|
||||||
from flask import Blueprint
|
from flask import Blueprint
|
||||||
|
|
||||||
# Global variables
|
# Global variables
|
||||||
|
|
||||||
EXTENSIONS_AUDIO = {'mp3', 'm4a', 'm4b'}
|
EXTENSIONS_AUDIO = {'mp3', 'm4a', 'm4b'}
|
||||||
|
|
||||||
# EXTENSIONS_READER = set(['txt', 'pdf', 'epub', 'zip', 'cbz', 'tar', 'cbt'] + (['rar','cbr'] if rar_support else []))
|
'''EXTENSIONS_READER = set(['txt', 'pdf', 'epub', 'zip', 'cbz', 'tar', 'cbt'] +
|
||||||
|
(['rar','cbr'] if feature_support['rar'] else []))'''
|
||||||
|
|
||||||
|
|
||||||
# custom error page
|
# custom error page
|
||||||
@ -346,8 +346,8 @@ def before_request():
|
|||||||
g.allow_upload = config.config_uploading
|
g.allow_upload = config.config_uploading
|
||||||
g.current_theme = config.config_theme
|
g.current_theme = config.config_theme
|
||||||
g.public_shelfes = ub.session.query(ub.Shelf).filter(ub.Shelf.is_public == 1).order_by(ub.Shelf.name).all()
|
g.public_shelfes = ub.session.query(ub.Shelf).filter(ub.Shelf.is_public == 1).order_by(ub.Shelf.name).all()
|
||||||
if not config.db_configured and request.endpoint not in ('web.basic_configuration', 'login') and '/static/' not in request.path:
|
if not config.db_configured and request.endpoint not in ('admin.basic_configuration', 'login') and '/static/' not in request.path:
|
||||||
return redirect(url_for('web.basic_configuration'))
|
return redirect(url_for('admin.basic_configuration'))
|
||||||
|
|
||||||
|
|
||||||
@web.route("/ajax/emailstat")
|
@web.route("/ajax/emailstat")
|
||||||
@ -373,7 +373,7 @@ def get_comic_book(book_id, book_format, page):
|
|||||||
if bookformat.format.lower() == book_format.lower():
|
if bookformat.format.lower() == book_format.lower():
|
||||||
cbr_file = os.path.join(config.config_calibre_dir, book.path, bookformat.name) + "." + book_format
|
cbr_file = os.path.join(config.config_calibre_dir, book.path, bookformat.name) + "." + book_format
|
||||||
if book_format in ("cbr", "rar"):
|
if book_format in ("cbr", "rar"):
|
||||||
if rar_support == True:
|
if feature_support['rar'] == True:
|
||||||
rarfile.UNRAR_TOOL = config.config_rarfile_location
|
rarfile.UNRAR_TOOL = config.config_rarfile_location
|
||||||
try:
|
try:
|
||||||
rf = rarfile.RarFile(cbr_file)
|
rf = rarfile.RarFile(cbr_file)
|
||||||
@ -636,7 +636,7 @@ def author(book_id, page):
|
|||||||
|
|
||||||
author_info = None
|
author_info = None
|
||||||
other_books = []
|
other_books = []
|
||||||
if goodreads_support and config.config_use_goodreads:
|
if feature_support['goodreads'] and config.config_use_goodreads:
|
||||||
try:
|
try:
|
||||||
gc = GoodreadsClient(config.config_goodreads_api_key, config.config_goodreads_api_secret)
|
gc = GoodreadsClient(config.config_goodreads_api_key, config.config_goodreads_api_secret)
|
||||||
author_info = gc.find_author(author_name=name)
|
author_info = gc.find_author(author_name=name)
|
||||||
@ -688,7 +688,7 @@ def get_unique_other_books(library_books, author_books):
|
|||||||
author_books)
|
author_books)
|
||||||
|
|
||||||
# Fuzzy match book titles
|
# Fuzzy match book titles
|
||||||
if levenshtein_support:
|
if feature_support['levenshtein']:
|
||||||
library_titles = reduce(lambda acc, book: acc + [book.title], library_books, [])
|
library_titles = reduce(lambda acc, book: acc + [book.title], library_books, [])
|
||||||
other_books = filter(lambda author_book: not filter(
|
other_books = filter(lambda author_book: not filter(
|
||||||
lambda library_book:
|
lambda library_book:
|
||||||
@ -1215,7 +1215,7 @@ def read_book(book_id, book_format):
|
|||||||
# copyfile(cbr_file, tmp_file)
|
# copyfile(cbr_file, tmp_file)
|
||||||
return render_title_template('readcbr.html', comicfile=all_name, title=_(u"Read a Book"),
|
return render_title_template('readcbr.html', comicfile=all_name, title=_(u"Read a Book"),
|
||||||
extension=fileext)
|
extension=fileext)
|
||||||
'''if rar_support == True:
|
'''if feature_support['rar']:
|
||||||
extensionList = ["cbr","cbt","cbz"]
|
extensionList = ["cbr","cbt","cbz"]
|
||||||
else:
|
else:
|
||||||
extensionList = ["cbt","cbz"]
|
extensionList = ["cbt","cbz"]
|
||||||
@ -1292,7 +1292,7 @@ def register():
|
|||||||
try:
|
try:
|
||||||
ub.session.add(content)
|
ub.session.add(content)
|
||||||
ub.session.commit()
|
ub.session.commit()
|
||||||
if oauth_support:
|
if feature_support['oauth']:
|
||||||
register_user_with_oauth(content)
|
register_user_with_oauth(content)
|
||||||
helper.send_registration_mail(to_save["email"], to_save["nickname"], password)
|
helper.send_registration_mail(to_save["email"], to_save["nickname"], password)
|
||||||
except Exception:
|
except Exception:
|
||||||
@ -1310,7 +1310,7 @@ def register():
|
|||||||
flash(_(u"This username or e-mail address is already in use."), category="error")
|
flash(_(u"This username or e-mail address is already in use."), category="error")
|
||||||
return render_title_template('register.html', title=_(u"register"), page="register")
|
return render_title_template('register.html', title=_(u"register"), page="register")
|
||||||
|
|
||||||
if oauth_support:
|
if feature_support['oauth']:
|
||||||
register_user_with_oauth()
|
register_user_with_oauth()
|
||||||
return render_title_template('register.html', config=config, title=_(u"register"), page="register")
|
return render_title_template('register.html', config=config, title=_(u"register"), page="register")
|
||||||
|
|
||||||
@ -1318,7 +1318,7 @@ def register():
|
|||||||
@web.route('/login', methods=['GET', 'POST'])
|
@web.route('/login', methods=['GET', 'POST'])
|
||||||
def login():
|
def login():
|
||||||
if not config.db_configured:
|
if not config.db_configured:
|
||||||
return redirect(url_for('web.basic_configuration'))
|
return redirect(url_for('admin.basic_configuration'))
|
||||||
if current_user is not None and current_user.is_authenticated:
|
if current_user is not None and current_user.is_authenticated:
|
||||||
return redirect(url_for('web.index'))
|
return redirect(url_for('web.index'))
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
@ -1358,7 +1358,7 @@ def login():
|
|||||||
def logout():
|
def logout():
|
||||||
if current_user is not None and current_user.is_authenticated:
|
if current_user is not None and current_user.is_authenticated:
|
||||||
logout_user()
|
logout_user()
|
||||||
if oauth_support:
|
if feature_support['oauth']:
|
||||||
logout_oauth_user()
|
logout_oauth_user()
|
||||||
return redirect(url_for('web.login'))
|
return redirect(url_for('web.login'))
|
||||||
|
|
||||||
@ -1370,7 +1370,7 @@ def remote_login():
|
|||||||
ub.session.add(auth_token)
|
ub.session.add(auth_token)
|
||||||
ub.session.commit()
|
ub.session.commit()
|
||||||
|
|
||||||
verify_url = url_for('verify_token', token=auth_token.auth_token, _external=true)
|
verify_url = url_for('web.verify_token', token=auth_token.auth_token, _external=true)
|
||||||
|
|
||||||
return render_title_template('remote_login.html', title=_(u"login"), token=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")
|
verify_url=verify_url, page="remotelogin")
|
||||||
@ -1385,7 +1385,7 @@ def verify_token(token):
|
|||||||
# Token not found
|
# Token not found
|
||||||
if auth_token is None:
|
if auth_token is None:
|
||||||
flash(_(u"Token not found"), category="error")
|
flash(_(u"Token not found"), category="error")
|
||||||
return redirect(url_for('index'))
|
return redirect(url_for('web.index'))
|
||||||
|
|
||||||
# Token expired
|
# Token expired
|
||||||
if datetime.datetime.now() > auth_token.expiration:
|
if datetime.datetime.now() > auth_token.expiration:
|
||||||
@ -1393,7 +1393,7 @@ def verify_token(token):
|
|||||||
ub.session.commit()
|
ub.session.commit()
|
||||||
|
|
||||||
flash(_(u"Token has expired"), category="error")
|
flash(_(u"Token has expired"), category="error")
|
||||||
return redirect(url_for('index'))
|
return redirect(url_for('web.index'))
|
||||||
|
|
||||||
# Update token with user information
|
# Update token with user information
|
||||||
auth_token.user_id = current_user.id
|
auth_token.user_id = current_user.id
|
||||||
@ -1401,7 +1401,7 @@ def verify_token(token):
|
|||||||
ub.session.commit()
|
ub.session.commit()
|
||||||
|
|
||||||
flash(_(u"Success! Please return to your device"), category="success")
|
flash(_(u"Success! Please return to your device"), category="success")
|
||||||
return redirect(url_for('index'))
|
return redirect(url_for('web.index'))
|
||||||
|
|
||||||
|
|
||||||
@web.route('/ajax/verify_token', methods=['POST'])
|
@web.route('/ajax/verify_token', methods=['POST'])
|
||||||
@ -1472,7 +1472,10 @@ def profile():
|
|||||||
downloads = list()
|
downloads = list()
|
||||||
languages = speaking_language()
|
languages = speaking_language()
|
||||||
translations = babel.list_translations() + [LC('en')]
|
translations = babel.list_translations() + [LC('en')]
|
||||||
oauth_status = get_oauth_status()
|
if feature_support['oauth']:
|
||||||
|
oauth_status = get_oauth_status()
|
||||||
|
else:
|
||||||
|
oauth_status = None
|
||||||
for book in content.downloads:
|
for book in content.downloads:
|
||||||
downloadBook = db.session.query(db.Books).filter(db.Books.id == book.book_id).first()
|
downloadBook = db.session.query(db.Books).filter(db.Books.id == book.book_id).first()
|
||||||
if downloadBook:
|
if downloadBook:
|
||||||
@ -1535,9 +1538,11 @@ def profile():
|
|||||||
ub.session.rollback()
|
ub.session.rollback()
|
||||||
flash(_(u"Found an existing account for this e-mail address."), category="error")
|
flash(_(u"Found an existing account for this e-mail address."), category="error")
|
||||||
return render_title_template("user_edit.html", content=content, downloads=downloads,
|
return render_title_template("user_edit.html", content=content, downloads=downloads,
|
||||||
title=_(u"%(name)s's profile", name=current_user.nickname, registered_oauth=oauth_check, oauth_status=oauth_status))
|
title=_(u"%(name)s's profile", name=current_user.nickname,
|
||||||
|
registered_oauth=oauth_check, oauth_status=oauth_status))
|
||||||
flash(_(u"Profile updated"), category="success")
|
flash(_(u"Profile updated"), category="success")
|
||||||
return render_title_template("user_edit.html", translations=translations, profile=1, languages=languages,
|
return render_title_template("user_edit.html", translations=translations, profile=1, languages=languages,
|
||||||
content=content, downloads=downloads, title=_(u"%(name)s's profile",
|
content=content, downloads=downloads, title=_(u"%(name)s's profile",
|
||||||
name=current_user.nickname), page="me", registered_oauth=oauth_check, oauth_status=oauth_status)
|
name=current_user.nickname), page="me", registered_oauth=oauth_check,
|
||||||
|
oauth_status=oauth_status)
|
||||||
|
|
||||||
|
@ -27,14 +27,12 @@ import socket
|
|||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
from email.generator import Generator
|
from email.generator import Generator
|
||||||
from cps import config, db # , app
|
from cps import config, db, app
|
||||||
# import web
|
|
||||||
from flask_babel import gettext as _
|
from flask_babel import gettext as _
|
||||||
import re
|
import re
|
||||||
# import gdriveutils as gd
|
import gdriveutils as gd
|
||||||
from subproc_wrapper import process_open
|
from subproc_wrapper import process_open
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from StringIO import StringIO
|
from StringIO import StringIO
|
||||||
from email.MIMEBase import MIMEBase
|
from email.MIMEBase import MIMEBase
|
||||||
@ -90,8 +88,8 @@ def get_attachment(bookpath, filename):
|
|||||||
data = file_.read()
|
data = file_.read()
|
||||||
file_.close()
|
file_.close()
|
||||||
except IOError as e:
|
except IOError as e:
|
||||||
# web.app.logger.exception(e) # traceback.print_exc()
|
app.logger.exception(e) # traceback.print_exc()
|
||||||
# web.app.logger.error(u'The requested file could not be read. Maybe wrong permissions?')
|
app.logger.error(u'The requested file could not be read. Maybe wrong permissions?')
|
||||||
return None
|
return None
|
||||||
|
|
||||||
attachment = MIMEBase('application', 'octet-stream')
|
attachment = MIMEBase('application', 'octet-stream')
|
||||||
@ -116,8 +114,7 @@ class emailbase():
|
|||||||
|
|
||||||
def send(self, strg):
|
def send(self, strg):
|
||||||
"""Send `strg' to the server."""
|
"""Send `strg' to the server."""
|
||||||
if self.debuglevel > 0:
|
app.logger.debug('send:' + repr(strg[:300]))
|
||||||
print('send:', repr(strg[:300]), file=sys.stderr)
|
|
||||||
if hasattr(self, 'sock') and self.sock:
|
if hasattr(self, 'sock') and self.sock:
|
||||||
try:
|
try:
|
||||||
if self.transferSize:
|
if self.transferSize:
|
||||||
@ -141,6 +138,9 @@ class emailbase():
|
|||||||
else:
|
else:
|
||||||
raise smtplib.SMTPServerDisconnected('please run connect() first')
|
raise smtplib.SMTPServerDisconnected('please run connect() first')
|
||||||
|
|
||||||
|
def _print_debug(self, *args):
|
||||||
|
app.logger.debug(args)
|
||||||
|
|
||||||
def getTransferStatus(self):
|
def getTransferStatus(self):
|
||||||
if self.transferSize:
|
if self.transferSize:
|
||||||
lock2 = threading.Lock()
|
lock2 = threading.Lock()
|
||||||
@ -254,14 +254,14 @@ class WorkerThread(threading.Thread):
|
|||||||
# if it does - mark the conversion task as complete and return a success
|
# if it does - mark the conversion task as complete and return a success
|
||||||
# this will allow send to kindle workflow to continue to work
|
# this will allow send to kindle workflow to continue to work
|
||||||
if os.path.isfile(file_path + format_new_ext):
|
if os.path.isfile(file_path + format_new_ext):
|
||||||
# web.app.logger.info("Book id %d already converted to %s", bookid, format_new_ext)
|
app.logger.info("Book id %d already converted to %s", bookid, format_new_ext)
|
||||||
cur_book = db.session.query(db.Books).filter(db.Books.id == bookid).first()
|
cur_book = db.session.query(db.Books).filter(db.Books.id == bookid).first()
|
||||||
self.queue[self.current]['path'] = file_path
|
self.queue[self.current]['path'] = file_path
|
||||||
self.queue[self.current]['title'] = cur_book.title
|
self.queue[self.current]['title'] = cur_book.title
|
||||||
self._handleSuccess()
|
self._handleSuccess()
|
||||||
return file_path + format_new_ext
|
return file_path + format_new_ext
|
||||||
else:
|
else:
|
||||||
web.app.logger.info("Book id %d - target format of %s does not exist. Moving forward with convert.", bookid, format_new_ext)
|
app.logger.info("Book id %d - target format of %s does not exist. Moving forward with convert.", bookid, format_new_ext)
|
||||||
|
|
||||||
# check if converter-executable is existing
|
# check if converter-executable is existing
|
||||||
if not os.path.exists(config.config_converterpath):
|
if not os.path.exists(config.config_converterpath):
|
||||||
@ -274,22 +274,22 @@ class WorkerThread(threading.Thread):
|
|||||||
if format_old_ext == '.epub' and format_new_ext == '.mobi':
|
if format_old_ext == '.epub' and format_new_ext == '.mobi':
|
||||||
if config.config_ebookconverter == 1:
|
if config.config_ebookconverter == 1:
|
||||||
'''if os.name == 'nt':
|
'''if os.name == 'nt':
|
||||||
command = web.ub.config.config_converterpath + u' "' + file_path + u'.epub"'
|
command = config.config_converterpath + u' "' + file_path + u'.epub"'
|
||||||
if sys.version_info < (3, 0):
|
if sys.version_info < (3, 0):
|
||||||
command = command.encode(sys.getfilesystemencoding())
|
command = command.encode(sys.getfilesystemencoding())
|
||||||
else:'''
|
else:'''
|
||||||
command = [config.config_converterpath, file_path + u'.epub']
|
command = [config.config_converterpath, file_path + u'.epub']
|
||||||
quotes = (1)
|
quotes = [1]
|
||||||
if config.config_ebookconverter == 2:
|
if config.config_ebookconverter == 2:
|
||||||
# Linux py2.7 encode as list without quotes no empty element for parameters
|
# Linux py2.7 encode as list without quotes no empty element for parameters
|
||||||
# linux py3.x no encode and as list without quotes no empty element for parameters
|
# linux py3.x no encode and as list without quotes no empty element for parameters
|
||||||
# windows py2.7 encode as string with quotes empty element for parameters is okay
|
# windows py2.7 encode as string with quotes empty element for parameters is okay
|
||||||
# windows py 3.x no encode and as string with quotes empty element for parameters is okay
|
# windows py 3.x no encode and as string with quotes empty element for parameters is okay
|
||||||
# separate handling for windows and linux
|
# separate handling for windows and linux
|
||||||
quotes = (1,2)
|
quotes = [1,2]
|
||||||
'''if os.name == 'nt':
|
'''if os.name == 'nt':
|
||||||
command = web.ub.config.config_converterpath + u' "' + file_path + format_old_ext + u'" "' + \
|
command = config.config_converterpath + u' "' + file_path + format_old_ext + u'" "' + \
|
||||||
file_path + format_new_ext + u'" ' + web.ub.config.config_calibre
|
file_path + format_new_ext + u'" ' + config.config_calibre
|
||||||
if sys.version_info < (3, 0):
|
if sys.version_info < (3, 0):
|
||||||
command = command.encode(sys.getfilesystemencoding())
|
command = command.encode(sys.getfilesystemencoding())
|
||||||
else:'''
|
else:'''
|
||||||
@ -317,13 +317,13 @@ class WorkerThread(threading.Thread):
|
|||||||
if conv_error:
|
if conv_error:
|
||||||
error_message = _(u"Kindlegen failed with Error %(error)s. Message: %(message)s",
|
error_message = _(u"Kindlegen failed with Error %(error)s. Message: %(message)s",
|
||||||
error=conv_error.group(1), message=conv_error.group(2).strip())
|
error=conv_error.group(1), message=conv_error.group(2).strip())
|
||||||
web.app.logger.debug("convert_kindlegen: " + nextline)
|
app.logger.debug("convert_kindlegen: " + nextline)
|
||||||
else:
|
else:
|
||||||
while p.poll() is None:
|
while p.poll() is None:
|
||||||
nextline = p.stdout.readline()
|
nextline = p.stdout.readline()
|
||||||
if os.name == 'nt' and sys.version_info < (3, 0):
|
if os.name == 'nt' and sys.version_info < (3, 0):
|
||||||
nextline = nextline.decode('windows-1252')
|
nextline = nextline.decode('windows-1252')
|
||||||
web.app.logger.debug(nextline.strip('\r\n'))
|
app.logger.debug(nextline.strip('\r\n'))
|
||||||
# parse progress string from calibre-converter
|
# parse progress string from calibre-converter
|
||||||
progress = re.search("(\d+)%\s.*", nextline)
|
progress = re.search("(\d+)%\s.*", nextline)
|
||||||
if progress:
|
if progress:
|
||||||
@ -353,7 +353,7 @@ class WorkerThread(threading.Thread):
|
|||||||
return file_path + format_new_ext
|
return file_path + format_new_ext
|
||||||
else:
|
else:
|
||||||
error_message = format_new_ext.upper() + ' format not found on disk'
|
error_message = format_new_ext.upper() + ' format not found on disk'
|
||||||
# web.app.logger.info("ebook converter failed with error while converting book")
|
app.logger.info("ebook converter failed with error while converting book")
|
||||||
if not error_message:
|
if not error_message:
|
||||||
error_message = 'Ebook converter failed with unknown error'
|
error_message = 'Ebook converter failed with unknown error'
|
||||||
self._handleError(error_message)
|
self._handleError(error_message)
|
||||||
@ -414,7 +414,6 @@ class WorkerThread(threading.Thread):
|
|||||||
def _send_raw_email(self):
|
def _send_raw_email(self):
|
||||||
self.queue[self.current]['starttime'] = datetime.now()
|
self.queue[self.current]['starttime'] = datetime.now()
|
||||||
self.UIqueue[self.current]['formStarttime'] = self.queue[self.current]['starttime']
|
self.UIqueue[self.current]['formStarttime'] = self.queue[self.current]['starttime']
|
||||||
# self.queue[self.current]['status'] = STAT_STARTED
|
|
||||||
self.UIqueue[self.current]['stat'] = STAT_STARTED
|
self.UIqueue[self.current]['stat'] = STAT_STARTED
|
||||||
obj=self.queue[self.current]
|
obj=self.queue[self.current]
|
||||||
# create MIME message
|
# create MIME message
|
||||||
@ -446,8 +445,11 @@ class WorkerThread(threading.Thread):
|
|||||||
# send email
|
# send email
|
||||||
timeout = 600 # set timeout to 5mins
|
timeout = 600 # set timeout to 5mins
|
||||||
|
|
||||||
org_stderr = sys.stderr
|
# redirect output to logfile on python2 pn python3 debugoutput is caught with overwritten
|
||||||
sys.stderr = StderrLogger()
|
# _print_debug function
|
||||||
|
if sys.version_info < (3, 0):
|
||||||
|
org_smtpstderr = smtplib.stderr
|
||||||
|
smtplib.stderr = StderrLogger()
|
||||||
|
|
||||||
if use_ssl == 2:
|
if use_ssl == 2:
|
||||||
self.asyncSMTP = email_SSL(obj['settings']["mail_server"], obj['settings']["mail_port"], timeout)
|
self.asyncSMTP = email_SSL(obj['settings']["mail_server"], obj['settings']["mail_port"], timeout)
|
||||||
@ -455,7 +457,7 @@ class WorkerThread(threading.Thread):
|
|||||||
self.asyncSMTP = email(obj['settings']["mail_server"], obj['settings']["mail_port"], timeout)
|
self.asyncSMTP = email(obj['settings']["mail_server"], obj['settings']["mail_port"], timeout)
|
||||||
|
|
||||||
# link to logginglevel
|
# link to logginglevel
|
||||||
if web.ub.config.config_log_level != logging.DEBUG:
|
if config.config_log_level != logging.DEBUG:
|
||||||
self.asyncSMTP.set_debuglevel(0)
|
self.asyncSMTP.set_debuglevel(0)
|
||||||
else:
|
else:
|
||||||
self.asyncSMTP.set_debuglevel(1)
|
self.asyncSMTP.set_debuglevel(1)
|
||||||
@ -466,7 +468,9 @@ class WorkerThread(threading.Thread):
|
|||||||
self.asyncSMTP.sendmail(obj['settings']["mail_from"], obj['recipent'], msg)
|
self.asyncSMTP.sendmail(obj['settings']["mail_from"], obj['recipent'], msg)
|
||||||
self.asyncSMTP.quit()
|
self.asyncSMTP.quit()
|
||||||
self._handleSuccess()
|
self._handleSuccess()
|
||||||
sys.stderr = org_stderr
|
|
||||||
|
if sys.version_info < (3, 0):
|
||||||
|
smtplib.stderr = org_smtpstderr
|
||||||
|
|
||||||
except (MemoryError) as e:
|
except (MemoryError) as e:
|
||||||
self._handleError(u'Error sending email: ' + e.message)
|
self._handleError(u'Error sending email: ' + e.message)
|
||||||
@ -497,7 +501,7 @@ class WorkerThread(threading.Thread):
|
|||||||
return retVal
|
return retVal
|
||||||
|
|
||||||
def _handleError(self, error_message):
|
def _handleError(self, error_message):
|
||||||
web.app.logger.error(error_message)
|
app.logger.error(error_message)
|
||||||
# self.queue[self.current]['status'] = STAT_FAIL
|
# self.queue[self.current]['status'] = STAT_FAIL
|
||||||
self.UIqueue[self.current]['stat'] = STAT_FAIL
|
self.UIqueue[self.current]['stat'] = STAT_FAIL
|
||||||
self.UIqueue[self.current]['progress'] = "100 %"
|
self.UIqueue[self.current]['progress'] = "100 %"
|
||||||
@ -519,13 +523,12 @@ class StderrLogger(object):
|
|||||||
buffer = ''
|
buffer = ''
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.logger = web.app.logger
|
self.logger = app.logger
|
||||||
|
|
||||||
def write(self, message):
|
def write(self, message):
|
||||||
try:
|
try:
|
||||||
if message == '\n':
|
if message == '\n':
|
||||||
self.logger.debug(self.buffer)
|
self.logger.debug(self.buffer.replace("\n","\\n"))
|
||||||
print(self.buffer)
|
|
||||||
self.buffer = ''
|
self.buffer = ''
|
||||||
else:
|
else:
|
||||||
self.buffer += message
|
self.buffer += message
|
||||||
|
@ -11,12 +11,19 @@ PyDrive==1.3.1
|
|||||||
PyYAML==3.12
|
PyYAML==3.12
|
||||||
rsa==3.4.2
|
rsa==3.4.2
|
||||||
six==1.10.0
|
six==1.10.0
|
||||||
|
|
||||||
# goodreads
|
# goodreads
|
||||||
goodreads>=0.3.2
|
goodreads>=0.3.2
|
||||||
python-Levenshtein>=0.12.0
|
python-Levenshtein>=0.12.0
|
||||||
|
|
||||||
# ldap login
|
# ldap login
|
||||||
python_ldap>=3.0.0
|
python_ldap>=3.0.0
|
||||||
|
|
||||||
# other
|
# other
|
||||||
lxml>=3.8.0
|
lxml>=3.8.0
|
||||||
rarfile>=2.7
|
rarfile>=2.7
|
||||||
natsort>=2.2.0
|
natsort>=2.2.0
|
||||||
|
|
||||||
|
# Oauth Login
|
||||||
|
flask-dance>=0.13.0
|
||||||
|
sqlalchemy_utils>=0.33.5
|
||||||
|
@ -13,5 +13,3 @@ SQLAlchemy>=1.1.0
|
|||||||
tornado>=4.1
|
tornado>=4.1
|
||||||
Wand>=0.4.4
|
Wand>=0.4.4
|
||||||
unidecode>=0.04.19
|
unidecode>=0.04.19
|
||||||
flask-dance>=0.13.0
|
|
||||||
sqlalchemy_utils>=0.33.5
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user