This commit is contained in:
xlivevil
2021-08-05 21:39:04 +08:00
56 changed files with 5275 additions and 4964 deletions
+3 -2
View File
@@ -102,8 +102,9 @@ def create_app():
log.info('Starting Calibre Web...')
if sys.version_info < (3, 0):
log.info('Python2 is EOL since end of 2019, this version of Calibre-Web is no longer supporting Python2 please consider upgrading to Python3')
print('Python2 is EOL since end of 2019, this version of Calibre-Web is no longer supporting Python2 please consider upgrading to Python3')
log.info('*** Python2 is EOL since end of 2019, this version of Calibre-Web is no longer supporting Python2, please update your installation to Python3 ***')
print('*** Python2 is EOL since end of 2019, this version of Calibre-Web is no longer supporting Python2, please update your installation to Python3 ***')
sys.exit(5)
Principal(app)
lm.init_app(app)
app.secret_key = os.getenv('SECRET_KEY', config_sql.get_flask_session_key(ub.session))
+5 -1
View File
@@ -34,6 +34,7 @@ from babel.dates import format_datetime
from flask import Blueprint, flash, redirect, url_for, abort, request, make_response, send_from_directory, g, Response
from flask_login import login_required, current_user, logout_user, confirm_login
from flask_babel import gettext as _
from flask import session as flask_session
from sqlalchemy import and_
from sqlalchemy.orm.attributes import flag_modified
from sqlalchemy.exc import IntegrityError, OperationalError, InvalidRequestError
@@ -98,8 +99,11 @@ def admin_required(f):
@admi.before_app_request
def before_request():
# make remember me function work
if current_user.is_authenticated:
confirm_login()
if not ub.check_user_session(current_user.id, flask_session.get('_id')) and 'opds' not in request.path:
logout_user()
g.constants = constants
g.user = current_user
g.allow_registration = config.config_public_reg
@@ -1796,7 +1800,7 @@ def import_ldap_users():
def extract_user_data_from_field(user, field):
match = re.search(field + r"=([\d\s\w-]+)", user, re.IGNORECASE | re.UNICODE)
match = re.search(field + r"=([\.\d\s\w-]+)", user, re.IGNORECASE | re.UNICODE)
if match:
return match.group(1)
else:
+3 -1
View File
@@ -39,7 +39,9 @@ def _get_command_version(path, pattern, argument=None):
if argument:
command.append(argument)
try:
return process_wait(command, pattern=pattern).string
match = process_wait(command, pattern=pattern)
if isinstance(match, re.Match):
return match.string
except Exception as ex:
log.warning("%s: %s", path, ex)
return _EXECUTION_ERROR
+4
View File
@@ -692,6 +692,10 @@ class CalibreDB():
query = self.session.query(database)
if len(join) == 6:
query = query.outerjoin(join[0], join[1]).outerjoin(join[2]).outerjoin(join[3], join[4]).outerjoin(join[5])
if len(join) == 5:
query = query.outerjoin(join[0], join[1]).outerjoin(join[2]).outerjoin(join[3], join[4])
if len(join) == 4:
query = query.outerjoin(join[0], join[1]).outerjoin(join[2]).outerjoin(join[3])
if len(join) == 3:
query = query.outerjoin(join[0], join[1]).outerjoin(join[2])
elif len(join) == 2:
+4
View File
@@ -3293,6 +3293,10 @@ div.btn-group[role=group][aria-label="Download, send to Kindle, reading"] .dropd
left: 0 !important;
overflow-y: auto;
}
#add-to-shelves {
max-height: calc(100% - 120px);
overflow-y: auto;
}
.dropdown-menu > li > a {
color: hsla(0, 0%, 100%, .7);
+1 -1
View File
@@ -706,7 +706,7 @@ $(function() {
method:"post",
contentType: "application/json; charset=utf-8",
dataType: "json",
url: window.location.pathname + "/../../ajax/view",
url: window.location.pathname + "/../ajax/view",
data: "{\"series\": {\"series_view\": \""+ view +"\"}}",
success: function success() {
location.reload();
+6 -1
View File
@@ -74,7 +74,12 @@ $(function() {
});
});
$("#merge_books").click(function() {
$("#merge_books").click(function(event) {
if ($(this).hasClass("disabled")) {
event.stopPropagation()
} else {
$('#mergeModal').modal("show");
}
$.ajax({
method:"post",
contentType: "application/json; charset=utf-8",
+1 -1
View File
@@ -21,7 +21,7 @@
<h2 class="{{page}}">{{_(title)}}</h2>
<div class="col-xs-12 col-sm-6">
<div class="row form-group">
<div class="btn btn-default disabled" id="merge_books" data-toggle="modal" data-target="#mergeModal" aria-disabled="true">{{_('Merge selected books')}}</div>
<div class="btn btn-default disabled" id="merge_books" aria-disabled="true">{{_('Merge selected books')}}</div>
<div class="btn btn-default disabled" id="delete_selection" aria-disabled="true">{{_('Remove Selections')}}</div>
</div>
<div class="row form-group">
+1 -1
View File
@@ -20,7 +20,7 @@
<input type="checkbox" id="config_use_google_drive" name="config_use_google_drive" data-control="gdrive_settings" {% if config.config_use_google_drive %}checked{% endif %} >
<label for="config_use_google_drive">{{_('Use Google Drive?')}}</label>
</div>
{% if not gdriveError %}
{% if not gdriveError and config.config_use_google_drive %}
{% if show_authenticate_google_drive and config.config_use_google_drive %}
<div class="form-group required">
<a href="{{ url_for('gdrive.authenticate_google_drive') }}" id="gdrive_auth" class="btn btn-primary">{{_('Authenticate Google Drive')}}</a>
+2 -1
View File
@@ -122,7 +122,8 @@
{% endif %}
{% if entry.series|length > 0 %}
<p>{{_('Book')}} {{entry.series_index|formatfloat(2)}} {{_('of')}} <a href="{{url_for('web.books_list', data='series', sort_param='stored', book_id=entry.series[0].id)}}">{{entry.series[0].name}}</a></p>
<p>{{_("Book %(index)s of %(range)s", index=entry.series_index|formatfloat(2), range=("<a href='" + url_for('web.books_list', data='series', sort_param='stored', book_id=entry.series[0].id) + "'>" + entry.series[0].name + "</a>")|safe) }}</p>
{% endif %}
{% if entry.languages.__len__() > 0 %}
+1 -2
View File
@@ -18,8 +18,7 @@
<button class="btn btn-primary char">{{char.char}}</button>
{% endfor %}
</div>
<button class="update-view btn btn-primary" href="#" data-target="series_view" id='list-button' data-view="list">List</button>
<button class="update-view btn btn-primary" data-target="series_view" id="list-button" data-view="list">List</button>
</div>
{% if entries[0] %}
+1 -1
View File
@@ -20,7 +20,7 @@
</div>
{% if data == "series" %}
<button class="update-view btn btn-primary" href="#" data-target="series_view" id='grid-button' data-view="grid">Grid</button>
<button class="update-view btn btn-primary" data-target="series_view" id="grid-button" data-view="grid">Grid</button>
{% endif %}
</div>
<div class="container">
+2 -2
View File
@@ -15,10 +15,10 @@
<div class="row">
<div class="col-xs-6 col-sm-7">
{% if log_enable %}
<a class="btn btn-default" id="log_file" href="{{url_for('admin.download_log', logtype=0)}}">{{_('Download Calibre-Web Log')}}</a>
<a class="btn btn-default" id="log_file_0" href="{{url_for('admin.download_log', logtype=0)}}">{{_('Download Calibre-Web Log')}}</a>
{% endif %}
{% if accesslog_enable %}
<a class="btn btn-default" id="log_file" href="{{url_for('admin.download_log', logtype=1)}}">{{_('Download Access Log')}}</a>
<a class="btn btn-default" id="log_file_1" href="{{url_for('admin.download_log', logtype=1)}}">{{_('Download Access Log')}}</a>
{% endif %}
</div>
</div>
+1 -1
View File
@@ -147,7 +147,7 @@
{{ user_checkbox_row("role", "delete_role", _('Delete'), visiblility, all_roles)}}
{{ user_checkbox_row("role", "edit_shelf_role", _('Edit Public Shelves'), visiblility, all_roles)}}
{% if kobo_support %}
{{ user_single_checkbox_row("kobo_only_shelves_sync", _('Sync Selected Shelves with Kobo'))}}
{{ user_single_checkbox_row("kobo_only_shelves_sync", _('Sync selected Shelves with Kobo'))}}
{% endif %}
{{ user_checkbox_row("sidebar_view", "detail_random", _('Show Random Books in Detail View'), visiblility, sidebar_settings)}}
{{ user_checkbox_row("sidebar_view", "sidebar_language", _('Show language selection'), visiblility, sidebar_settings)}}
Binary file not shown.
File diff suppressed because it is too large Load Diff
Binary file not shown.
File diff suppressed because it is too large Load Diff
Binary file not shown.
File diff suppressed because it is too large Load Diff
Binary file not shown.
File diff suppressed because it is too large Load Diff
Binary file not shown.
File diff suppressed because it is too large Load Diff
Binary file not shown.
File diff suppressed because it is too large Load Diff
Binary file not shown.
File diff suppressed because it is too large Load Diff
Binary file not shown.
File diff suppressed because it is too large Load Diff
Binary file not shown.
File diff suppressed because it is too large Load Diff
Binary file not shown.
File diff suppressed because it is too large Load Diff
Binary file not shown.
File diff suppressed because it is too large Load Diff
Binary file not shown.
File diff suppressed because it is too large Load Diff
Binary file not shown.
File diff suppressed because it is too large Load Diff
Binary file not shown.
File diff suppressed because it is too large Load Diff
Binary file not shown.
File diff suppressed because it is too large Load Diff
Binary file not shown.
File diff suppressed because it is too large Load Diff
Binary file not shown.
File diff suppressed because it is too large Load Diff
Binary file not shown.
File diff suppressed because it is too large Load Diff
+44 -1
View File
@@ -27,6 +27,8 @@ from flask import session as flask_session
from binascii import hexlify
from flask_login import AnonymousUserMixin, current_user
from flask_login import user_logged_in
from contextlib import contextmanager
try:
from flask_dance.consumer.backend.sqla import OAuthConsumerMixin
@@ -61,6 +63,36 @@ Base = declarative_base()
searched_ids = {}
def signal_store_user_session(object, user):
store_user_session()
def store_user_session():
if flask_session.get('_user_id', ""):
try:
if not check_user_session(flask_session.get('_user_id', ""), flask_session.get('_id', "")):
user_session = User_Sessions(flask_session.get('_user_id', ""), flask_session.get('_id', ""))
session.add(user_session)
session.commit()
except (exc.OperationalError, exc.InvalidRequestError):
session.rollback()
# log.debug(flask_session.get('_id', ""))
def delete_user_session(user_id, session_key):
try:
# log.debug(session_key)
session.query(User_Sessions).filter(User_Sessions.user_id==user_id,
User_Sessions.session_key==session_key).delete()
session.commit()
except (exc.OperationalError, exc.InvalidRequestError):
session.rollback()
def check_user_session(user_id, session_key):
return bool(session.query(User_Sessions).filter(User_Sessions.user_id==user_id,
User_Sessions.session_key==session_key).one_or_none())
user_logged_in.connect(signal_store_user_session)
def store_ids(result):
ids = list()
for element in result:
@@ -72,7 +104,7 @@ class UserBase:
@property
def is_authenticated(self):
return True
return self.is_active
def _has_role(self, role_flag):
return constants.has_flag(self.role, role_flag)
@@ -261,6 +293,17 @@ class Anonymous(AnonymousUserMixin, UserBase):
flask_session['view'][page][prop] = value
return None
class User_Sessions(Base):
__tablename__ = 'user_session'
id = Column(Integer, primary_key=True)
user_id = Column(Integer, ForeignKey('user.id'))
session_key = Column(String, default="")
def __init__(self, user_id, session_key):
self.user_id = user_id
self.session_key = session_key
# Baseclass representing Shelfs in calibre-web in app.db
class Shelf(Base):
+3 -1
View File
@@ -21,7 +21,8 @@ import binascii
from sqlalchemy.sql.expression import func
from werkzeug.security import check_password_hash
from flask_login import login_required
from flask_login import login_required, login_user
from . import lm, ub, config, constants, services
@@ -58,6 +59,7 @@ def load_user_from_request(request):
if rp_header_username:
user = _fetch_user_by_name(rp_header_username)
if user:
login_user(user)
return user
auth_header = request.headers.get("Authorization")
+13 -2
View File
@@ -423,7 +423,11 @@ def render_rated_books(page, book_id, order):
entries, random, pagination = calibre_db.fill_indexpage(page, 0,
db.Books,
db.Books.ratings.any(db.Ratings.rating > 9),
order)
order,
db.books_series_link,
db.Books.id == db.books_series_link.c.book,
db.Series)
return render_title_template('index.html', random=random, entries=entries, pagination=pagination,
id=book_id, title=_(u"Top Rated Books"), page="rated")
else:
@@ -629,6 +633,9 @@ def render_read_books(page, are_read, as_xml=False, order=None):
db.Books,
db_filter,
order,
db.books_series_link,
db.Books.id == db.books_series_link.c.book,
db.Series,
ub.ReadBook, db.Books.id == ub.ReadBook.book_id)
else:
try:
@@ -640,6 +647,9 @@ def render_read_books(page, are_read, as_xml=False, order=None):
db.Books,
db_filter,
order,
db.books_series_link,
db.Books.id == db.books_series_link.c.book,
db.Series,
db.cc_classes[config.config_read_column])
except (KeyError, AttributeError):
log.error("Custom Column No.%d is not existing in calibre database", config.config_read_column)
@@ -787,7 +797,7 @@ def list_books():
db.books_series_link, db.Books.id == db.books_series_link.c.book, db.Series
elif sort == "languages":
order = [db.Languages.lang_code.asc()] if order == "asc" else [db.Languages.lang_code.desc()]
join = db.books_languages_link,db.Books.id == db.books_languages_link.c.book, db.Languages
join = db.books_languages_link, db.Books.id == db.books_languages_link.c.book, db.Languages
elif order and sort in ["sort", "title", "authors_sort", "series_index"]:
order = [text(sort + " " + order)]
elif not state:
@@ -1582,6 +1592,7 @@ def login():
@login_required
def logout():
if current_user is not None and current_user.is_authenticated:
ub.delete_user_session(current_user.id, flask_session.get('_id',""))
logout_user()
if feature_support['oauth'] and (config.config_login_type == 2 or config.config_login_type == 3):
logout_oauth_user()