mirror of
https://github.com/janeczku/calibre-web.git
synced 2024-11-21 17:56:35 +02:00
Merge branch 'master' into Develop
# Conflicts: # cps/opds.py # cps/server.py # cps/web.py
This commit is contained in:
commit
4b7a0f3662
1
.gitattributes
vendored
1
.gitattributes
vendored
@ -1,4 +1,5 @@
|
||||
constants.py ident export-subst
|
||||
/test export-ignore
|
||||
/library export-ignore
|
||||
cps/static/css/libs/* linguist-vendored
|
||||
cps/static/js/libs/* linguist-vendored
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -28,6 +28,7 @@ cps/cache
|
||||
.idea/
|
||||
*.bak
|
||||
*.log.*
|
||||
.key
|
||||
|
||||
settings.yaml
|
||||
gdrive_credentials
|
||||
|
@ -52,7 +52,8 @@ In the Wiki there are also examples for: a [manual installation](https://github.
|
||||
|
||||
Point your browser to `http://localhost:8083` or `http://localhost:8083/opds` for the OPDS catalog \
|
||||
Login with default admin login \
|
||||
Set `Location of Calibre database` to the path of the folder where your Calibre library (metadata.db) lives, push "submit" button \
|
||||
If you don't have a Calibre database already, this [database](https://github.com/janeczku/calibre-web/blob/master/library/metadata.db) can be used. **IMPORTATNT** Please move the database out of the calibre-web folder structure, as it will be overwritten during update. \
|
||||
Set `Location of Calibre database` to the path of the folder where your Calibre library (metadata.db) lives, push "submit" button. \
|
||||
Optionally a Google Drive can be used to host the calibre library [-> Using Google Drive integration](https://github.com/janeczku/calibre-web/wiki/G-Drive-Setup#using-google-drive-integration) \
|
||||
Afterwards you can configure your Calibre-Web instance ([Basic Configuration](https://github.com/janeczku/calibre-web/wiki/Configuration#basic-configuration) and [UI Configuration](https://github.com/janeczku/calibre-web/wiki/Configuration#ui-configuration) on admin page)
|
||||
|
||||
|
@ -21,9 +21,10 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
from flask_login import LoginManager
|
||||
from flask import session
|
||||
|
||||
from flask_login import LoginManager, confirm_login
|
||||
from flask import session, current_app
|
||||
from flask_login.utils import decode_cookie
|
||||
from flask_login.signals import user_loaded_from_cookie
|
||||
|
||||
class MyLoginManager(LoginManager):
|
||||
def _session_protection_failed(self):
|
||||
@ -33,3 +34,19 @@ class MyLoginManager(LoginManager):
|
||||
and _session.get('csrf_token', None))) and ident != _session.get('_id', None):
|
||||
return super(). _session_protection_failed()
|
||||
return False
|
||||
|
||||
def _load_user_from_remember_cookie(self, cookie):
|
||||
user_id = decode_cookie(cookie)
|
||||
if user_id is not None:
|
||||
session["_user_id"] = user_id
|
||||
session["_fresh"] = False
|
||||
user = None
|
||||
if self._user_callback:
|
||||
user = self._user_callback(user_id)
|
||||
if user is not None:
|
||||
app = current_app._get_current_object()
|
||||
user_loaded_from_cookie.send(app, user=user)
|
||||
# if session was restored from remember me cookie make login valid
|
||||
confirm_login()
|
||||
return user
|
||||
return None
|
||||
|
@ -33,7 +33,7 @@ from datetime import time as datetime_time
|
||||
from functools import wraps
|
||||
|
||||
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_login import login_required, current_user, logout_user
|
||||
from flask_babel import gettext as _
|
||||
from flask_babel import get_locale, format_time, format_datetime, format_timedelta
|
||||
from flask import session as flask_session
|
||||
@ -101,21 +101,16 @@ 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.user = current_user
|
||||
g.google_site_verification = os.getenv('GOOGLE_SITE_VERIFICATION','')
|
||||
g.allow_registration = config.config_public_reg
|
||||
g.allow_anonymous = config.config_anonbrowse
|
||||
g.allow_upload = config.config_uploading
|
||||
g.current_theme = config.config_theme
|
||||
g.config_authors_max = config.config_authors_max
|
||||
g.shelves_access = ub.session.query(ub.Shelf).filter(
|
||||
or_(ub.Shelf.is_public == 1, ub.Shelf.user_id == current_user.id)).order_by(ub.Shelf.name).all()
|
||||
if '/static/' not in request.path and not config.db_configured and \
|
||||
request.endpoint not in ('admin.ajax_db_config',
|
||||
'admin.simulatedbchange',
|
||||
|
11
cps/babel.py
11
cps/babel.py
@ -1,7 +1,8 @@
|
||||
from babel import negotiate_locale
|
||||
from flask_babel import Babel, Locale
|
||||
from babel.core import UnknownLocaleError
|
||||
from flask import request, g
|
||||
from flask import request
|
||||
from flask_login import current_user
|
||||
|
||||
from . import logger
|
||||
|
||||
@ -11,10 +12,10 @@ babel = Babel()
|
||||
|
||||
def get_locale():
|
||||
# if a user is logged in, use the locale from the user settings
|
||||
user = getattr(g, 'user', None)
|
||||
if user is not None and hasattr(user, "locale"):
|
||||
if user.name != 'Guest': # if the account is the guest account bypass the config lang settings
|
||||
return user.locale
|
||||
if current_user is not None and hasattr(current_user, "locale"):
|
||||
# if the account is the guest account bypass the config lang settings
|
||||
if current_user.name != 'Guest':
|
||||
return current_user.locale
|
||||
|
||||
preferred = list()
|
||||
if request.accept_languages:
|
||||
|
@ -163,7 +163,7 @@ def selected_roles(dictionary):
|
||||
BookMeta = namedtuple('BookMeta', 'file_path, extension, title, author, cover, description, tags, series, '
|
||||
'series_id, languages, publisher, pubdate, identifiers')
|
||||
|
||||
STABLE_VERSION = {'version': '0.6.19'}
|
||||
STABLE_VERSION = {'version': '0.6.20 Beta'}
|
||||
|
||||
NIGHTLY_VERSION = dict()
|
||||
NIGHTLY_VERSION[0] = '$Format:%H$'
|
||||
|
60
cps/opds.py
60
cps/opds.py
@ -22,41 +22,26 @@
|
||||
|
||||
import datetime
|
||||
from urllib.parse import unquote_plus
|
||||
from functools import wraps
|
||||
|
||||
from flask import Blueprint, request, render_template, Response, g, make_response, abort
|
||||
from flask import Blueprint, request, render_template, make_response, abort
|
||||
from flask_login import current_user
|
||||
from flask_babel import get_locale
|
||||
from flask_babel import gettext as _
|
||||
from sqlalchemy.sql.expression import func, text, or_, and_, true
|
||||
from sqlalchemy.exc import InvalidRequestError, OperationalError
|
||||
from werkzeug.security import check_password_hash
|
||||
|
||||
from . import constants, logger, config, db, calibre_db, ub, services, isoLanguages, limiter
|
||||
from . import logger, config, db, calibre_db, ub, isoLanguages
|
||||
from .usermanagement import requires_basic_auth_if_no_ano
|
||||
from .helper import get_download_link, get_book_cover
|
||||
from .pagination import Pagination
|
||||
from .web import render_read_books
|
||||
from .usermanagement import load_user_from_request
|
||||
from flask_babel import gettext as _
|
||||
from flask_limiter import RateLimitExceeded
|
||||
|
||||
|
||||
opds = Blueprint('opds', __name__)
|
||||
|
||||
log = logger.create()
|
||||
|
||||
|
||||
def requires_basic_auth_if_no_ano(f):
|
||||
@wraps(f)
|
||||
def decorated(*args, **kwargs):
|
||||
auth = request.authorization
|
||||
if config.config_anonbrowse != 1:
|
||||
if not auth or auth.type != 'basic' or not check_auth(auth.username, auth.password):
|
||||
return authenticate()
|
||||
return f(*args, **kwargs)
|
||||
if config.config_login_type == constants.LOGIN_LDAP and services.ldap and config.config_anonbrowse != 1:
|
||||
return services.ldap.basic_auth_required(f)
|
||||
return decorated
|
||||
|
||||
|
||||
@opds.route("/opds/")
|
||||
@opds.route("/opds")
|
||||
@requires_basic_auth_if_no_ano
|
||||
@ -356,7 +341,8 @@ def feed_languages(book_id):
|
||||
@requires_basic_auth_if_no_ano
|
||||
def feed_shelfindex():
|
||||
off = request.args.get("offset") or 0
|
||||
shelf = g.shelves_access
|
||||
shelf = ub.session.query(ub.Shelf).filter(
|
||||
or_(ub.Shelf.is_public == 1, ub.Shelf.user_id == current_user.id)).order_by(ub.Shelf.name).all()
|
||||
number = len(shelf)
|
||||
pagination = Pagination((int(off) / (int(config.config_books_per_page)) + 1), config.config_books_per_page,
|
||||
number)
|
||||
@ -403,11 +389,7 @@ def feed_shelf(book_id):
|
||||
@opds.route("/opds/download/<book_id>/<book_format>/")
|
||||
@requires_basic_auth_if_no_ano
|
||||
def opds_download_link(book_id, book_format):
|
||||
# I gave up with this: With enabled ldap login, the user doesn't get logged in, therefore it's always guest
|
||||
# workaround, loading the user from the request and checking its download rights here
|
||||
# in case of anonymous browsing user is None
|
||||
user = load_user_from_request(request) or current_user
|
||||
if not user.role_download():
|
||||
if not current_user.role_download():
|
||||
return abort(403)
|
||||
if "Kobo" in request.headers.get('User-Agent'):
|
||||
client = "kobo"
|
||||
@ -479,32 +461,6 @@ def feed_search(term):
|
||||
return render_xml_template('feed.xml', searchterm="")
|
||||
|
||||
|
||||
def check_auth(username, password):
|
||||
try:
|
||||
limiter.check()
|
||||
except RateLimitExceeded:
|
||||
return abort(429) # False
|
||||
try:
|
||||
username = username.encode('windows-1252')
|
||||
except UnicodeEncodeError:
|
||||
username = username.encode('utf-8')
|
||||
user = ub.session.query(ub.User).filter(func.lower(ub.User.name) ==
|
||||
username.decode('utf-8').lower()).first()
|
||||
if bool(user and check_password_hash(str(user.password), password)):
|
||||
[limiter.limiter.storage.clear(k.key) for k in limiter.current_limits]
|
||||
return True
|
||||
else:
|
||||
ip_address = request.headers.get('X-Forwarded-For', request.remote_addr)
|
||||
log.warning('OPDS Login failed for user "%s" IP-address: %s', username.decode('utf-8'), ip_address)
|
||||
return False
|
||||
|
||||
|
||||
def authenticate():
|
||||
return Response(
|
||||
'Could not verify your access level for that URL.\n'
|
||||
'You have to login with proper credentials', 401,
|
||||
{'WWW-Authenticate': 'Basic realm="Login Required"'})
|
||||
|
||||
|
||||
def render_xml_template(*args, **kwargs):
|
||||
# ToDo: return time in current timezone similar to %z
|
||||
|
@ -20,11 +20,13 @@ from flask import render_template, g, abort, request
|
||||
from flask_babel import gettext as _
|
||||
from werkzeug.local import LocalProxy
|
||||
from flask_login import current_user
|
||||
from sqlalchemy.sql.expression import or_
|
||||
|
||||
from . import config, constants, logger
|
||||
from . import config, constants, logger, ub
|
||||
from .ub import User
|
||||
|
||||
|
||||
|
||||
log = logger.create()
|
||||
|
||||
def get_sidebar_config(kwargs=None):
|
||||
@ -45,12 +47,12 @@ def get_sidebar_config(kwargs=None):
|
||||
"show_text": _('Show Hot Books'), "config_show": True})
|
||||
if current_user.role_admin():
|
||||
sidebar.append({"glyph": "glyphicon-download", "text": _('Downloaded Books'), "link": 'web.download_list',
|
||||
"id": "download", "visibility": constants.SIDEBAR_DOWNLOAD, 'public': (not g.user.is_anonymous),
|
||||
"id": "download", "visibility": constants.SIDEBAR_DOWNLOAD, 'public': (not current_user.is_anonymous),
|
||||
"page": "download", "show_text": _('Show Downloaded Books'),
|
||||
"config_show": content})
|
||||
else:
|
||||
sidebar.append({"glyph": "glyphicon-download", "text": _('Downloaded Books'), "link": 'web.books_list',
|
||||
"id": "download", "visibility": constants.SIDEBAR_DOWNLOAD, 'public': (not g.user.is_anonymous),
|
||||
"id": "download", "visibility": constants.SIDEBAR_DOWNLOAD, 'public': (not current_user.is_anonymous),
|
||||
"page": "download", "show_text": _('Show Downloaded Books'),
|
||||
"config_show": content})
|
||||
sidebar.append(
|
||||
@ -58,11 +60,11 @@ def get_sidebar_config(kwargs=None):
|
||||
"visibility": constants.SIDEBAR_BEST_RATED, 'public': True, "page": "rated",
|
||||
"show_text": _('Show Top Rated Books'), "config_show": True})
|
||||
sidebar.append({"glyph": "glyphicon-eye-open", "text": _('Read Books'), "link": 'web.books_list', "id": "read",
|
||||
"visibility": constants.SIDEBAR_READ_AND_UNREAD, 'public': (not g.user.is_anonymous),
|
||||
"visibility": constants.SIDEBAR_READ_AND_UNREAD, 'public': (not current_user.is_anonymous),
|
||||
"page": "read", "show_text": _('Show Read and Unread'), "config_show": content})
|
||||
sidebar.append(
|
||||
{"glyph": "glyphicon-eye-close", "text": _('Unread Books'), "link": 'web.books_list', "id": "unread",
|
||||
"visibility": constants.SIDEBAR_READ_AND_UNREAD, 'public': (not g.user.is_anonymous), "page": "unread",
|
||||
"visibility": constants.SIDEBAR_READ_AND_UNREAD, 'public': (not current_user.is_anonymous), "page": "unread",
|
||||
"show_text": _('Show unread'), "config_show": False})
|
||||
sidebar.append({"glyph": "glyphicon-random", "text": _('Discover'), "link": 'web.books_list', "id": "rand",
|
||||
"visibility": constants.SIDEBAR_RANDOM, 'public': True, "page": "discover",
|
||||
@ -81,7 +83,7 @@ def get_sidebar_config(kwargs=None):
|
||||
"visibility": constants.SIDEBAR_PUBLISHER, 'public': True, "page": "publisher",
|
||||
"show_text": _('Show Publisher Section'), "config_show":True})
|
||||
sidebar.append({"glyph": "glyphicon-flag", "text": _('Languages'), "link": 'web.language_overview', "id": "lang",
|
||||
"visibility": constants.SIDEBAR_LANGUAGE, 'public': (g.user.filter_language() == 'all'),
|
||||
"visibility": constants.SIDEBAR_LANGUAGE, 'public': (current_user.filter_language() == 'all'),
|
||||
"page": "language",
|
||||
"show_text": _('Show Language Section'), "config_show": True})
|
||||
sidebar.append({"glyph": "glyphicon-star-empty", "text": _('Ratings'), "link": 'web.ratings_list', "id": "rate",
|
||||
@ -92,13 +94,16 @@ def get_sidebar_config(kwargs=None):
|
||||
"page": "format", "show_text": _('Show File Formats Section'), "config_show": True})
|
||||
sidebar.append(
|
||||
{"glyph": "glyphicon-trash", "text": _('Archived Books'), "link": 'web.books_list', "id": "archived",
|
||||
"visibility": constants.SIDEBAR_ARCHIVED, 'public': (not g.user.is_anonymous), "page": "archived",
|
||||
"visibility": constants.SIDEBAR_ARCHIVED, 'public': (not current_user.is_anonymous), "page": "archived",
|
||||
"show_text": _('Show Archived Books'), "config_show": content})
|
||||
if not simple:
|
||||
sidebar.append(
|
||||
{"glyph": "glyphicon-th-list", "text": _('Books List'), "link": 'web.books_table', "id": "list",
|
||||
"visibility": constants.SIDEBAR_LIST, 'public': (not g.user.is_anonymous), "page": "list",
|
||||
"visibility": constants.SIDEBAR_LIST, 'public': (not current_user.is_anonymous), "page": "list",
|
||||
"show_text": _('Show Books List'), "config_show": content})
|
||||
g.shelves_access = ub.session.query(ub.Shelf).filter(
|
||||
or_(ub.Shelf.is_public == 1, ub.Shelf.user_id == current_user.id)).order_by(ub.Shelf.name).all()
|
||||
|
||||
return sidebar, simple
|
||||
|
||||
|
||||
|
@ -35,12 +35,13 @@ search = Blueprint('search', __name__)
|
||||
log = logger.create()
|
||||
|
||||
|
||||
@search.route("/search", methods=["GET"])
|
||||
@search.route("/search", methods=["POST"])
|
||||
@login_required_if_no_ano
|
||||
def simple_search():
|
||||
term = request.args.get("query")
|
||||
term = dict(request.form).get("query")
|
||||
if term:
|
||||
return redirect(url_for('web.books_list', data="search", sort_param='stored', query=term.strip()))
|
||||
flask_session['query'] = json.dumps(term.strip())
|
||||
return redirect(url_for('web.books_list', data="search", sort_param='stored', query="")) # term.strip()
|
||||
else:
|
||||
return render_title_template('search.html',
|
||||
searchterm="",
|
||||
|
@ -269,7 +269,7 @@ class WebServer(object):
|
||||
@staticmethod
|
||||
def shutdown_scheduler():
|
||||
scheduler = BackgroundScheduler()
|
||||
if scheduler: # and not scheduler.scheduler.STATE_STOPPED:
|
||||
if scheduler:
|
||||
scheduler.scheduler.shutdown()
|
||||
|
||||
def _killServer(self, __, ___):
|
||||
|
@ -140,6 +140,7 @@ table .bg-dark-danger a { color: #fff; }
|
||||
|
||||
.container-fluid .book {
|
||||
margin-top: 20px;
|
||||
max-width: 180px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
13
cps/static/js/compress/jszip.min.js
vendored
13
cps/static/js/compress/jszip.min.js
vendored
File diff suppressed because one or more lines are too long
@ -364,12 +364,6 @@ $(function() {
|
||||
layoutMode : "fitRows"
|
||||
});
|
||||
|
||||
$(".grid").isotope({
|
||||
// options
|
||||
itemSelector : ".grid-item",
|
||||
layoutMode : "fitColumns"
|
||||
});
|
||||
|
||||
if ($(".load-more").length && $(".next").length) {
|
||||
var $loadMore = $(".load-more .row").infiniteScroll({
|
||||
debug: false,
|
||||
|
@ -6,7 +6,7 @@
|
||||
<!-- Always use full-sized image for the book edit page -->
|
||||
<img id="detailcover" title="{{book.title}}" src="{{url_for('web.get_cover', book_id=book.id, resolution='og', c=book|last_modified)}}" />
|
||||
</div>
|
||||
{% if g.user.role_delete_books() %}
|
||||
{% if current_user.role_delete_books() %}
|
||||
<div class="text-center">
|
||||
<button type="button" class="btn btn-danger" id="delete" data-toggle="modal" data-delete-id="{{ book.id }}" data-target="#deleteModal">{{_("Delete Book")}}</button>
|
||||
</div>
|
||||
@ -99,7 +99,7 @@
|
||||
<label for="rating">{{_('Rating')}}</label>
|
||||
<input type="number" name="rating" id="rating" class="rating input-lg" data-clearable="" value="{% if book.ratings %}{{(book.ratings[0].rating / 2)|int}}{% endif %}">
|
||||
</div>
|
||||
{% if g.user.role_upload() and g.allow_upload %}
|
||||
{% if current_user.role_upload() and g.allow_upload %}
|
||||
<div class="form-group">
|
||||
<label for="cover_url">{{_('Fetch Cover from URL (JPEG - Image will be downloaded and stored in database)')}}</label>
|
||||
<input type="text" class="form-control" name="cover_url" id="cover_url" value="">
|
||||
@ -196,7 +196,7 @@
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if g.user.role_upload() and g.allow_upload %}
|
||||
{% if current_user.role_upload() and g.allow_upload %}
|
||||
<div role="group" aria-label="Upload new book format">
|
||||
<label class="btn btn-primary btn-file" for="btn-upload-format">{{ _('Upload Format') }}</label>
|
||||
<div class="upload-format-input-text" id="upload-format"></div>
|
||||
@ -219,7 +219,7 @@
|
||||
{% endblock %}
|
||||
|
||||
{% block modal %}
|
||||
{{ delete_book() }}
|
||||
{{ delete_book(current_user.role_delete_books()) }}
|
||||
{{ delete_confirm_modal() }}
|
||||
|
||||
<div class="modal fade" id="metaModal" tabindex="-1" role="dialog" aria-labelledby="metaModalLabel">
|
||||
@ -291,7 +291,7 @@
|
||||
'description': {{_('Description')|safe|tojson}},
|
||||
'source': {{_('Source')|safe|tojson}},
|
||||
};
|
||||
var language = '{{ g.user.locale }}';
|
||||
var language = '{{ current_user.locale }}';
|
||||
|
||||
$("#add-identifier-line").click(function() {
|
||||
// create a random identifier type to have a valid name in form. This will not be used when dealing with the form
|
||||
@ -313,8 +313,8 @@
|
||||
<script src="{{ url_for('static', filename='js/get_meta.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/libs/tinymce/tinymce.min.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/libs/bootstrap-datepicker/bootstrap-datepicker.min.js') }}"></script>
|
||||
{% if not g.user.locale == 'en' %}
|
||||
<script src="{{ url_for('static', filename='js/libs/bootstrap-datepicker/locales/bootstrap-datepicker.' + g.user.locale + '.min.js') }}" charset="UTF-8"></script>
|
||||
{% if not current_user.locale == 'en' %}
|
||||
<script src="{{ url_for('static', filename='js/libs/bootstrap-datepicker/locales/bootstrap-datepicker.' + current_user.locale + '.min.js') }}" charset="UTF-8"></script>
|
||||
{% endif %}
|
||||
<script src="{{ url_for('static', filename='js/edit_books.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/fullscreen.js') }}"></script>
|
||||
|
@ -4,7 +4,7 @@
|
||||
{% if sort %}data-sortable="true" {% endif %}
|
||||
data-visible = "{{visiblility.get(parameter)}}"
|
||||
data-escape="true"
|
||||
{% if g.user.role_edit() %}
|
||||
{% if current_user.role_edit() %}
|
||||
data-editable-type="text"
|
||||
data-editable-url="{{ url_for('edit-book.edit_list_book', param=parameter)}}"
|
||||
data-editable-title="{{ edit_text }}"
|
||||
@ -53,10 +53,10 @@
|
||||
</div>
|
||||
|
||||
<table id="books-table" class="table table-no-bordered table-striped"
|
||||
data-url="{{url_for('web.list_books')}}" data-locale="{{ g.user.locale }}">
|
||||
data-url="{{url_for('web.list_books')}}" data-locale="{{ current_user.locale }}">
|
||||
<thead>
|
||||
<tr>
|
||||
{% if g.user.role_edit() %}
|
||||
{% if current_user.role_edit() %}
|
||||
<th data-field="state" data-checkbox="true" data-sortable="true"></th>
|
||||
{% endif %}
|
||||
<th data-field="id" id="id" data-visible="false" data-switchable="false"></th>
|
||||
@ -66,37 +66,37 @@
|
||||
{{ text_table_row('authors', _('Enter Authors'),_('Authors'), true, true) }}
|
||||
{{ text_table_row('tags', _('Enter Categories'),_('Categories'), false, true) }}
|
||||
{{ text_table_row('series', _('Enter Series'),_('Series'), false, true) }}
|
||||
<th data-field="series_index" id="series_index" data-visible="{{visiblility.get('series_index')}}" data-edit-validate="{{ _('This Field is Required') }}" data-sortable="true" {% if g.user.role_edit() %} data-editable-type="number" data-editable-placeholder="1" data-editable-step="0.01" data-editable-min="0" data-editable-url="{{ url_for('edit-book.edit_list_book', param='series_index')}}" data-edit="true" data-editable-title="{{_('Enter Title')}}"{% endif %}>{{_('Series Index')}}</th>
|
||||
<th data-field="series_index" id="series_index" data-visible="{{visiblility.get('series_index')}}" data-edit-validate="{{ _('This Field is Required') }}" data-sortable="true" {% if current_user.role_edit() %} data-editable-type="number" data-editable-placeholder="1" data-editable-step="0.01" data-editable-min="0" data-editable-url="{{ url_for('edit-book.edit_list_book', param='series_index')}}" data-edit="true" data-editable-title="{{_('Enter Title')}}"{% endif %}>{{_('Series Index')}}</th>
|
||||
{{ text_table_row('languages', _('Enter Languages'),_('Languages'), false, true) }}
|
||||
<!--th data-field="pubdate" data-type="date" data-visible="{{visiblility.get('pubdate')}}" data-viewformat="dd.mm.yyyy" id="pubdate" data-sortable="true">{{_('Publishing Date')}}</th-->
|
||||
{{ text_table_row('publishers', _('Enter Publishers'),_('Publishers'), false, true) }}
|
||||
<th data-field="comments" id="comments" data-escape="true" data-editable-mode="popup" data-visible="{{visiblility.get('comments')}}" data-sortable="false" {% if g.user.role_edit() %} data-editable-type="wysihtml5" data-editable-url="{{ url_for('edit-book.edit_list_book', param='comments')}}" data-edit="true" data-editable-title="{{_('Enter comments')}}"{% endif %}>{{_('Comments')}}</th>
|
||||
{% if g.user.check_visibility(32768) %}
|
||||
<th data-field="comments" id="comments" data-escape="true" data-editable-mode="popup" data-visible="{{visiblility.get('comments')}}" data-sortable="false" {% if current_user.role_edit() %} data-editable-type="wysihtml5" data-editable-url="{{ url_for('edit-book.edit_list_book', param='comments')}}" data-edit="true" data-editable-title="{{_('Enter comments')}}"{% endif %}>{{_('Comments')}}</th>
|
||||
{% if current_user.check_visibility(32768) %}
|
||||
{{ book_checkbox_row('is_archived', _('Archive Status'), false)}}
|
||||
{% endif %}
|
||||
{{ book_checkbox_row('read_status', _('Read Status'), false)}}
|
||||
{% for c in cc %}
|
||||
{% if c.datatype == "int" %}
|
||||
<th data-field="custom_column_{{ c.id|string }}" id="custom_column_{{ c.id|string }}" data-visible="{{visiblility.get('custom_column_'+ c.id|string)}}" data-sortable="false" {% if g.user.role_edit() %} data-editable-type="number" data-editable-placeholder="1" data-editable-step="1" data-editable-url="{{ url_for('edit-book.edit_list_book', param='custom_column_'+ c.id|string)}}" data-edit="true" data-editable-title="{{_('Enter ') + c.name}}"{% endif %}>{{c.name}}</th>
|
||||
<th data-field="custom_column_{{ c.id|string }}" id="custom_column_{{ c.id|string }}" data-visible="{{visiblility.get('custom_column_'+ c.id|string)}}" data-sortable="false" {% if current_user.role_edit() %} data-editable-type="number" data-editable-placeholder="1" data-editable-step="1" data-editable-url="{{ url_for('edit-book.edit_list_book', param='custom_column_'+ c.id|string)}}" data-edit="true" data-editable-title="{{_('Enter ') + c.name}}"{% endif %}>{{c.name}}</th>
|
||||
{% elif c.datatype == "rating" %}
|
||||
<th data-field="custom_column_{{ c.id|string }}" id="custom_column_{{ c.id|string }}" data-formatter="ratingFormatter" data-visible="{{visiblility.get('custom_column_'+ c.id|string)}}" data-sortable="false" {% if g.user.role_edit() %} data-editable-type="number" data-editable-placeholder="1" data-editable-step="0.5" data-editable-step="1" data-editable-min="1" data-editable-max="5" data-editable-url="{{ url_for('edit-book.edit_list_book', param='custom_column_'+ c.id|string)}}" data-edit="true" data-editable-title="{{_('Enter ') + c.name}}"{% endif %}>{{c.name}}</th>
|
||||
<th data-field="custom_column_{{ c.id|string }}" id="custom_column_{{ c.id|string }}" data-formatter="ratingFormatter" data-visible="{{visiblility.get('custom_column_'+ c.id|string)}}" data-sortable="false" {% if current_user.role_edit() %} data-editable-type="number" data-editable-placeholder="1" data-editable-step="0.5" data-editable-step="1" data-editable-min="1" data-editable-max="5" data-editable-url="{{ url_for('edit-book.edit_list_book', param='custom_column_'+ c.id|string)}}" data-edit="true" data-editable-title="{{_('Enter ') + c.name}}"{% endif %}>{{c.name}}</th>
|
||||
{% elif c.datatype == "float" %}
|
||||
<th data-field="custom_column_{{ c.id|string }}" id="custom_column_{{ c.id|string }}" data-visible="{{visiblility.get('custom_column_'+ c.id|string)}}" data-sortable="false" {% if g.user.role_edit() %} data-editable-type="number" data-editable-placeholder="1" data-editable-step="0.01" data-editable-url="{{ url_for('edit-book.edit_list_book', param='custom_column_'+ c.id|string)}}" data-edit="true" data-editable-title="{{_('Enter ') + c.name}}"{% endif %}>{{c.name}}</th>
|
||||
<th data-field="custom_column_{{ c.id|string }}" id="custom_column_{{ c.id|string }}" data-visible="{{visiblility.get('custom_column_'+ c.id|string)}}" data-sortable="false" {% if current_user.role_edit() %} data-editable-type="number" data-editable-placeholder="1" data-editable-step="0.01" data-editable-url="{{ url_for('edit-book.edit_list_book', param='custom_column_'+ c.id|string)}}" data-edit="true" data-editable-title="{{_('Enter ') + c.name}}"{% endif %}>{{c.name}}</th>
|
||||
{% elif c.datatype == "enumeration" %}
|
||||
<th data-field="custom_column_{{ c.id|string }}" id="custom_column_{{ c.id|string }}" data-visible="{{visiblility.get('custom_column_'+ c.id|string)}}" data-sortable="false" {% if g.user.role_edit() %} data-editable-type="select" data-editable-source={{ url_for('edit-book.table_get_custom_enum', c_id=c.id) }} data-editable-url="{{ url_for('edit-book.edit_list_book', param='custom_column_'+ c.id|string)}}" data-edit="true" data-editable-title="{{_('Enter ') + c.name}}"{% endif %}>{{c.name}}</th>
|
||||
<th data-field="custom_column_{{ c.id|string }}" id="custom_column_{{ c.id|string }}" data-visible="{{visiblility.get('custom_column_'+ c.id|string)}}" data-sortable="false" {% if current_user.role_edit() %} data-editable-type="select" data-editable-source={{ url_for('edit-book.table_get_custom_enum', c_id=c.id) }} data-editable-url="{{ url_for('edit-book.edit_list_book', param='custom_column_'+ c.id|string)}}" data-edit="true" data-editable-title="{{_('Enter ') + c.name}}"{% endif %}>{{c.name}}</th>
|
||||
{% elif c.datatype in ["datetime"] %}
|
||||
<!-- missing -->
|
||||
{% elif c.datatype == "text" %}
|
||||
{{ text_table_row('custom_column_' + c.id|string, _('Enter ') + c.name, c.name, false, false) }}
|
||||
{% elif c.datatype == "comments" %}
|
||||
<th data-field="custom_column_{{ c.id|string }}" id="custom_column_{{ c.id|string }}" data-escape="true" data-editable-mode="popup" data-visible="{{visiblility.get('custom_column_'+ c.id|string)}}" data-sortable="false" {% if g.user.role_edit() %} data-editable-type="wysihtml5" data-editable-url="{{ url_for('edit-book.edit_list_book', param='custom_column_'+ c.id|string)}}" data-edit="true" data-editable-title="{{_('Enter ') + c.name}}"{% endif %}>{{c.name}}</th>
|
||||
<th data-field="custom_column_{{ c.id|string }}" id="custom_column_{{ c.id|string }}" data-escape="true" data-editable-mode="popup" data-visible="{{visiblility.get('custom_column_'+ c.id|string)}}" data-sortable="false" {% if current_user.role_edit() %} data-editable-type="wysihtml5" data-editable-url="{{ url_for('edit-book.edit_list_book', param='custom_column_'+ c.id|string)}}" data-edit="true" data-editable-title="{{_('Enter ') + c.name}}"{% endif %}>{{c.name}}</th>
|
||||
{% elif c.datatype == "bool" %}
|
||||
{{ book_checkbox_row('custom_column_' + c.id|string, c.name, false)}}
|
||||
{% else %}
|
||||
<!--{{ text_table_row('custom_column_' + c.id|string, _('Enter ') + c.name, c.name, false, false) }} -->
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% if g.user.role_delete_books() and g.user.role_edit()%}
|
||||
{% if current_user.role_delete_books() and current_user.role_edit()%}
|
||||
<th data-align="right" data-formatter="EbookActions" data-switchable="false">{{_('Delete')}}</th>
|
||||
{% endif %}
|
||||
</tr>
|
||||
@ -104,8 +104,8 @@
|
||||
</table>
|
||||
{% endblock %}
|
||||
{% block modal %}
|
||||
{{ delete_book() }}
|
||||
{% if g.user.role_edit() %}
|
||||
{{ delete_book(current_user.role_delete_books()) }}
|
||||
{% if current_user.role_edit() %}
|
||||
<div class="modal fade" id="mergeModal" role="dialog" aria-labelledby="metaMergeLabel">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
@ -137,8 +137,8 @@
|
||||
<script src="{{ url_for('static', filename='js/libs/bootstrap-table/bootstrap-table-locale-all.min.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/libs/bootstrap-table/bootstrap-table-editable.min.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/libs/bootstrap-table/bootstrap-editable.min.js') }}"></script>
|
||||
{% if not g.user.locale == 'en' %}
|
||||
<script src="{{ url_for('static', filename='js/libs/bootstrap-table/locale/bootstrap-table-' + g.user.locale + '.min.js') }}" charset="UTF-8"></script>
|
||||
{% if not current_user.locale == 'en' %}
|
||||
<script src="{{ url_for('static', filename='js/libs/bootstrap-table/locale/bootstrap-table-' + current_user.locale + '.min.js') }}" charset="UTF-8"></script>
|
||||
{% endif %}
|
||||
<script src="{{ url_for('static', filename='js/libs/wysihtml5-0.3.0.min.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/libs/bootstrap-wysihtml5-0.0.3.min.js') }}"></script>
|
||||
|
@ -11,7 +11,7 @@
|
||||
<div class="col-sm-9 col-lg-9 book-meta">
|
||||
<div class="btn-toolbar" role="toolbar">
|
||||
<div class="btn-group" role="group" aria-label="Download, send to eReader, reading">
|
||||
{% if g.user.role_download() %}
|
||||
{% if current_user.role_download() %}
|
||||
{% if entry.data|length %}
|
||||
<div class="btn-group" role="group">
|
||||
{% if entry.data|length < 2 %}
|
||||
@ -37,7 +37,7 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if g.user.kindle_mail and entry.email_share_list %}
|
||||
{% if current_user.kindle_mail and entry.email_share_list %}
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||||
{% if entry.email_share_list.__len__() == 1 %}
|
||||
<div id="sendbtn" data-action="{{url_for('web.send_to_ereader', book_id=entry.id, book_format=entry.email_share_list[0]['format'], convert=entry.email_share_list[0]['convert'])}}" data-text="{{_('Send to eReader')}}" class="btn btn-primary postAction" role="button"><span class="glyphicon glyphicon-send"></span> {{entry.email_share_list[0]['text']}}</div>
|
||||
@ -55,7 +55,7 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if entry.reader_list and g.user.role_viewer() %}
|
||||
{% if entry.reader_list and current_user.role_viewer() %}
|
||||
<div class="btn-group" role="group">
|
||||
{% if entry.reader_list|length > 1 %}
|
||||
<button id="read-in-browser" type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
@ -72,7 +72,7 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if entry.audio_entries|length > 0 and g.user.role_viewer() %}
|
||||
{% if entry.audio_entries|length > 0 and current_user.role_viewer() %}
|
||||
<div class="btn-group" role="group">
|
||||
{% if entry.audio_entries|length > 1 %}
|
||||
<button id="listen-in-browser" type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
@ -213,7 +213,7 @@
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if not g.user.is_anonymous %}
|
||||
{% if not current_user.is_anonymous %}
|
||||
|
||||
<div class="custom_columns">
|
||||
<p>
|
||||
@ -225,7 +225,7 @@
|
||||
</label>
|
||||
</form>
|
||||
</p>
|
||||
{% if g.user.check_visibility(32768) %}
|
||||
{% if current_user.check_visibility(32768) %}
|
||||
<p>
|
||||
<form id="archived_form" action="{{ url_for('web.toggle_archived', book_id=entry.id)}}" method="POST">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||||
@ -250,8 +250,8 @@
|
||||
|
||||
<div class="more-stuff">
|
||||
|
||||
{% if g.user.is_authenticated %}
|
||||
{% if g.user.shelf.all() or g.shelves_access %}
|
||||
{% if current_user.is_authenticated %}
|
||||
{% if current_user.shelf.all() or g.shelves_access %}
|
||||
<div id="shelf-actions" class="btn-toolbar" role="toolbar">
|
||||
<div class="btn-group" role="group" aria-label="Add to shelves">
|
||||
<button id="add-to-shelf" type="button" class="btn btn-primary btn-sm dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
@ -260,7 +260,7 @@
|
||||
</button>
|
||||
<ul id="add-to-shelves" class="dropdown-menu" aria-labelledby="add-to-shelf">
|
||||
{% for shelf in g.shelves_access %}
|
||||
{% if not shelf.id in books_shelfs and ( not shelf.is_public or g.user.role_edit_shelfs() ) %}
|
||||
{% if not shelf.id in books_shelfs and ( not shelf.is_public or current_user.role_edit_shelfs() ) %}
|
||||
<li>
|
||||
<a data-href="{{ url_for('shelf.add_to_shelf', book_id=entry.id, shelf_id=shelf.id) }}"
|
||||
data-remove-href="{{ url_for('shelf.remove_from_shelf', book_id=entry.id, shelf_id=shelf.id) }}"
|
||||
@ -281,7 +281,7 @@
|
||||
data-add-href="{{ url_for('shelf.add_to_shelf', book_id=entry.id, shelf_id=shelf.id) }}"
|
||||
class="btn btn-sm btn-default" role="button" data-shelf-action="remove"
|
||||
>
|
||||
<span {% if not shelf.is_public or g.user.role_edit_shelfs() %}
|
||||
<span {% if not shelf.is_public or current_user.role_edit_shelfs() %}
|
||||
class="glyphicon glyphicon-remove"
|
||||
{% endif %}></span> {{shelf.name}}{% if shelf.is_public == 1 %} {{_('(Public)')}}{% endif %}
|
||||
</a>
|
||||
@ -294,7 +294,7 @@
|
||||
{% endif %}
|
||||
|
||||
{% endif %}
|
||||
{% if g.user.role_edit() %}
|
||||
{% if current_user.role_edit() %}
|
||||
<div class="btn-toolbar" role="toolbar">
|
||||
<div class="btn-group" role="group" aria-label="Edit/Delete book">
|
||||
<a href="{{ url_for('edit-book.show_edit_book', book_id=entry.id) }}" class="btn btn-sm btn-primary" id="edit_book" role="button"><span class="glyphicon glyphicon-edit"></span> {{_('Edit Metadata')}}</a>
|
||||
|
@ -80,7 +80,7 @@
|
||||
</div>
|
||||
<button id="domain_allow_submit" class="btn btn-default">{{_('Add')}}</button>
|
||||
</form>
|
||||
<table class="table table-no-bordered" id="domain-allow-table" data-url="{{url_for('admin.list_domain', allow=1)}}" data-id-field="id" data-show-header="false" data-editable-mode="inline" data-locale="{{ g.user.locale }}">
|
||||
<table class="table table-no-bordered" id="domain-allow-table" data-url="{{url_for('admin.list_domain', allow=1)}}" data-id-field="id" data-show-header="false" data-editable-mode="inline" data-locale="{{ current_user.locale }}">
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-field="domain" id="domain-allow" data-escape="true" data-editable-type="text" data-editable-url="{{ url_for('admin.edit_domain', allow = 1)}}" data-editable="true" data-editable-title="{{_('Enter domainname')}}"></th>
|
||||
@ -90,7 +90,7 @@
|
||||
</thead>
|
||||
</table>
|
||||
<h2>{{_('Denied Domains (Blacklist)')}}</h2>
|
||||
<table class="table table-no-bordered" id="domain-deny-table" data-url="{{url_for('admin.list_domain', allow=0)}}" data-id-field="id" data-show-header="false" data-editable-mode="inline" data-locale="{{ g.user.locale }}">
|
||||
<table class="table table-no-bordered" id="domain-deny-table" data-url="{{url_for('admin.list_domain', allow=0)}}" data-id-field="id" data-show-header="false" data-editable-mode="inline" data-locale="{{ current_user.locale }}">
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-field="domain" id="domain-deny" data-escape="true" data-editable-type="text" data-editable-url="{{ url_for('admin.edit_domain', allow = 0)}}" data-editable="true" data-editable-title="{{_('Enter domainname')}}"></th>
|
||||
|
@ -1,7 +1,7 @@
|
||||
{% import 'image.html' as image %}
|
||||
{% extends "layout.html" %}
|
||||
{% block body %}
|
||||
{% if g.user.show_detail_random() and page != "discover" %}
|
||||
{% if current_user.show_detail_random() and page != "discover" %}
|
||||
<div class="discover random-books">
|
||||
<h2 class="random-books">{{_('Discover (Random Books)')}}</h2>
|
||||
<div class="row display-flex">
|
||||
|
@ -1,7 +1,7 @@
|
||||
{% from 'modal_dialogs.html' import restrict_modal, delete_book, filechooser_modal, delete_confirm_modal, change_confirm_modal %}
|
||||
{% import 'image.html' as image %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ g.user.locale }}">
|
||||
<html lang="{{ current_user.locale }}">
|
||||
<head>
|
||||
<title>{{instance}} | {{title}}</title>
|
||||
<meta charset="utf-8">
|
||||
@ -40,8 +40,9 @@
|
||||
<div class="home-btn"><a class="home-btn-tooltip" href="/" data-toggle="tooltip" title="" data-placement="bottom" data-original-title="Home"></a></div>
|
||||
<div class="plexBack"><a href="{{url_for('web.index')}}"></a></div>
|
||||
{% endif %}
|
||||
{% if g.user.is_authenticated or g.allow_anonymous %}
|
||||
<form class="navbar-form navbar-left" role="search" action="{{url_for('search.simple_search')}}" method="GET">
|
||||
{% if current_user.is_authenticated or g.allow_anonymous %}
|
||||
<form class="navbar-form navbar-left" role="search" action="{{url_for('search.simple_search')}}" method="POST">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||||
<div class="form-group input-group input-group-sm">
|
||||
<label for="query" class="sr-only">{{_('Search')}}</label>
|
||||
<input type="text" class="form-control" id="query" name="query" placeholder="{{_('Search Library')}}" value="{{searchterm}}">
|
||||
@ -52,28 +53,28 @@
|
||||
</form>
|
||||
{% endif %}
|
||||
<div class="navbar-collapse collapse">
|
||||
{% if g.user.is_authenticated or g.allow_anonymous %}
|
||||
{% if current_user.is_authenticated or g.allow_anonymous %}
|
||||
<ul class="nav navbar-nav ">
|
||||
<li><a href="{{url_for('search.advanced_search')}}" id="advanced_search"><span class="glyphicon glyphicon-search"></span><span class="hidden-sm"> {{_('Advanced Search')}}</span></a></li>
|
||||
</ul>
|
||||
{% endif %}
|
||||
<ul class="nav navbar-nav navbar-right" id="main-nav">
|
||||
{% if g.user.is_authenticated or g.allow_anonymous %}
|
||||
{% if current_user.is_authenticated or g.allow_anonymous %}
|
||||
{% if g.current_theme == 1 %}
|
||||
<li class="dropdown"><a href="#" class="dropdown-toggle profileDrop" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"><span class="glyphicon glyphicon-user"></span></a>
|
||||
<ul class="dropdown-menu profileDropli">
|
||||
<li><a id="top_user" data-text="{{_('Account')}}" href="{{url_for('web.profile')}}"><span class="glyphicon glyphicon-user"></span> <span class="hidden-sm">{{g.user.name}}</span></a></li>
|
||||
{% if g.allow_registration and not g.user.is_authenticated %}
|
||||
<li><a id="top_user" data-text="{{_('Account')}}" href="{{url_for('web.profile')}}"><span class="glyphicon glyphicon-user"></span> <span class="hidden-sm">{{current_user.name}}</span></a></li>
|
||||
{% if g.allow_registration and not current_user.is_authenticated %}
|
||||
<li><a id="login" href="{{url_for('web.login')}}"><span class="glyphicon glyphicon-log-in"></span> {{_('Login')}}</a></li>
|
||||
<li><a id="register" href="{{url_for('web.register')}}"><span class="glyphicon glyphicon-user"></span> {{_('Register')}}</a></li>
|
||||
{% endif %}
|
||||
{% if not g.user.is_anonymous %}
|
||||
{% if not current_user.is_anonymous %}
|
||||
<li><a id="logout" href="{{url_for('web.logout')}}"><span class="glyphicon glyphicon-log-out"></span> <span class="hidden-sm">{{_('Logout')}}</span></a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if g.user.role_upload() and g.allow_upload %}
|
||||
{% if current_user.role_upload() and g.allow_upload %}
|
||||
<li>
|
||||
<form id="form-upload" class="navbar-form" action="{{ url_for('edit-book.upload') }}" data-title="{{_('Uploading...')}}" data-footer="{{_('Close')}}" data-failed="{{_('Error')}}" data-message="{{_('Upload done, processing, please wait...')}}" method="post" enctype="multipart/form-data">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||||
@ -84,20 +85,20 @@
|
||||
</form>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if not g.user.is_anonymous and not simple%}
|
||||
{% if not current_user.is_anonymous and not simple%}
|
||||
<li class="top_tasks"><a id="top_tasks" href="{{url_for('tasks.get_tasks_status')}}"><span class="glyphicon glyphicon-tasks"></span> <span class="hidden-sm">{{_('Tasks')}}</span></a></li>
|
||||
{% endif %}
|
||||
{% if g.user.role_admin() %}
|
||||
{% if current_user.role_admin() %}
|
||||
<li><a id="top_admin" data-text="{{_('Settings')}}" href="{{url_for('admin.admin')}}"><span class="glyphicon glyphicon-dashboard"></span> <span class="hidden-sm">{{_('Admin')}}</span></a></li>
|
||||
{% endif %}
|
||||
{% if g.current_theme == 0 %}
|
||||
<li><a id="top_user" data-text="{{_('Account')}}" href="{{url_for('web.profile')}}"><span class="glyphicon glyphicon-user"></span> <span class="hidden-sm">{{g.user.name}}</span></a></li>
|
||||
{% if not g.user.is_anonymous %}
|
||||
<li><a id="top_user" data-text="{{_('Account')}}" href="{{url_for('web.profile')}}"><span class="glyphicon glyphicon-user"></span> <span class="hidden-sm">{{current_user.name}}</span></a></li>
|
||||
{% if not current_user.is_anonymous %}
|
||||
<li><a id="logout" href="{{url_for('web.logout')}}"><span class="glyphicon glyphicon-log-out"></span> <span class="hidden-sm">{{_('Logout')}}</span></a></li>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if g.allow_registration and not g.user.is_authenticated and g.current_theme == 0 %}
|
||||
{% if g.allow_registration and not current_user.is_authenticated and g.current_theme == 0 %}
|
||||
<li><a id="login" href="{{url_for('web.login')}}"><span class="glyphicon glyphicon-log-in"></span> {{_('Login')}}</a></li>
|
||||
<li><a id="register" href="{{url_for('web.register')}}"><span class="glyphicon glyphicon-user"></span> {{_('Register')}}</a></li>
|
||||
{% endif %}
|
||||
@ -138,22 +139,22 @@
|
||||
{%endif%}
|
||||
<div class="container-fluid">
|
||||
<div class="row-fluid">
|
||||
{% if g.user.is_authenticated or g.allow_anonymous %}
|
||||
{% if current_user.is_authenticated or g.allow_anonymous %}
|
||||
<div class="col-sm-2">
|
||||
<nav class="navigation">
|
||||
<ul class="list-unstyled" id="scnd-nav" intent in-standard-append="nav.navigation" in-mobile-after="#main-nav" in-mobile-class="nav navbar-nav">
|
||||
<li class="nav-head hidden-xs">{{_('Browse')}}</li>
|
||||
{% for element in sidebar %}
|
||||
{% if g.user.check_visibility(element['visibility']) and element['public'] %}
|
||||
{% if current_user.check_visibility(element['visibility']) and element['public'] %}
|
||||
<li id="nav_{{element['id']}}" {% if page == element['page'] %}class="active"{% endif %}><a href="{{url_for(element['link'], data=element['page'], sort_param='stored')}}"><span class="glyphicon {{element['glyph']}}"></span> {{_(element['text'])}}</a></li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% if g.user.is_authenticated or g.allow_anonymous %}
|
||||
{% if current_user.is_authenticated or g.allow_anonymous %}
|
||||
<li class="nav-head hidden-xs public-shelves">{{_('Shelves')}}</li>
|
||||
{% for shelf in g.shelves_access %}
|
||||
<li><a href="{{url_for('shelf.show_shelf', shelf_id=shelf.id)}}"><span class="glyphicon glyphicon-list shelf"></span> {{shelf.name|shortentitle(40)}}{% if shelf.is_public == 1 %} {{_('(Public)')}}{% endif %}</a></li>
|
||||
{% endfor %}
|
||||
{% if not g.user.is_anonymous %}
|
||||
{% if not current_user.is_anonymous %}
|
||||
<li id="nav_createshelf" class="create-shelf"><a href="{{url_for('shelf.create_shelf')}}">{{_('Create a Shelf')}}</a></li>
|
||||
<li id="nav_about" {% if page == 'stat' %}class="active"{% endif %}><a href="{{url_for('about.stats')}}"><span class="glyphicon glyphicon-info-sign"></span> {{_('About')}}</a></li>
|
||||
{% endif %}
|
||||
|
@ -149,7 +149,7 @@
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if not g.user.is_anonymous %}
|
||||
{% if not current_user.is_anonymous %}
|
||||
|
||||
<div class="custom_columns">
|
||||
<p>
|
||||
@ -159,7 +159,7 @@
|
||||
<span>{{_('Read')}}</span>
|
||||
</label>
|
||||
</p>
|
||||
{% if g.user.check_visibility(32768) %}
|
||||
{% if current_user.check_visibility(32768) %}
|
||||
<p>
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||||
<label class="block-label">
|
||||
@ -182,8 +182,8 @@
|
||||
|
||||
<div class="more-stuff">
|
||||
|
||||
{% if g.user.is_authenticated %}
|
||||
{% if g.user.shelf.all() or g.shelves_access %}
|
||||
{% if current_user.is_authenticated %}
|
||||
{% if current_user.shelf.all() or g.shelves_access %}
|
||||
<div id="shelf-actions" class="btn-toolbar" role="toolbar">
|
||||
<div class="btn-group" role="group" aria-label="Add to shelves">
|
||||
<button id="add-to-shelf" type="button" class="btn btn-primary btn-sm dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
@ -192,7 +192,7 @@
|
||||
</button>
|
||||
<ul id="add-to-shelves" class="dropdown-menu" aria-labelledby="add-to-shelf">
|
||||
{% for shelf in g.shelves_access %}
|
||||
{% if not shelf.id in books_shelfs and ( not shelf.is_public or g.user.role_edit_shelfs() ) %}
|
||||
{% if not shelf.id in books_shelfs and ( not shelf.is_public or current_user.role_edit_shelfs() ) %}
|
||||
<li>
|
||||
<a data-href="{{ url_for('shelf.add_to_shelf', book_id=entry.id, shelf_id=shelf.id) }}"
|
||||
data-remove-href="{{ url_for('shelf.remove_from_shelf', book_id=entry.id, shelf_id=shelf.id) }}"
|
||||
@ -213,7 +213,7 @@
|
||||
data-add-href="{{ url_for('shelf.add_to_shelf', book_id=entry.id, shelf_id=shelf.id) }}"
|
||||
class="btn btn-sm btn-default" role="button" data-shelf-action="remove"
|
||||
>
|
||||
<span {% if not shelf.is_public or g.user.role_edit_shelfs() %}
|
||||
<span {% if not shelf.is_public or current_user.role_edit_shelfs() %}
|
||||
class="glyphicon glyphicon-remove"
|
||||
{% endif %}></span> {{shelf.name}}{% if shelf.is_public == 1 %} {{_('(Public)')}}{% endif %}
|
||||
</a>
|
||||
@ -343,7 +343,7 @@ window.calibre = {
|
||||
bookUrl: "{{ url_for('static', filename=mp3file) }}/",
|
||||
bookmarkUrl: "{{ url_for('web.set_bookmark', book_id=mp3file, book_format=audioformat.upper()) }}",
|
||||
bookmark: "{{ bookmark.bookmark_key if bookmark != None }}",
|
||||
useBookmarks: "{{ g.user.is_authenticated | tojson }}"
|
||||
useBookmarks: "{{ current_user.is_authenticated | tojson }}"
|
||||
|
||||
};
|
||||
</script>
|
||||
|
@ -37,8 +37,8 @@
|
||||
</div>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
{% macro delete_book() %}
|
||||
{% if g.user.role_delete_books() %}
|
||||
{% macro delete_book(allow) %}
|
||||
{% if allow %}
|
||||
<div class="modal fade" id="deleteModal" role="dialog" aria-labelledby="metaDeleteLabel">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
|
@ -108,7 +108,7 @@
|
||||
bookmarkUrl: "{{ url_for('web.set_bookmark', book_id=bookid, book_format='EPUB') }}",
|
||||
bookUrl: "{{ url_for('web.serve_book', book_id=bookid, book_format='epub', anyname='file.epub') }}",
|
||||
bookmark: "{{ bookmark.bookmark_key if bookmark != None }}",
|
||||
useBookmarks: "{{ g.user.is_authenticated | tojson }}"
|
||||
useBookmarks: "{{ current_user.is_authenticated | tojson }}"
|
||||
};
|
||||
|
||||
function selectTheme(id) {
|
||||
|
@ -171,7 +171,7 @@ See https://github.com/adobe-type-tools/cmap-resources
|
||||
<span data-l10n-id="presentation_mode_label">Presentation Mode</span>
|
||||
</button>
|
||||
|
||||
{% if g.user.role_download() %}
|
||||
{% if current_user.role_download() %}
|
||||
<button id="secondaryPrint" class="secondaryToolbarButton visibleMediumView" title="Print" tabindex="53" data-l10n-id="print">
|
||||
<span data-l10n-id="print_label">Print</span>
|
||||
</button>
|
||||
@ -280,7 +280,7 @@ See https://github.com/adobe-type-tools/cmap-resources
|
||||
<span data-l10n-id="presentation_mode_label">Presentation Mode</span>
|
||||
</button>
|
||||
|
||||
{% if g.user.role_download() %}
|
||||
{% if current_user.role_download() %}
|
||||
<button id="print" class="toolbarButton hiddenMediumView" title="Print" tabindex="33" data-l10n-id="print">
|
||||
<span data-l10n-id="print_label">Print</span>
|
||||
</button>
|
||||
|
@ -7,8 +7,8 @@
|
||||
<p>{{_('Search Term:')}} {{adv_searchterm}}</p>
|
||||
{% else %}
|
||||
<h2>{{result_count}} {{_('Results for:')}} {{adv_searchterm}}</h2>
|
||||
{% if g.user.is_authenticated %}
|
||||
{% if g.user.shelf.all() or g.shelves_access %}
|
||||
{% if current_user.is_authenticated %}
|
||||
{% if current_user.shelf.all() or g.shelves_access %}
|
||||
<div id="shelf-actions" class="btn-toolbar" role="toolbar">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||||
<div class="btn-group" role="group" aria-label="Add to shelves">
|
||||
@ -18,7 +18,7 @@
|
||||
</button>
|
||||
<ul id="add-to-shelves" class="dropdown-menu" aria-labelledby="add-to-shelf">
|
||||
{% for shelf in g.shelves_access %}
|
||||
{% if not shelf.is_public or g.user.role_edit_shelfs() %}
|
||||
{% if not shelf.is_public or current_user.role_edit_shelfs() %}
|
||||
<li><a class="postAction" role="button" data-action="{{ url_for('shelf.search_to_shelf', shelf_id=shelf.id) }}"> {{shelf.name}}{% if shelf.is_public == 1 %} {{_('(Public)')}}{% endif %}</a></li>
|
||||
{% endif %}
|
||||
{%endfor%}
|
||||
|
@ -230,18 +230,18 @@
|
||||
|
||||
{% block js %}
|
||||
<script>
|
||||
var language = '{{ g.user.locale }}';
|
||||
var language = '{{ current_user.locale }}';
|
||||
</script>
|
||||
<script src="{{ url_for('static', filename='js/libs/bootstrap-datepicker/bootstrap-datepicker.min.js') }}"></script>
|
||||
{% if not g.user.locale == 'en' %}
|
||||
<script src="{{ url_for('static', filename='js/libs/bootstrap-datepicker/locales/bootstrap-datepicker.' + g.user.locale + '.min.js') }}" charset="UTF-8"></script>
|
||||
{% if not current_user.locale == 'en' %}
|
||||
<script src="{{ url_for('static', filename='js/libs/bootstrap-datepicker/locales/bootstrap-datepicker.' + current_user.locale + '.min.js') }}" charset="UTF-8"></script>
|
||||
{% endif %}
|
||||
<script src="{{ url_for('static', filename='js/libs/bootstrap-rating-input.min.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/libs/typeahead.bundle.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/edit_books.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/libs/bootstrap-select.min.js')}}"></script>
|
||||
{% if not g.user.locale == 'en' %}
|
||||
<script src="{{ url_for('static', filename='js/libs/bootstrap-select/defaults-' + g.user.locale + '.min.js') }}" charset="UTF-8"></script>
|
||||
{% if not current_user.locale == 'en' %}
|
||||
<script src="{{ url_for('static', filename='js/libs/bootstrap-select/defaults-' + current_user.locale + '.min.js') }}" charset="UTF-8"></script>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
{% block header %}
|
||||
|
@ -4,11 +4,11 @@
|
||||
<div class="discover">
|
||||
<h2>{{title}}</h2>
|
||||
<!--form method="post"--->
|
||||
{% if g.user.role_download() %}
|
||||
{% if current_user.role_download() %}
|
||||
<a id="shelf_down" href="{{ url_for('shelf.show_simpleshelf', shelf_id=shelf.id) }}" class="btn btn-primary">{{ _('Download') }} </a>
|
||||
{% endif %}
|
||||
{% if g.user.is_authenticated %}
|
||||
{% if (g.user.role_edit_shelfs() and shelf.is_public ) or not shelf.is_public %}
|
||||
{% if current_user.is_authenticated %}
|
||||
{% if (current_user.role_edit_shelfs() and shelf.is_public ) or not shelf.is_public %}
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||||
<div class="btn btn-danger" data-action="{{url_for('shelf.delete_shelf', shelf_id=shelf.id)}}" id="delete_shelf" data-value="{{ shelf.id }}">{{ _('Delete this Shelf') }}</div>
|
||||
<a id="edit_shelf" href="{{ url_for('shelf.edit_shelf', shelf_id=shelf.id) }}" class="btn btn-primary">{{ _('Edit Shelf Properties') }} </a>
|
||||
|
@ -8,7 +8,7 @@
|
||||
<label for="title">{{_('Title')}}</label>
|
||||
<input type="text" class="form-control" name="title" id="title" value="{{ shelf.name if shelf.name != None }}">
|
||||
</div>
|
||||
{% if g.user.role_edit_shelfs() %}
|
||||
{% if current_user.role_edit_shelfs() %}
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" name="is_public" {% if shelf.is_public == 1 %}checked{% endif %}> {{_('Share with Everyone')}}
|
||||
|
@ -1,5 +1,5 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ g.user.locale }}">
|
||||
<html lang="{{ current_user.locale }}">
|
||||
<head>
|
||||
<title>{{instance}} | {{title}}</title>
|
||||
<meta charset="utf-8">
|
||||
@ -58,7 +58,7 @@
|
||||
</div>
|
||||
|
||||
<div class="btn-group" role="group" aria-label="Download, send to eReader, reading">
|
||||
{% if g.user.role_download() %}
|
||||
{% if current_user.role_download() %}
|
||||
{% if entry.Books.data|length %}
|
||||
<div class="btn-group" role="group">
|
||||
{% for format in entry.Books.data %}
|
||||
|
@ -25,7 +25,7 @@
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
{% if g.user.role_admin() %}
|
||||
{% if current_user.role_admin() %}
|
||||
<h3>{{_('System Statistics')}}</h3>
|
||||
<table id="libs" class="table">
|
||||
<thead>
|
||||
|
@ -5,10 +5,10 @@
|
||||
{% block body %}
|
||||
<div class="discover">
|
||||
<h2>{{_('Tasks')}}</h2>
|
||||
<table class="table table-no-bordered" id="tasktable" data-url="{{ url_for('tasks.get_email_status_json') }}" data-sort-name="starttime" data-sort-order="asc" data-locale="{{ g.user.locale }}">
|
||||
<table class="table table-no-bordered" id="tasktable" data-url="{{ url_for('tasks.get_email_status_json') }}" data-sort-name="starttime" data-sort-order="asc" data-locale="{{ current_user.locale }}">
|
||||
<thead>
|
||||
<tr>
|
||||
{% if g.user.role_admin() %}
|
||||
{% if current_user.role_admin() %}
|
||||
<th data-halign="right" data-align="right" data-field="user" data-sortable="true">{{_('User')}}</th>
|
||||
{% endif %}
|
||||
<th data-halign="right" data-align="right" data-field="taskMessage" data-sortable="true">{{_('Task')}}</th>
|
||||
@ -16,7 +16,7 @@
|
||||
<th data-halign="right" data-align="right" data-field="progress" data-sortable="true" data-sorter="elementSorter">{{_('Progress')}}</th>
|
||||
<th data-halign="right" data-align="right" data-field="runtime" data-sortable="true" data-sort-name="rt">{{_('Run Time')}}</th>
|
||||
<th data-halign="right" data-align="right" data-field="starttime" data-sortable="true" data-sort-name="id">{{_('Start Time')}}</th>
|
||||
{% if g.user.role_admin() %}
|
||||
{% if current_user.role_admin() %}
|
||||
<th data-halign="right" data-align="right" data-formatter="TaskActions" data-switchable="false">{{_('Actions')}}</th>
|
||||
{% endif %}
|
||||
<th data-field="id" data-visible="false"></th>
|
||||
@ -27,8 +27,8 @@
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block modal %}
|
||||
{{ delete_book() }}
|
||||
{% if g.user.role_admin() %}
|
||||
{{ delete_book(current_user.role_delete_books()) }}
|
||||
{% if current_user.role_admin() %}
|
||||
<div class="modal fade" id="cancelTaskModal" role="dialog" aria-labelledby="metaCancelTaskLabel">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
|
@ -5,7 +5,7 @@
|
||||
<form role="form" method="POST" autocomplete="off">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||||
<div class="col-md-10 col-lg-8">
|
||||
{% if new_user or ( g.user and content.name != "Guest" and g.user.role_admin() ) %}
|
||||
{% if new_user or ( current_user and content.name != "Guest" and current_user.role_admin() ) %}
|
||||
<div class="form-group required">
|
||||
<label for="name">{{_('Username')}}</label>
|
||||
<input type="text" class="form-control" name="name" id="name" value="{{ content.name if content.name != None }}" autocomplete="off">
|
||||
@ -15,8 +15,8 @@
|
||||
<label for="email">{{_('Email')}}</label>
|
||||
<input type="email" class="form-control" name="email" id="email" value="{{ content.email if content.email != None }}" autocomplete="off">
|
||||
</div>
|
||||
{% if ( g.user and g.user.role_passwd() or g.user.role_admin() ) and not content.role_anonymous() %}
|
||||
{% if g.user and g.user.role_admin() and not new_user and not profile and ( mail_configured and content.email if content.email != None ) %}
|
||||
{% if ( current_user and current_user.role_passwd() or current_user.role_admin() ) and not content.role_anonymous() %}
|
||||
{% if current_user and current_user.role_admin() and not new_user and not profile and ( mail_configured and content.email if content.email != None ) %}
|
||||
<a class="btn btn-default postAction" id="resend_password" role="button" data-action="{{url_for('admin.reset_user_password', user_id = content.id) }}">{{_('Reset user Password')}}</a>
|
||||
{% endif %}
|
||||
<div class="form-group">
|
||||
@ -83,13 +83,13 @@
|
||||
<input type="checkbox" name="Show_detail_random" id="Show_detail_random" {% if content.show_detail_random() %}checked{% endif %}>
|
||||
<label for="Show_detail_random">{{_('Show Random Books in Detail View')}}</label>
|
||||
</div>
|
||||
{% if ( g.user and g.user.role_admin() and not new_user ) and not simple %}
|
||||
{% if ( current_user and current_user.role_admin() and not new_user ) and not simple %}
|
||||
<a href="#" id="get_user_tags" class="btn btn-default" data-id="{{content.id}}" data-toggle="modal" data-target="#restrictModal">{{_('Add Allowed/Denied Tags')}}</a>
|
||||
<a href="#" id="get_user_column_values" data-id="{{content.id}}" class="btn btn-default" data-toggle="modal" data-target="#restrictModal">{{_('Add allowed/Denied Custom Column Values')}}</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
{% if g.user and g.user.role_admin() and not profile %}
|
||||
{% if current_user and current_user.role_admin() and not profile %}
|
||||
{% if not content.role_anonymous() %}
|
||||
<div class="form-group">
|
||||
<input type="checkbox" name="admin_role" id="admin_role" {% if content.role_admin() %}checked{% endif %}>
|
||||
@ -143,7 +143,7 @@
|
||||
{% if not profile %}
|
||||
<div class="btn btn-default" data-back="{{ url_for('admin.admin') }}" id="back">{{_('Cancel')}}</div>
|
||||
{% endif %}
|
||||
{% if g.user and g.user.role_admin() and not profile and not new_user and not content.role_anonymous() %}
|
||||
{% if current_user and current_user.role_admin() and not profile and not new_user and not content.role_anonymous() %}
|
||||
<div class="btn btn-danger" id="btndeluser" data-value="{{ content.id }}" data-remote="false" >{{_('Delete User')}}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
@ -125,7 +125,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<table id="user-table" class="table table-no-bordered table-striped"
|
||||
data-url="{{url_for('admin.list_users')}}" data-locale="{{ g.user.locale }}">
|
||||
data-url="{{url_for('admin.list_users')}}" data-locale="{{ current_user.locale }}">
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-name="edit" data-buttontext="{{_('Edit User')}}" data-visible="{{visiblility.get('edit')}}" data-formatter="singleUserFormatter">{{_('Edit')}}</th>
|
||||
@ -185,8 +185,8 @@
|
||||
<script src="{{ url_for('static', filename='js/libs/bootstrap-table/bootstrap-table-editable.min.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/libs/bootstrap-table/bootstrap-editable.min.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/libs/bootstrap-select.min.js')}}"></script>
|
||||
{% if not g.user.locale == 'en' %}
|
||||
<script src="{{ url_for('static', filename='js/libs/bootstrap-select/defaults-' + g.user.locale + '.min.js') }}" charset="UTF-8"></script>
|
||||
{% if not current_user.locale == 'en' %}
|
||||
<script src="{{ url_for('static', filename='js/libs/bootstrap-select/defaults-' + current_user.locale + '.min.js') }}" charset="UTF-8"></script>
|
||||
{% endif %}
|
||||
<script src="{{ url_for('static', filename='js/table.js') }}"></script>
|
||||
{% endblock %}
|
||||
|
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
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
@ -16,16 +16,16 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import base64
|
||||
import binascii
|
||||
from functools import wraps
|
||||
|
||||
from sqlalchemy.sql.expression import func
|
||||
from werkzeug.security import check_password_hash
|
||||
from flask_login import login_required, login_user
|
||||
from flask import request, Response
|
||||
|
||||
from . import lm, ub, config, constants, services
|
||||
from . import lm, ub, config, constants, services, logger
|
||||
|
||||
log = logger.create()
|
||||
|
||||
def login_required_if_no_ano(func):
|
||||
@wraps(func)
|
||||
@ -36,6 +36,51 @@ def login_required_if_no_ano(func):
|
||||
|
||||
return decorated_view
|
||||
|
||||
def requires_basic_auth_if_no_ano(f):
|
||||
@wraps(f)
|
||||
def decorated(*args, **kwargs):
|
||||
auth = request.authorization
|
||||
if not auth or auth.type != 'basic':
|
||||
if config.config_anonbrowse != 1:
|
||||
user = load_user_from_reverse_proxy_header(request)
|
||||
if user:
|
||||
return f(*args, **kwargs)
|
||||
return _authenticate()
|
||||
else:
|
||||
return f(*args, **kwargs)
|
||||
if config.config_login_type == constants.LOGIN_LDAP and services.ldap:
|
||||
result, error = services.ldap.bind_user(auth.username, auth.password)
|
||||
if result:
|
||||
user = _fetch_user_by_name(auth.username)
|
||||
login_user(user)
|
||||
else:
|
||||
log.error(error)
|
||||
user = None
|
||||
else:
|
||||
user = _load_user_from_auth_header(auth.username, auth.password)
|
||||
if not user:
|
||||
return _authenticate()
|
||||
return f(*args, **kwargs)
|
||||
return decorated
|
||||
|
||||
|
||||
def _load_user_from_auth_header(username, password):
|
||||
user = _fetch_user_by_name(username)
|
||||
if bool(user and check_password_hash(str(user.password), password)):
|
||||
login_user(user)
|
||||
return user
|
||||
else:
|
||||
ip_address = request.headers.get('X-Forwarded-For', request.remote_addr)
|
||||
log.warning('OPDS Login failed for user "%s" IP-address: %s', username, ip_address)
|
||||
return None
|
||||
|
||||
|
||||
def _authenticate():
|
||||
return Response(
|
||||
'Could not verify your access level for that URL.\n'
|
||||
'You have to login with proper credentials', 401,
|
||||
{'WWW-Authenticate': 'Basic realm="Login Required"'})
|
||||
|
||||
|
||||
def _fetch_user_by_name(username):
|
||||
return ub.session.query(ub.User).filter(func.lower(ub.User.name) == username.lower()).first()
|
||||
@ -43,45 +88,20 @@ def _fetch_user_by_name(username):
|
||||
|
||||
@lm.user_loader
|
||||
def load_user(user_id):
|
||||
return ub.session.query(ub.User).filter(ub.User.id == int(user_id)).first()
|
||||
user = ub.session.query(ub.User).filter(ub.User.id == int(user_id)).first()
|
||||
return user
|
||||
|
||||
|
||||
@lm.request_loader
|
||||
def load_user_from_request(request):
|
||||
def load_user_from_reverse_proxy_header(req):
|
||||
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)
|
||||
rp_header_username = req.headers.get(rp_header_name)
|
||||
if rp_header_username:
|
||||
user = _fetch_user_by_name(rp_header_username)
|
||||
if user:
|
||||
login_user(user)
|
||||
return user
|
||||
return None
|
||||
|
||||
auth_header = request.headers.get("Authorization")
|
||||
if auth_header:
|
||||
user = load_user_from_auth_header(auth_header)
|
||||
if user:
|
||||
return user
|
||||
|
||||
return
|
||||
|
||||
|
||||
def load_user_from_auth_header(header_val):
|
||||
if header_val.startswith('Basic '):
|
||||
header_val = header_val.replace('Basic ', '', 1)
|
||||
basic_username = basic_password = '' # nosec
|
||||
try:
|
||||
header_val = base64.b64decode(header_val).decode('utf-8')
|
||||
# Users with colon are invalid: rfc7617 page 4
|
||||
basic_username = header_val.split(':', 1)[0]
|
||||
basic_password = header_val.split(':', 1)[1]
|
||||
except (TypeError, UnicodeDecodeError, binascii.Error):
|
||||
pass
|
||||
user = _fetch_user_by_name(basic_username)
|
||||
if user and config.config_login_type == constants.LOGIN_LDAP and services.ldap:
|
||||
if services.ldap.bind_user(str(user.password), basic_password):
|
||||
return user
|
||||
if user and check_password_hash(str(user.password), basic_password):
|
||||
return user
|
||||
return
|
||||
|
@ -31,6 +31,7 @@ from flask_babel import gettext as _
|
||||
from flask_babel import lazy_gettext as N_
|
||||
from flask_babel import get_locale
|
||||
from flask_login import login_user, logout_user, login_required, current_user
|
||||
from flask_limiter import RateLimitExceeded
|
||||
from sqlalchemy.exc import IntegrityError, InvalidRequestError, OperationalError
|
||||
from sqlalchemy.sql.expression import text, func, false, not_, and_, or_
|
||||
from sqlalchemy.orm.attributes import flag_modified
|
||||
@ -56,7 +57,8 @@ from .kobo_sync_status import remove_synced_book
|
||||
from .render_template import render_title_template
|
||||
from .kobo_sync_status import change_archived_books
|
||||
from . import limiter
|
||||
from flask_limiter import RateLimitExceeded
|
||||
from .services.worker import WorkerThread
|
||||
from .tasks_status import render_task_status
|
||||
|
||||
|
||||
feature_support = {
|
||||
@ -394,7 +396,7 @@ def render_books_list(data, sort_param, book_id, page):
|
||||
elif data == "archived":
|
||||
return render_archived_books(page, order)
|
||||
elif data == "search":
|
||||
term = (request.args.get('query') or '')
|
||||
term = json.loads(flask_session.get('query', ''))
|
||||
offset = int(int(config.config_books_per_page) * (page - 1))
|
||||
return render_search_results(term, offset, order, config.config_books_per_page)
|
||||
elif data == "advsearch":
|
||||
|
BIN
library/metadata.db
Normal file
BIN
library/metadata.db
Normal file
Binary file not shown.
432
messages.pot
432
messages.pot
File diff suppressed because it is too large
Load Diff
@ -5,7 +5,7 @@ Flask-Babel>=0.11.1,<3.1.0
|
||||
Flask-Login>=0.3.2,<0.6.3
|
||||
Flask-Principal>=0.3.2,<0.5.1
|
||||
backports_abc>=0.4
|
||||
Flask>=1.0.2,<2.2.0
|
||||
Flask>=1.0.2,<2.3.0
|
||||
iso-639>=0.4.5,<0.5.0
|
||||
PyPDF>=3.0.0,<3.3.0
|
||||
pytz>=2016.10
|
||||
|
Loading…
Reference in New Issue
Block a user