1
0
mirror of https://github.com/janeczku/calibre-web synced 2024-12-27 10:30:31 +00:00

moved language selection back, default value for restriction columns

Merge branch 'master' into Develop

# Conflicts:
#	cps/editbooks.py
This commit is contained in:
Ozzieisaacs 2020-01-05 08:43:00 +01:00
commit cabad83418
8 changed files with 110 additions and 45 deletions

View File

@ -185,6 +185,8 @@ def update_view_configuration():
config.config_default_show = sum(int(k[5:]) for k in to_save if k.startswith('show_')) config.config_default_show = sum(int(k[5:]) for k in to_save if k.startswith('show_'))
'''if "Show_mature_content" in to_save: '''if "Show_mature_content" in to_save:
config.config_default_show |= constants.MATURE_CONTENT''' config.config_default_show |= constants.MATURE_CONTENT'''
if "Show_detail_random" in to_save:
config.config_default_show |= constants.DETAIL_RANDOM
config.save() config.save()
flash(_(u"Calibre-Web configuration updated"), category="success") flash(_(u"Calibre-Web configuration updated"), category="success")
@ -470,6 +472,10 @@ def _configuration_update_helper():
_config_int("config_updatechannel") _config_int("config_updatechannel")
# Reverse proxy login configuration
_config_checkbox("config_allow_reverse_proxy_header_login")
_config_string("config_reverse_proxy_login_header_name")
# GitHub OAuth configuration # GitHub OAuth configuration
if config.config_login_type == constants.LOGIN_OAUTH: if config.config_login_type == constants.LOGIN_OAUTH:
active_oauths = 0 active_oauths = 0

View File

@ -73,11 +73,11 @@ class _Settings(_Base):
config_default_role = Column(SmallInteger, default=0) config_default_role = Column(SmallInteger, default=0)
config_default_show = Column(SmallInteger, default=6143) config_default_show = Column(SmallInteger, default=6143)
config_columns_to_ignore = Column(String) config_columns_to_ignore = Column(String)
config_restricted_tags = Column(String) config_restricted_tags = Column(String, default="")
config_allowed_tags = Column(String) config_allowed_tags = Column(String, default="")
config_restricted_column = Column(SmallInteger, default=0) config_restricted_column = Column(SmallInteger, default=0)
config_restricted_column_value = Column(String) config_restricted_column_value = Column(String, default="")
config_allowed_column_value = Column(String) config_allowed_column_value = Column(String, default="")
config_use_google_drive = Column(Boolean, default=False) config_use_google_drive = Column(Boolean, default=False)
config_google_drive_folder = Column(String) config_google_drive_folder = Column(String)
@ -111,6 +111,9 @@ class _Settings(_Base):
config_updatechannel = Column(Integer, default=constants.UPDATE_STABLE) config_updatechannel = Column(Integer, default=constants.UPDATE_STABLE)
config_reverse_proxy_login_header_name = Column(String)
config_allow_reverse_proxy_header_login = Column(Boolean, default=False)
def __repr__(self): def __repr__(self):
return self.__class__.__name__ return self.__class__.__name__
@ -264,8 +267,7 @@ class _ConfigSQL(object):
for k, v in self.__dict__.items(): for k, v in self.__dict__.items():
if k[0] == '_': if k[0] == '_':
continue continue
if hasattr(s, k): # and getattr(s, k, None) != v: if hasattr(s, k):
# log.debug("_Settings save '%s' = %r", k, v)
setattr(s, k, v) setattr(s, k, v)
log.debug("_ConfigSQL updating storage") log.debug("_ConfigSQL updating storage")
@ -288,12 +290,18 @@ def _migrate_table(session, orm_class):
try: try:
session.query(column).first() session.query(column).first()
except exc.OperationalError as err: except exc.OperationalError as err:
log.debug("%s: %s", column_name, err) log.debug("%s: %s", column_name, err.args[0])
if column.default is not None: if column.default is not None:
if sys.version_info < (3, 0): if sys.version_info < (3, 0):
if isinstance(column.default.arg,unicode): if isinstance(column.default.arg,unicode):
column.default.arg = column.default.arg.encode('utf-8') column.default.arg = column.default.arg.encode('utf-8')
column_default = "" if column.default is None else ("DEFAULT %r" % column.default.arg) if column.default is None:
column_default = ""
else:
if isinstance(column.default.arg, bool):
column_default = ("DEFAULT %r" % int(column.default.arg))
else:
column_default = ("DEFAULT %r" % column.default.arg)
alter_table = "ALTER TABLE %s ADD COLUMN `%s` %s %s" % (orm_class.__tablename__, alter_table = "ALTER TABLE %s ADD COLUMN `%s` %s %s" % (orm_class.__tablename__,
column_name, column_name,
column.type, column.type,

View File

@ -656,6 +656,12 @@ def upload():
db_language = db.Languages(input_language) db_language = db.Languages(input_language)
db.session.add(db_language) db.session.add(db_language)
# If the language of the file is excluded from the users view, it's not imported, to allow the user to view
# the book it's language is set to the filter language
if db_language != current_user.filter_language() and current_user.filter_language() != "all":
db_language = db.session.query(db.Languages).\
filter(db.Languages.lang_code == current_user.filter_language()).first()
# combine path and normalize path from windows systems # combine path and normalize path from windows systems
path = os.path.join(author_dir, title_dir).replace('\\', '/') path = os.path.join(author_dir, title_dir).replace('\\', '/')
db_book = db.Books(title, "", db_author.sort, datetime.datetime.now(), datetime.datetime(101, 1, 1), db_book = db.Books(title, "", db_author.sort, datetime.datetime.now(), datetime.datetime(101, 1, 1),
@ -688,8 +694,10 @@ def upload():
# save data to database, reread data # save data to database, reread data
db.session.commit() db.session.commit()
db.update_title_sort(config) db.update_title_sort(config)
book = db.session.query(db.Books).filter(db.Books.id == book_id).filter(common_filters()).first() # Reread book. It's important not to filter the result, as it could have language which hide it from
# upload book to gdrive if necessary and add "(bookid)" to folder name # current users view (tags are not stored/extracted from metadata and could also be limited)
book = db.session.query(db.Books).filter(db.Books.id == book_id).first()
# upload book to gdrive if nesseccary and add "(bookid)" to folder name
if config.config_use_google_drive: if config.config_use_google_drive:
gdriveutils.updateGdriveCalibreFromLocal() gdriveutils.updateGdriveCalibreFromLocal()
error = helper.update_dir_stucture(book.id, config.config_calibre_dir) error = helper.update_dir_stucture(book.id, config.config_calibre_dir)

View File

@ -30,6 +30,7 @@ from sqlalchemy.sql.expression import func, or_, and_
from . import logger, ub, searched_ids, db from . import logger, ub, searched_ids, db
from .web import render_title_template from .web import render_title_template
from .helper import common_filters
shelf = Blueprint('shelf', __name__) shelf = Blueprint('shelf', __name__)
@ -281,16 +282,10 @@ def show_shelf(shelf_type, shelf_id):
if shelf: if shelf:
page = "shelf.html" if shelf_type == 1 else 'shelfdown.html' page = "shelf.html" if shelf_type == 1 else 'shelfdown.html'
books_in_shelf = ub.session.query(ub.BookShelf).filter(ub.BookShelf.shelf == shelf_id).order_by( books_in_shelf = ub.session.query(ub.BookShelf).filter(ub.BookShelf.shelf == shelf_id)\
ub.BookShelf.order.asc()).all() .order_by(ub.BookShelf.order.asc()).all()
for book in books_in_shelf: books_list = [ b.book_id for b in books_in_shelf]
cur_book = db.session.query(db.Books).filter(db.Books.id == book.book_id).first() result = db.session.query(db.Books).filter(db.Books.id.in_(books_list)).filter(common_filters()).all()
if cur_book:
result.append(cur_book)
else:
log.info('Not existing book %s in %s deleted', book.book_id, shelf)
ub.session.query(ub.BookShelf).filter(ub.BookShelf.book_id == book.book_id).delete()
ub.session.commit()
return render_title_template(page, entries=result, title=_(u"Shelf: '%(name)s'", name=shelf.name), return render_title_template(page, entries=result, title=_(u"Shelf: '%(name)s'", name=shelf.name),
shelf=shelf, page="shelf") shelf=shelf, page="shelf")
else: else:
@ -322,9 +317,8 @@ def order_shelf(shelf_id):
if shelf: if shelf:
books_in_shelf2 = ub.session.query(ub.BookShelf).filter(ub.BookShelf.shelf == shelf_id) \ books_in_shelf2 = ub.session.query(ub.BookShelf).filter(ub.BookShelf.shelf == shelf_id) \
.order_by(ub.BookShelf.order.asc()).all() .order_by(ub.BookShelf.order.asc()).all()
for book in books_in_shelf2: books_list = [ b.book_id for b in books_in_shelf2]
cur_book = db.session.query(db.Books).filter(db.Books.id == book.book_id).first() result = db.session.query(db.Books).filter(db.Books.id.in_(books_list)).filter(common_filters()).all()
result.append(cur_book)
return render_title_template('shelf_order.html', entries=result, return render_title_template('shelf_order.html', entries=result,
title=_(u"Change order of Shelf: '%(name)s'", name=shelf.name), title=_(u"Change order of Shelf: '%(name)s'", name=shelf.name),
shelf=shelf, page="shelforder") shelf=shelf, page="shelforder")

View File

@ -1,4 +1,7 @@
{% extends "layout.html" %} {% extends "layout.html" %}
{% macro display_bool_setting(setting_value) -%}
{% if setting_value %}<span class="glyphicon glyphicon-ok"></span>{% else %}<span class="glyphicon glyphicon-remove"></span>{% endif %}
{%- endmacro %}
{% block body %} {% block body %}
<div class="container-fluid"> <div class="container-fluid">
<div class="row"> <div class="row">
@ -23,11 +26,11 @@
<td>{{user.email}}</td> <td>{{user.email}}</td>
<td>{{user.kindle_mail}}</td> <td>{{user.kindle_mail}}</td>
<td>{{user.downloads.count()}}</td> <td>{{user.downloads.count()}}</td>
<td class="hidden-xs">{% if user.role_admin() %}<span class="glyphicon glyphicon-ok"></span>{% else %}<span class="glyphicon glyphicon-remove"></span>{% endif %}</td> <td class="hidden-xs">{{ display_bool_setting(user.role_admin()) }}</td>
<td class="hidden-xs">{% if user.role_download() %}<span class="glyphicon glyphicon-ok"></span>{% else %}<span class="glyphicon glyphicon-remove"></span>{% endif %}</td> <td class="hidden-xs">{{ display_bool_setting(user.role_download()) }}</td>
<td class="hidden-xs">{% if user.role_viewer() %}<span class="glyphicon glyphicon-ok"></span>{% else %}<span class="glyphicon glyphicon-remove"></span>{% endif %}</td> <td class="hidden-xs">{{ display_bool_setting(user.role_viewer()) }}</td>
<td class="hidden-xs">{% if user.role_upload() %}<span class="glyphicon glyphicon-ok"></span>{% else %}<span class="glyphicon glyphicon-remove"></span>{% endif %}</td> <td class="hidden-xs">{{ display_bool_setting(user.role_upload()) }}</td>
<td class="hidden-xs">{% if user.role_edit() %}<span class="glyphicon glyphicon-ok"></span>{% else %}<span class="glyphicon glyphicon-remove"></span>{% endif %}</td> <td class="hidden-xs">{{ display_bool_setting(user.role_edit()) }}</td>
</tr> </tr>
{% endif %} {% endif %}
{% endfor %} {% endfor %}
@ -83,20 +86,30 @@
</div> </div>
<div class="row"> <div class="row">
<div class="col-xs-6 col-sm-7">{{_('Uploading')}}</div> <div class="col-xs-6 col-sm-7">{{_('Uploading')}}</div>
<div class="col-xs-6 col-sm-5">{% if config.config_uploading %}<span class="glyphicon glyphicon-ok"></span>{% else %}<span class="glyphicon glyphicon-remove"></span>{% endif %}</div> <div class="col-xs-6 col-sm-5">{{ display_bool_setting(config.config_uploading) }}</div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-xs-6 col-sm-7">{{_('Anonymous browsing')}}</div> <div class="col-xs-6 col-sm-7">{{_('Anonymous browsing')}}</div>
<div class="col-xs-6 col-sm-5">{% if config.config_anonbrowse %}<span class="glyphicon glyphicon-ok"></span>{% else %}<span class="glyphicon glyphicon-remove"></span>{% endif %}</div> <div class="col-xs-6 col-sm-5">{{ display_bool_setting(config.config_anonbrowse) }}</div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-xs-6 col-sm-7">{{_('Public registration')}}</div> <div class="col-xs-6 col-sm-7">{{_('Public registration')}}</div>
<div class="col-xs-6 col-sm-5">{% if config.config_public_reg %}<span class="glyphicon glyphicon-ok"></span>{% else %}<span class="glyphicon glyphicon-remove"></span>{% endif %}</div> <div class="col-xs-6 col-sm-5">{{ display_bool_setting(config.config_public_reg) }}</div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-xs-6 col-sm-7">{{_('Remote login')}}</div> <div class="col-xs-6 col-sm-7">{{_('Remote login')}}</div>
<div class="col-xs-6 col-sm-5">{% if config.config_remote_login %}<span class="glyphicon glyphicon-ok"></span>{% else %}<span class="glyphicon glyphicon-remove"></span>{% endif %}</div> <div class="col-xs-6 col-sm-5">{{ display_bool_setting(config.config_remote_login) }}</div>
</div> </div>
<div class="row">
<div class="col-xs-6 col-sm-7">{{_('Reverse proxy login')}}</div>
<div class="col-xs-6 col-sm-5">{{ display_bool_setting(config.config_allow_reverse_proxy_header_login) }}</div>
</div>
{% if config.config_allow_reverse_proxy_header_login %}
<div class="row">
<div class="col-xs-6 col-sm-7">{{_('Reverse proxy header name')}}</div>
<div class="col-xs-6 col-sm-5">{{ config.config_reverse_proxy_login_header_name }}</div>
</div>
{% endif %}
</div> </div>
<div class="btn btn-default"><a id="basic_config" href="{{url_for('admin.configuration')}}">{{_('Basic Configuration')}}</a></div> <div class="btn btn-default"><a id="basic_config" href="{{url_for('admin.configuration')}}">{{_('Basic Configuration')}}</a></div>
<div class="btn btn-default"><a id="view_config" href="{{url_for('admin.view_configuration')}}">{{_('UI Configuration')}}</a></div> <div class="btn btn-default"><a id="view_config" href="{{url_for('admin.view_configuration')}}">{{_('UI Configuration')}}</a></div>

View File

@ -275,6 +275,16 @@
</div> </div>
{% endif %} {% endif %}
{% endif %} {% endif %}
<div class="form-group">
<input type="checkbox" id="config_allow_reverse_proxy_header_login" name="config_allow_reverse_proxy_header_login" data-control="reverse-proxy-login-settings" {% if config.config_allow_reverse_proxy_header_login %}checked{% endif %}>
<label for="config_allow_reverse_proxy_header_login">{{_('Allow Reverse Proxy Authentication')}}</label>
</div>
<div data-related="reverse-proxy-login-settings">
<div class="form-group">
<label for="config_reverse_proxy_login_header_name">{{_('Reverse Proxy Header Name')}}</label>
<input type="text" class="form-control" id="config_reverse_proxy_login_header_name" name="config_reverse_proxy_login_header_name" value="{% if config.config_reverse_proxy_login_header_name != None %}{{ config.config_reverse_proxy_login_header_name }}{% endif %}" autocomplete="off">
</div>
</div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -31,13 +31,7 @@
<label for="kobo_user_key">{{_('KoboStore UserKey')}}</label> <label for="kobo_user_key">{{_('KoboStore UserKey')}}</label>
<input type="password" class="form-control" name="kobo_user_key" id="kobo_user_key" value="" autocomplete="off"> <input type="password" class="form-control" name="kobo_user_key" id="kobo_user_key" value="" autocomplete="off">
</div> </div>
<label for="locale">{{_('Language')}}</label> {% if ( g.user and g.user.role_admin() ) %}
<select name="locale" id="locale" class="form-control">
{% for translation in translations %}
<option value="{{translation}}" {% if translation|string == content.locale %}selected{% endif %} {% if new_user == 1 and loop.first %}selected{% endif %}>{{ translation.display_name }}</option>
{% endfor %}
</select>
</div>
<div class="form-group"> <div class="form-group">
<label for="restricted_tags">{{_('Restricted Tags')}}</label> <label for="restricted_tags">{{_('Restricted Tags')}}</label>
<input type="text" class="form-control" name="restricted_tags" id="restricted_tags" value="{{ content.restricted_tags if content.restricted_tags != None }}" autocomplete="off"> <input type="text" class="form-control" name="restricted_tags" id="restricted_tags" value="{{ content.restricted_tags if content.restricted_tags != None }}" autocomplete="off">
@ -46,6 +40,15 @@
<label for="restricted_column_value">{{_('Restricted Column Content')}}</label> <label for="restricted_column_value">{{_('Restricted Column Content')}}</label>
<input type="text" class="form-control" name="restricted_column_value" id="restricted_column_value" value="{{ content.restricted_column_value if content.restricted_column_value != None }}" autocomplete="off"> <input type="text" class="form-control" name="restricted_column_value" id="restricted_column_value" value="{{ content.restricted_column_value if content.restricted_column_value != None }}" autocomplete="off">
</div> </div>
{% endif %}
<label for="locale">{{_('Language')}}</label>
<select name="locale" id="locale" class="form-control">
{% for translation in translations %}
<option value="{{translation}}" {% if translation|string == content.locale %}selected{% endif %} {% if new_user == 1 and loop.first %}selected{% endif %}>{{ translation.display_name|capitalize }}</option>
{% endfor %}
</select>
</div>
<div class="form-group"> <div class="form-group">
<label for="default_language">{{_('Show books with language')}}</label> <label for="default_language">{{_('Show books with language')}}</label>
<select name="default_language" id="default_language" class="form-control"> <select name="default_language" id="default_language" class="form-control">

View File

@ -114,14 +114,35 @@ web = Blueprint('web', __name__)
log = logger.create() log = logger.create()
# ################################### Login logic and rights management ############################################### # ################################### Login logic and rights management ###############################################
def _fetch_user_by_name(username):
return ub.session.query(ub.User).filter(func.lower(ub.User.nickname) == username.lower()).first()
@lm.user_loader @lm.user_loader
def load_user(user_id): def load_user(user_id):
return ub.session.query(ub.User).filter(ub.User.id == int(user_id)).first() return ub.session.query(ub.User).filter(ub.User.id == int(user_id)).first()
@lm.header_loader @lm.request_loader
def load_user_from_header(header_val): def load_user_from_request(request):
auth_header = request.headers.get("Authorization")
if auth_header:
user = load_user_from_auth_header(auth_header)
if user:
return user
if config.config_allow_reverse_proxy_header_login:
rp_header_name = config.config_reverse_proxy_login_header_name
if rp_header_name:
rp_header_username = request.headers.get(rp_header_name)
if rp_header_username:
user = _fetch_user_by_name(rp_header_username)
if user:
return user
return
def load_user_from_auth_header(header_val):
if header_val.startswith('Basic '): if header_val.startswith('Basic '):
header_val = header_val.replace('Basic ', '', 1) header_val = header_val.replace('Basic ', '', 1)
basic_username = basic_password = '' basic_username = basic_password = ''
@ -131,7 +152,7 @@ def load_user_from_header(header_val):
basic_password = header_val.split(':')[1] basic_password = header_val.split(':')[1]
except TypeError: except TypeError:
pass pass
user = ub.session.query(ub.User).filter(func.lower(ub.User.nickname) == basic_username.lower()).first() user = _fetch_user_by_name(basic_username)
if user and check_password_hash(str(user.password), basic_password): if user and check_password_hash(str(user.password), basic_password):
return user return user
return return
@ -789,7 +810,9 @@ def get_tasks_status():
@app.route("/reconnect") @app.route("/reconnect")
def reconnect(): def reconnect():
db.reconnect_db(config) db.session.close()
db.engine.dispose()
db.setup_db(config)
return json.dumps({}) return json.dumps({})
@web.route("/search", methods=["GET"]) @web.route("/search", methods=["GET"])