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
|
constants.py ident export-subst
|
||||||
/test export-ignore
|
/test export-ignore
|
||||||
|
/library export-ignore
|
||||||
cps/static/css/libs/* linguist-vendored
|
cps/static/css/libs/* linguist-vendored
|
||||||
cps/static/js/libs/* linguist-vendored
|
cps/static/js/libs/* linguist-vendored
|
||||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -28,6 +28,7 @@ cps/cache
|
||||||
.idea/
|
.idea/
|
||||||
*.bak
|
*.bak
|
||||||
*.log.*
|
*.log.*
|
||||||
|
.key
|
||||||
|
|
||||||
settings.yaml
|
settings.yaml
|
||||||
gdrive_credentials
|
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 \
|
Point your browser to `http://localhost:8083` or `http://localhost:8083/opds` for the OPDS catalog \
|
||||||
Login with default admin login \
|
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) \
|
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)
|
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/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
from flask_login import LoginManager
|
from flask_login import LoginManager, confirm_login
|
||||||
from flask import session
|
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):
|
class MyLoginManager(LoginManager):
|
||||||
def _session_protection_failed(self):
|
def _session_protection_failed(self):
|
||||||
|
@ -33,3 +34,19 @@ class MyLoginManager(LoginManager):
|
||||||
and _session.get('csrf_token', None))) and ident != _session.get('_id', None):
|
and _session.get('csrf_token', None))) and ident != _session.get('_id', None):
|
||||||
return super(). _session_protection_failed()
|
return super(). _session_protection_failed()
|
||||||
return False
|
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 functools import wraps
|
||||||
|
|
||||||
from flask import Blueprint, flash, redirect, url_for, abort, request, make_response, send_from_directory, g, Response
|
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 gettext as _
|
||||||
from flask_babel import get_locale, format_time, format_datetime, format_timedelta
|
from flask_babel import get_locale, format_time, format_datetime, format_timedelta
|
||||||
from flask import session as flask_session
|
from flask import session as flask_session
|
||||||
|
@ -101,21 +101,16 @@ def admin_required(f):
|
||||||
|
|
||||||
@admi.before_app_request
|
@admi.before_app_request
|
||||||
def before_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:
|
if not ub.check_user_session(current_user.id, flask_session.get('_id')) and 'opds' not in request.path:
|
||||||
logout_user()
|
logout_user()
|
||||||
g.constants = constants
|
g.constants = constants
|
||||||
g.user = current_user
|
# g.user = current_user
|
||||||
g.google_site_verification = os.getenv('GOOGLE_SITE_VERIFICATION','')
|
g.google_site_verification = os.getenv('GOOGLE_SITE_VERIFICATION','')
|
||||||
g.allow_registration = config.config_public_reg
|
g.allow_registration = config.config_public_reg
|
||||||
g.allow_anonymous = config.config_anonbrowse
|
g.allow_anonymous = config.config_anonbrowse
|
||||||
g.allow_upload = config.config_uploading
|
g.allow_upload = config.config_uploading
|
||||||
g.current_theme = config.config_theme
|
g.current_theme = config.config_theme
|
||||||
g.config_authors_max = config.config_authors_max
|
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 \
|
if '/static/' not in request.path and not config.db_configured and \
|
||||||
request.endpoint not in ('admin.ajax_db_config',
|
request.endpoint not in ('admin.ajax_db_config',
|
||||||
'admin.simulatedbchange',
|
'admin.simulatedbchange',
|
||||||
|
|
11
cps/babel.py
11
cps/babel.py
|
@ -1,7 +1,8 @@
|
||||||
from babel import negotiate_locale
|
from babel import negotiate_locale
|
||||||
from flask_babel import Babel, Locale
|
from flask_babel import Babel, Locale
|
||||||
from babel.core import UnknownLocaleError
|
from babel.core import UnknownLocaleError
|
||||||
from flask import request, g
|
from flask import request
|
||||||
|
from flask_login import current_user
|
||||||
|
|
||||||
from . import logger
|
from . import logger
|
||||||
|
|
||||||
|
@ -11,10 +12,10 @@ babel = Babel()
|
||||||
|
|
||||||
def get_locale():
|
def get_locale():
|
||||||
# if a user is logged in, use the locale from the user settings
|
# if a user is logged in, use the locale from the user settings
|
||||||
user = getattr(g, 'user', None)
|
if current_user is not None and hasattr(current_user, "locale"):
|
||||||
if user is not None and hasattr(user, "locale"):
|
# if the account is the guest account bypass the config lang settings
|
||||||
if user.name != 'Guest': # if the account is the guest account bypass the config lang settings
|
if current_user.name != 'Guest':
|
||||||
return user.locale
|
return current_user.locale
|
||||||
|
|
||||||
preferred = list()
|
preferred = list()
|
||||||
if request.accept_languages:
|
if request.accept_languages:
|
||||||
|
|
|
@ -163,7 +163,7 @@ def selected_roles(dictionary):
|
||||||
BookMeta = namedtuple('BookMeta', 'file_path, extension, title, author, cover, description, tags, series, '
|
BookMeta = namedtuple('BookMeta', 'file_path, extension, title, author, cover, description, tags, series, '
|
||||||
'series_id, languages, publisher, pubdate, identifiers')
|
'series_id, languages, publisher, pubdate, identifiers')
|
||||||
|
|
||||||
STABLE_VERSION = {'version': '0.6.19'}
|
STABLE_VERSION = {'version': '0.6.20 Beta'}
|
||||||
|
|
||||||
NIGHTLY_VERSION = dict()
|
NIGHTLY_VERSION = dict()
|
||||||
NIGHTLY_VERSION[0] = '$Format:%H$'
|
NIGHTLY_VERSION[0] = '$Format:%H$'
|
||||||
|
|
60
cps/opds.py
60
cps/opds.py
|
@ -22,41 +22,26 @@
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
from urllib.parse import unquote_plus
|
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_login import current_user
|
||||||
from flask_babel import get_locale
|
from flask_babel import get_locale
|
||||||
|
from flask_babel import gettext as _
|
||||||
from sqlalchemy.sql.expression import func, text, or_, and_, true
|
from sqlalchemy.sql.expression import func, text, or_, and_, true
|
||||||
from sqlalchemy.exc import InvalidRequestError, OperationalError
|
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 .helper import get_download_link, get_book_cover
|
||||||
from .pagination import Pagination
|
from .pagination import Pagination
|
||||||
from .web import render_read_books
|
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__)
|
opds = Blueprint('opds', __name__)
|
||||||
|
|
||||||
log = logger.create()
|
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/")
|
||||||
@opds.route("/opds")
|
@opds.route("/opds")
|
||||||
@requires_basic_auth_if_no_ano
|
@requires_basic_auth_if_no_ano
|
||||||
|
@ -356,7 +341,8 @@ def feed_languages(book_id):
|
||||||
@requires_basic_auth_if_no_ano
|
@requires_basic_auth_if_no_ano
|
||||||
def feed_shelfindex():
|
def feed_shelfindex():
|
||||||
off = request.args.get("offset") or 0
|
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)
|
number = len(shelf)
|
||||||
pagination = Pagination((int(off) / (int(config.config_books_per_page)) + 1), config.config_books_per_page,
|
pagination = Pagination((int(off) / (int(config.config_books_per_page)) + 1), config.config_books_per_page,
|
||||||
number)
|
number)
|
||||||
|
@ -403,11 +389,7 @@ def feed_shelf(book_id):
|
||||||
@opds.route("/opds/download/<book_id>/<book_format>/")
|
@opds.route("/opds/download/<book_id>/<book_format>/")
|
||||||
@requires_basic_auth_if_no_ano
|
@requires_basic_auth_if_no_ano
|
||||||
def opds_download_link(book_id, book_format):
|
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
|
if not current_user.role_download():
|
||||||
# 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():
|
|
||||||
return abort(403)
|
return abort(403)
|
||||||
if "Kobo" in request.headers.get('User-Agent'):
|
if "Kobo" in request.headers.get('User-Agent'):
|
||||||
client = "kobo"
|
client = "kobo"
|
||||||
|
@ -479,32 +461,6 @@ def feed_search(term):
|
||||||
return render_xml_template('feed.xml', searchterm="")
|
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):
|
def render_xml_template(*args, **kwargs):
|
||||||
# ToDo: return time in current timezone similar to %z
|
# 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 flask_babel import gettext as _
|
||||||
from werkzeug.local import LocalProxy
|
from werkzeug.local import LocalProxy
|
||||||
from flask_login import current_user
|
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
|
from .ub import User
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
log = logger.create()
|
log = logger.create()
|
||||||
|
|
||||||
def get_sidebar_config(kwargs=None):
|
def get_sidebar_config(kwargs=None):
|
||||||
|
@ -45,12 +47,12 @@ def get_sidebar_config(kwargs=None):
|
||||||
"show_text": _('Show Hot Books'), "config_show": True})
|
"show_text": _('Show Hot Books'), "config_show": True})
|
||||||
if current_user.role_admin():
|
if current_user.role_admin():
|
||||||
sidebar.append({"glyph": "glyphicon-download", "text": _('Downloaded Books'), "link": 'web.download_list',
|
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'),
|
"page": "download", "show_text": _('Show Downloaded Books'),
|
||||||
"config_show": content})
|
"config_show": content})
|
||||||
else:
|
else:
|
||||||
sidebar.append({"glyph": "glyphicon-download", "text": _('Downloaded Books'), "link": 'web.books_list',
|
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'),
|
"page": "download", "show_text": _('Show Downloaded Books'),
|
||||||
"config_show": content})
|
"config_show": content})
|
||||||
sidebar.append(
|
sidebar.append(
|
||||||
|
@ -58,11 +60,11 @@ def get_sidebar_config(kwargs=None):
|
||||||
"visibility": constants.SIDEBAR_BEST_RATED, 'public': True, "page": "rated",
|
"visibility": constants.SIDEBAR_BEST_RATED, 'public': True, "page": "rated",
|
||||||
"show_text": _('Show Top Rated Books'), "config_show": True})
|
"show_text": _('Show Top Rated Books'), "config_show": True})
|
||||||
sidebar.append({"glyph": "glyphicon-eye-open", "text": _('Read Books'), "link": 'web.books_list', "id": "read",
|
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})
|
"page": "read", "show_text": _('Show Read and Unread'), "config_show": content})
|
||||||
sidebar.append(
|
sidebar.append(
|
||||||
{"glyph": "glyphicon-eye-close", "text": _('Unread Books'), "link": 'web.books_list', "id": "unread",
|
{"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})
|
"show_text": _('Show unread'), "config_show": False})
|
||||||
sidebar.append({"glyph": "glyphicon-random", "text": _('Discover'), "link": 'web.books_list', "id": "rand",
|
sidebar.append({"glyph": "glyphicon-random", "text": _('Discover'), "link": 'web.books_list', "id": "rand",
|
||||||
"visibility": constants.SIDEBAR_RANDOM, 'public': True, "page": "discover",
|
"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",
|
"visibility": constants.SIDEBAR_PUBLISHER, 'public': True, "page": "publisher",
|
||||||
"show_text": _('Show Publisher Section'), "config_show":True})
|
"show_text": _('Show Publisher Section'), "config_show":True})
|
||||||
sidebar.append({"glyph": "glyphicon-flag", "text": _('Languages'), "link": 'web.language_overview', "id": "lang",
|
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",
|
"page": "language",
|
||||||
"show_text": _('Show Language Section'), "config_show": True})
|
"show_text": _('Show Language Section'), "config_show": True})
|
||||||
sidebar.append({"glyph": "glyphicon-star-empty", "text": _('Ratings'), "link": 'web.ratings_list', "id": "rate",
|
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})
|
"page": "format", "show_text": _('Show File Formats Section'), "config_show": True})
|
||||||
sidebar.append(
|
sidebar.append(
|
||||||
{"glyph": "glyphicon-trash", "text": _('Archived Books'), "link": 'web.books_list', "id": "archived",
|
{"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})
|
"show_text": _('Show Archived Books'), "config_show": content})
|
||||||
if not simple:
|
if not simple:
|
||||||
sidebar.append(
|
sidebar.append(
|
||||||
{"glyph": "glyphicon-th-list", "text": _('Books List'), "link": 'web.books_table', "id": "list",
|
{"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})
|
"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
|
return sidebar, simple
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -35,12 +35,13 @@ search = Blueprint('search', __name__)
|
||||||
log = logger.create()
|
log = logger.create()
|
||||||
|
|
||||||
|
|
||||||
@search.route("/search", methods=["GET"])
|
@search.route("/search", methods=["POST"])
|
||||||
@login_required_if_no_ano
|
@login_required_if_no_ano
|
||||||
def simple_search():
|
def simple_search():
|
||||||
term = request.args.get("query")
|
term = dict(request.form).get("query")
|
||||||
if term:
|
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:
|
else:
|
||||||
return render_title_template('search.html',
|
return render_title_template('search.html',
|
||||||
searchterm="",
|
searchterm="",
|
||||||
|
|
|
@ -269,7 +269,7 @@ class WebServer(object):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def shutdown_scheduler():
|
def shutdown_scheduler():
|
||||||
scheduler = BackgroundScheduler()
|
scheduler = BackgroundScheduler()
|
||||||
if scheduler: # and not scheduler.scheduler.STATE_STOPPED:
|
if scheduler:
|
||||||
scheduler.scheduler.shutdown()
|
scheduler.scheduler.shutdown()
|
||||||
|
|
||||||
def _killServer(self, __, ___):
|
def _killServer(self, __, ___):
|
||||||
|
|
|
@ -140,6 +140,7 @@ table .bg-dark-danger a { color: #fff; }
|
||||||
|
|
||||||
.container-fluid .book {
|
.container-fluid .book {
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
|
max-width: 180px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
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"
|
layoutMode : "fitRows"
|
||||||
});
|
});
|
||||||
|
|
||||||
$(".grid").isotope({
|
|
||||||
// options
|
|
||||||
itemSelector : ".grid-item",
|
|
||||||
layoutMode : "fitColumns"
|
|
||||||
});
|
|
||||||
|
|
||||||
if ($(".load-more").length && $(".next").length) {
|
if ($(".load-more").length && $(".next").length) {
|
||||||
var $loadMore = $(".load-more .row").infiniteScroll({
|
var $loadMore = $(".load-more .row").infiniteScroll({
|
||||||
debug: false,
|
debug: false,
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<!-- Always use full-sized image for the book edit page -->
|
<!-- 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)}}" />
|
<img id="detailcover" title="{{book.title}}" src="{{url_for('web.get_cover', book_id=book.id, resolution='og', c=book|last_modified)}}" />
|
||||||
</div>
|
</div>
|
||||||
{% if g.user.role_delete_books() %}
|
{% if current_user.role_delete_books() %}
|
||||||
<div class="text-center">
|
<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>
|
<button type="button" class="btn btn-danger" id="delete" data-toggle="modal" data-delete-id="{{ book.id }}" data-target="#deleteModal">{{_("Delete Book")}}</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -99,7 +99,7 @@
|
||||||
<label for="rating">{{_('Rating')}}</label>
|
<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 %}">
|
<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>
|
</div>
|
||||||
{% if g.user.role_upload() and g.allow_upload %}
|
{% if current_user.role_upload() and g.allow_upload %}
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="cover_url">{{_('Fetch Cover from URL (JPEG - Image will be downloaded and stored in database)')}}</label>
|
<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="">
|
<input type="text" class="form-control" name="cover_url" id="cover_url" value="">
|
||||||
|
@ -196,7 +196,7 @@
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endif %}
|
{% 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">
|
<div role="group" aria-label="Upload new book format">
|
||||||
<label class="btn btn-primary btn-file" for="btn-upload-format">{{ _('Upload Format') }}</label>
|
<label class="btn btn-primary btn-file" for="btn-upload-format">{{ _('Upload Format') }}</label>
|
||||||
<div class="upload-format-input-text" id="upload-format"></div>
|
<div class="upload-format-input-text" id="upload-format"></div>
|
||||||
|
@ -219,7 +219,7 @@
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block modal %}
|
{% block modal %}
|
||||||
{{ delete_book() }}
|
{{ delete_book(current_user.role_delete_books()) }}
|
||||||
{{ delete_confirm_modal() }}
|
{{ delete_confirm_modal() }}
|
||||||
|
|
||||||
<div class="modal fade" id="metaModal" tabindex="-1" role="dialog" aria-labelledby="metaModalLabel">
|
<div class="modal fade" id="metaModal" tabindex="-1" role="dialog" aria-labelledby="metaModalLabel">
|
||||||
|
@ -291,7 +291,7 @@
|
||||||
'description': {{_('Description')|safe|tojson}},
|
'description': {{_('Description')|safe|tojson}},
|
||||||
'source': {{_('Source')|safe|tojson}},
|
'source': {{_('Source')|safe|tojson}},
|
||||||
};
|
};
|
||||||
var language = '{{ g.user.locale }}';
|
var language = '{{ current_user.locale }}';
|
||||||
|
|
||||||
$("#add-identifier-line").click(function() {
|
$("#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
|
// 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/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/tinymce/tinymce.min.js') }}"></script>
|
||||||
<script src="{{ url_for('static', filename='js/libs/bootstrap-datepicker/bootstrap-datepicker.min.js') }}"></script>
|
<script src="{{ url_for('static', filename='js/libs/bootstrap-datepicker/bootstrap-datepicker.min.js') }}"></script>
|
||||||
{% if not g.user.locale == 'en' %}
|
{% if not current_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>
|
<script src="{{ url_for('static', filename='js/libs/bootstrap-datepicker/locales/bootstrap-datepicker.' + current_user.locale + '.min.js') }}" charset="UTF-8"></script>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<script src="{{ url_for('static', filename='js/edit_books.js') }}"></script>
|
<script src="{{ url_for('static', filename='js/edit_books.js') }}"></script>
|
||||||
<script src="{{ url_for('static', filename='js/fullscreen.js') }}"></script>
|
<script src="{{ url_for('static', filename='js/fullscreen.js') }}"></script>
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
{% if sort %}data-sortable="true" {% endif %}
|
{% if sort %}data-sortable="true" {% endif %}
|
||||||
data-visible = "{{visiblility.get(parameter)}}"
|
data-visible = "{{visiblility.get(parameter)}}"
|
||||||
data-escape="true"
|
data-escape="true"
|
||||||
{% if g.user.role_edit() %}
|
{% if current_user.role_edit() %}
|
||||||
data-editable-type="text"
|
data-editable-type="text"
|
||||||
data-editable-url="{{ url_for('edit-book.edit_list_book', param=parameter)}}"
|
data-editable-url="{{ url_for('edit-book.edit_list_book', param=parameter)}}"
|
||||||
data-editable-title="{{ edit_text }}"
|
data-editable-title="{{ edit_text }}"
|
||||||
|
@ -53,10 +53,10 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<table id="books-table" class="table table-no-bordered table-striped"
|
<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>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
{% if g.user.role_edit() %}
|
{% if current_user.role_edit() %}
|
||||||
<th data-field="state" data-checkbox="true" data-sortable="true"></th>
|
<th data-field="state" data-checkbox="true" data-sortable="true"></th>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<th data-field="id" id="id" data-visible="false" data-switchable="false"></th>
|
<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('authors', _('Enter Authors'),_('Authors'), true, true) }}
|
||||||
{{ text_table_row('tags', _('Enter Categories'),_('Categories'), false, true) }}
|
{{ text_table_row('tags', _('Enter Categories'),_('Categories'), false, true) }}
|
||||||
{{ text_table_row('series', _('Enter Series'),_('Series'), 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) }}
|
{{ 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-->
|
<!--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) }}
|
{{ 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>
|
<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 g.user.check_visibility(32768) %}
|
{% if current_user.check_visibility(32768) %}
|
||||||
{{ book_checkbox_row('is_archived', _('Archive Status'), false)}}
|
{{ book_checkbox_row('is_archived', _('Archive Status'), false)}}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{{ book_checkbox_row('read_status', _('Read Status'), false)}}
|
{{ book_checkbox_row('read_status', _('Read Status'), false)}}
|
||||||
{% for c in cc %}
|
{% for c in cc %}
|
||||||
{% if c.datatype == "int" %}
|
{% 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" %}
|
{% 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" %}
|
{% 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" %}
|
{% 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"] %}
|
{% elif c.datatype in ["datetime"] %}
|
||||||
<!-- missing -->
|
<!-- missing -->
|
||||||
{% elif c.datatype == "text" %}
|
{% elif c.datatype == "text" %}
|
||||||
{{ text_table_row('custom_column_' + c.id|string, _('Enter ') + c.name, c.name, false, false) }}
|
{{ text_table_row('custom_column_' + c.id|string, _('Enter ') + c.name, c.name, false, false) }}
|
||||||
{% elif c.datatype == "comments" %}
|
{% 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" %}
|
{% elif c.datatype == "bool" %}
|
||||||
{{ book_checkbox_row('custom_column_' + c.id|string, c.name, false)}}
|
{{ book_checkbox_row('custom_column_' + c.id|string, c.name, false)}}
|
||||||
{% else %}
|
{% else %}
|
||||||
<!--{{ text_table_row('custom_column_' + c.id|string, _('Enter ') + c.name, c.name, false, false) }} -->
|
<!--{{ text_table_row('custom_column_' + c.id|string, _('Enter ') + c.name, c.name, false, false) }} -->
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% 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>
|
<th data-align="right" data-formatter="EbookActions" data-switchable="false">{{_('Delete')}}</th>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -104,8 +104,8 @@
|
||||||
</table>
|
</table>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block modal %}
|
{% block modal %}
|
||||||
{{ delete_book() }}
|
{{ delete_book(current_user.role_delete_books()) }}
|
||||||
{% if g.user.role_edit() %}
|
{% if current_user.role_edit() %}
|
||||||
<div class="modal fade" id="mergeModal" role="dialog" aria-labelledby="metaMergeLabel">
|
<div class="modal fade" id="mergeModal" role="dialog" aria-labelledby="metaMergeLabel">
|
||||||
<div class="modal-dialog">
|
<div class="modal-dialog">
|
||||||
<div class="modal-content">
|
<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-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-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-table/bootstrap-editable.min.js') }}"></script>
|
||||||
{% if not g.user.locale == 'en' %}
|
{% if not current_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>
|
<script src="{{ url_for('static', filename='js/libs/bootstrap-table/locale/bootstrap-table-' + current_user.locale + '.min.js') }}" charset="UTF-8"></script>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<script src="{{ url_for('static', filename='js/libs/wysihtml5-0.3.0.min.js') }}"></script>
|
<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>
|
<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="col-sm-9 col-lg-9 book-meta">
|
||||||
<div class="btn-toolbar" role="toolbar">
|
<div class="btn-toolbar" role="toolbar">
|
||||||
<div class="btn-group" role="group" aria-label="Download, send to eReader, reading">
|
<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 %}
|
{% if entry.data|length %}
|
||||||
<div class="btn-group" role="group">
|
<div class="btn-group" role="group">
|
||||||
{% if entry.data|length < 2 %}
|
{% if entry.data|length < 2 %}
|
||||||
|
@ -37,7 +37,7 @@
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% 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() }}">
|
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||||||
{% if entry.email_share_list.__len__() == 1 %}
|
{% 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>
|
<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>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% 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">
|
<div class="btn-group" role="group">
|
||||||
{% if entry.reader_list|length > 1 %}
|
{% 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">
|
<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 %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% 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">
|
<div class="btn-group" role="group">
|
||||||
{% if entry.audio_entries|length > 1 %}
|
{% 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">
|
<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>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if not g.user.is_anonymous %}
|
{% if not current_user.is_anonymous %}
|
||||||
|
|
||||||
<div class="custom_columns">
|
<div class="custom_columns">
|
||||||
<p>
|
<p>
|
||||||
|
@ -225,7 +225,7 @@
|
||||||
</label>
|
</label>
|
||||||
</form>
|
</form>
|
||||||
</p>
|
</p>
|
||||||
{% if g.user.check_visibility(32768) %}
|
{% if current_user.check_visibility(32768) %}
|
||||||
<p>
|
<p>
|
||||||
<form id="archived_form" action="{{ url_for('web.toggle_archived', book_id=entry.id)}}" method="POST">
|
<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() }}">
|
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||||||
|
@ -250,8 +250,8 @@
|
||||||
|
|
||||||
<div class="more-stuff">
|
<div class="more-stuff">
|
||||||
|
|
||||||
{% if g.user.is_authenticated %}
|
{% if current_user.is_authenticated %}
|
||||||
{% if g.user.shelf.all() or g.shelves_access %}
|
{% if current_user.shelf.all() or g.shelves_access %}
|
||||||
<div id="shelf-actions" class="btn-toolbar" role="toolbar">
|
<div id="shelf-actions" class="btn-toolbar" role="toolbar">
|
||||||
<div class="btn-group" role="group" aria-label="Add to shelves">
|
<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">
|
<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>
|
</button>
|
||||||
<ul id="add-to-shelves" class="dropdown-menu" aria-labelledby="add-to-shelf">
|
<ul id="add-to-shelves" class="dropdown-menu" aria-labelledby="add-to-shelf">
|
||||||
{% for shelf in g.shelves_access %}
|
{% 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>
|
<li>
|
||||||
<a data-href="{{ url_for('shelf.add_to_shelf', book_id=entry.id, shelf_id=shelf.id) }}"
|
<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) }}"
|
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) }}"
|
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"
|
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"
|
class="glyphicon glyphicon-remove"
|
||||||
{% endif %}></span> {{shelf.name}}{% if shelf.is_public == 1 %} {{_('(Public)')}}{% endif %}
|
{% endif %}></span> {{shelf.name}}{% if shelf.is_public == 1 %} {{_('(Public)')}}{% endif %}
|
||||||
</a>
|
</a>
|
||||||
|
@ -294,7 +294,7 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if g.user.role_edit() %}
|
{% if current_user.role_edit() %}
|
||||||
<div class="btn-toolbar" role="toolbar">
|
<div class="btn-toolbar" role="toolbar">
|
||||||
<div class="btn-group" role="group" aria-label="Edit/Delete book">
|
<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>
|
<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>
|
</div>
|
||||||
<button id="domain_allow_submit" class="btn btn-default">{{_('Add')}}</button>
|
<button id="domain_allow_submit" class="btn btn-default">{{_('Add')}}</button>
|
||||||
</form>
|
</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>
|
<thead>
|
||||||
<tr>
|
<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>
|
<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>
|
</thead>
|
||||||
</table>
|
</table>
|
||||||
<h2>{{_('Denied Domains (Blacklist)')}}</h2>
|
<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>
|
<thead>
|
||||||
<tr>
|
<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>
|
<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 %}
|
{% import 'image.html' as image %}
|
||||||
{% extends "layout.html" %}
|
{% extends "layout.html" %}
|
||||||
{% block body %}
|
{% 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">
|
<div class="discover random-books">
|
||||||
<h2 class="random-books">{{_('Discover (Random Books)')}}</h2>
|
<h2 class="random-books">{{_('Discover (Random Books)')}}</h2>
|
||||||
<div class="row display-flex">
|
<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 %}
|
{% from 'modal_dialogs.html' import restrict_modal, delete_book, filechooser_modal, delete_confirm_modal, change_confirm_modal %}
|
||||||
{% import 'image.html' as image %}
|
{% import 'image.html' as image %}
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="{{ g.user.locale }}">
|
<html lang="{{ current_user.locale }}">
|
||||||
<head>
|
<head>
|
||||||
<title>{{instance}} | {{title}}</title>
|
<title>{{instance}} | {{title}}</title>
|
||||||
<meta charset="utf-8">
|
<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="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>
|
<div class="plexBack"><a href="{{url_for('web.index')}}"></a></div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if g.user.is_authenticated or g.allow_anonymous %}
|
{% if current_user.is_authenticated or g.allow_anonymous %}
|
||||||
<form class="navbar-form navbar-left" role="search" action="{{url_for('search.simple_search')}}" method="GET">
|
<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">
|
<div class="form-group input-group input-group-sm">
|
||||||
<label for="query" class="sr-only">{{_('Search')}}</label>
|
<label for="query" class="sr-only">{{_('Search')}}</label>
|
||||||
<input type="text" class="form-control" id="query" name="query" placeholder="{{_('Search Library')}}" value="{{searchterm}}">
|
<input type="text" class="form-control" id="query" name="query" placeholder="{{_('Search Library')}}" value="{{searchterm}}">
|
||||||
|
@ -52,28 +53,28 @@
|
||||||
</form>
|
</form>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<div class="navbar-collapse collapse">
|
<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 ">
|
<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>
|
<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>
|
</ul>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<ul class="nav navbar-nav navbar-right" id="main-nav">
|
<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 %}
|
{% 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>
|
<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">
|
<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>
|
<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 g.user.is_authenticated %}
|
{% 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="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>
|
<li><a id="register" href="{{url_for('web.register')}}"><span class="glyphicon glyphicon-user"></span> {{_('Register')}}</a></li>
|
||||||
{% endif %}
|
{% 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>
|
<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 %}
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if g.user.role_upload() and g.allow_upload %}
|
{% if current_user.role_upload() and g.allow_upload %}
|
||||||
<li>
|
<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">
|
<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() }}">
|
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||||||
|
@ -84,20 +85,20 @@
|
||||||
</form>
|
</form>
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% 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>
|
<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 %}
|
{% 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>
|
<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 %}
|
{% endif %}
|
||||||
{% if g.current_theme == 0 %}
|
{% 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>
|
<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 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>
|
<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 %}
|
{% 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="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>
|
<li><a id="register" href="{{url_for('web.register')}}"><span class="glyphicon glyphicon-user"></span> {{_('Register')}}</a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -138,22 +139,22 @@
|
||||||
{%endif%}
|
{%endif%}
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<div class="row-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">
|
<div class="col-sm-2">
|
||||||
<nav class="navigation">
|
<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">
|
<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>
|
<li class="nav-head hidden-xs">{{_('Browse')}}</li>
|
||||||
{% for element in sidebar %}
|
{% 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>
|
<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 %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% 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>
|
<li class="nav-head hidden-xs public-shelves">{{_('Shelves')}}</li>
|
||||||
{% for shelf in g.shelves_access %}
|
{% 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>
|
<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 %}
|
{% 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_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>
|
<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 %}
|
{% endif %}
|
||||||
|
|
|
@ -149,7 +149,7 @@
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if not g.user.is_anonymous %}
|
{% if not current_user.is_anonymous %}
|
||||||
|
|
||||||
<div class="custom_columns">
|
<div class="custom_columns">
|
||||||
<p>
|
<p>
|
||||||
|
@ -159,7 +159,7 @@
|
||||||
<span>{{_('Read')}}</span>
|
<span>{{_('Read')}}</span>
|
||||||
</label>
|
</label>
|
||||||
</p>
|
</p>
|
||||||
{% if g.user.check_visibility(32768) %}
|
{% if current_user.check_visibility(32768) %}
|
||||||
<p>
|
<p>
|
||||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||||||
<label class="block-label">
|
<label class="block-label">
|
||||||
|
@ -182,8 +182,8 @@
|
||||||
|
|
||||||
<div class="more-stuff">
|
<div class="more-stuff">
|
||||||
|
|
||||||
{% if g.user.is_authenticated %}
|
{% if current_user.is_authenticated %}
|
||||||
{% if g.user.shelf.all() or g.shelves_access %}
|
{% if current_user.shelf.all() or g.shelves_access %}
|
||||||
<div id="shelf-actions" class="btn-toolbar" role="toolbar">
|
<div id="shelf-actions" class="btn-toolbar" role="toolbar">
|
||||||
<div class="btn-group" role="group" aria-label="Add to shelves">
|
<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">
|
<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>
|
</button>
|
||||||
<ul id="add-to-shelves" class="dropdown-menu" aria-labelledby="add-to-shelf">
|
<ul id="add-to-shelves" class="dropdown-menu" aria-labelledby="add-to-shelf">
|
||||||
{% for shelf in g.shelves_access %}
|
{% 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>
|
<li>
|
||||||
<a data-href="{{ url_for('shelf.add_to_shelf', book_id=entry.id, shelf_id=shelf.id) }}"
|
<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) }}"
|
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) }}"
|
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"
|
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"
|
class="glyphicon glyphicon-remove"
|
||||||
{% endif %}></span> {{shelf.name}}{% if shelf.is_public == 1 %} {{_('(Public)')}}{% endif %}
|
{% endif %}></span> {{shelf.name}}{% if shelf.is_public == 1 %} {{_('(Public)')}}{% endif %}
|
||||||
</a>
|
</a>
|
||||||
|
@ -343,7 +343,7 @@ window.calibre = {
|
||||||
bookUrl: "{{ url_for('static', filename=mp3file) }}/",
|
bookUrl: "{{ url_for('static', filename=mp3file) }}/",
|
||||||
bookmarkUrl: "{{ url_for('web.set_bookmark', book_id=mp3file, book_format=audioformat.upper()) }}",
|
bookmarkUrl: "{{ url_for('web.set_bookmark', book_id=mp3file, book_format=audioformat.upper()) }}",
|
||||||
bookmark: "{{ bookmark.bookmark_key if bookmark != None }}",
|
bookmark: "{{ bookmark.bookmark_key if bookmark != None }}",
|
||||||
useBookmarks: "{{ g.user.is_authenticated | tojson }}"
|
useBookmarks: "{{ current_user.is_authenticated | tojson }}"
|
||||||
|
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -37,8 +37,8 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
{% macro delete_book() %}
|
{% macro delete_book(allow) %}
|
||||||
{% if g.user.role_delete_books() %}
|
{% if allow %}
|
||||||
<div class="modal fade" id="deleteModal" role="dialog" aria-labelledby="metaDeleteLabel">
|
<div class="modal fade" id="deleteModal" role="dialog" aria-labelledby="metaDeleteLabel">
|
||||||
<div class="modal-dialog">
|
<div class="modal-dialog">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
|
|
|
@ -108,7 +108,7 @@
|
||||||
bookmarkUrl: "{{ url_for('web.set_bookmark', book_id=bookid, book_format='EPUB') }}",
|
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') }}",
|
bookUrl: "{{ url_for('web.serve_book', book_id=bookid, book_format='epub', anyname='file.epub') }}",
|
||||||
bookmark: "{{ bookmark.bookmark_key if bookmark != None }}",
|
bookmark: "{{ bookmark.bookmark_key if bookmark != None }}",
|
||||||
useBookmarks: "{{ g.user.is_authenticated | tojson }}"
|
useBookmarks: "{{ current_user.is_authenticated | tojson }}"
|
||||||
};
|
};
|
||||||
|
|
||||||
function selectTheme(id) {
|
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>
|
<span data-l10n-id="presentation_mode_label">Presentation Mode</span>
|
||||||
</button>
|
</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">
|
<button id="secondaryPrint" class="secondaryToolbarButton visibleMediumView" title="Print" tabindex="53" data-l10n-id="print">
|
||||||
<span data-l10n-id="print_label">Print</span>
|
<span data-l10n-id="print_label">Print</span>
|
||||||
</button>
|
</button>
|
||||||
|
@ -280,7 +280,7 @@ See https://github.com/adobe-type-tools/cmap-resources
|
||||||
<span data-l10n-id="presentation_mode_label">Presentation Mode</span>
|
<span data-l10n-id="presentation_mode_label">Presentation Mode</span>
|
||||||
</button>
|
</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">
|
<button id="print" class="toolbarButton hiddenMediumView" title="Print" tabindex="33" data-l10n-id="print">
|
||||||
<span data-l10n-id="print_label">Print</span>
|
<span data-l10n-id="print_label">Print</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -7,8 +7,8 @@
|
||||||
<p>{{_('Search Term:')}} {{adv_searchterm}}</p>
|
<p>{{_('Search Term:')}} {{adv_searchterm}}</p>
|
||||||
{% else %}
|
{% else %}
|
||||||
<h2>{{result_count}} {{_('Results for:')}} {{adv_searchterm}}</h2>
|
<h2>{{result_count}} {{_('Results for:')}} {{adv_searchterm}}</h2>
|
||||||
{% if g.user.is_authenticated %}
|
{% if current_user.is_authenticated %}
|
||||||
{% if g.user.shelf.all() or g.shelves_access %}
|
{% if current_user.shelf.all() or g.shelves_access %}
|
||||||
<div id="shelf-actions" class="btn-toolbar" role="toolbar">
|
<div id="shelf-actions" class="btn-toolbar" role="toolbar">
|
||||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||||||
<div class="btn-group" role="group" aria-label="Add to shelves">
|
<div class="btn-group" role="group" aria-label="Add to shelves">
|
||||||
|
@ -18,7 +18,7 @@
|
||||||
</button>
|
</button>
|
||||||
<ul id="add-to-shelves" class="dropdown-menu" aria-labelledby="add-to-shelf">
|
<ul id="add-to-shelves" class="dropdown-menu" aria-labelledby="add-to-shelf">
|
||||||
{% for shelf in g.shelves_access %}
|
{% 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>
|
<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 %}
|
{% endif %}
|
||||||
{%endfor%}
|
{%endfor%}
|
||||||
|
|
|
@ -230,18 +230,18 @@
|
||||||
|
|
||||||
{% block js %}
|
{% block js %}
|
||||||
<script>
|
<script>
|
||||||
var language = '{{ g.user.locale }}';
|
var language = '{{ current_user.locale }}';
|
||||||
</script>
|
</script>
|
||||||
<script src="{{ url_for('static', filename='js/libs/bootstrap-datepicker/bootstrap-datepicker.min.js') }}"></script>
|
<script src="{{ url_for('static', filename='js/libs/bootstrap-datepicker/bootstrap-datepicker.min.js') }}"></script>
|
||||||
{% if not g.user.locale == 'en' %}
|
{% if not current_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>
|
<script src="{{ url_for('static', filename='js/libs/bootstrap-datepicker/locales/bootstrap-datepicker.' + current_user.locale + '.min.js') }}" charset="UTF-8"></script>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<script src="{{ url_for('static', filename='js/libs/bootstrap-rating-input.min.js') }}"></script>
|
<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/libs/typeahead.bundle.js') }}"></script>
|
||||||
<script src="{{ url_for('static', filename='js/edit_books.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>
|
<script src="{{ url_for('static', filename='js/libs/bootstrap-select.min.js')}}"></script>
|
||||||
{% if not g.user.locale == 'en' %}
|
{% if not current_user.locale == 'en' %}
|
||||||
<script src="{{ url_for('static', filename='js/libs/bootstrap-select/defaults-' + g.user.locale + '.min.js') }}" charset="UTF-8"></script>
|
<script src="{{ url_for('static', filename='js/libs/bootstrap-select/defaults-' + current_user.locale + '.min.js') }}" charset="UTF-8"></script>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block header %}
|
{% block header %}
|
||||||
|
|
|
@ -4,11 +4,11 @@
|
||||||
<div class="discover">
|
<div class="discover">
|
||||||
<h2>{{title}}</h2>
|
<h2>{{title}}</h2>
|
||||||
<!--form method="post"--->
|
<!--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>
|
<a id="shelf_down" href="{{ url_for('shelf.show_simpleshelf', shelf_id=shelf.id) }}" class="btn btn-primary">{{ _('Download') }} </a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if g.user.is_authenticated %}
|
{% if current_user.is_authenticated %}
|
||||||
{% if (g.user.role_edit_shelfs() and shelf.is_public ) or not shelf.is_public %}
|
{% if (current_user.role_edit_shelfs() and shelf.is_public ) or not shelf.is_public %}
|
||||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
<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>
|
<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>
|
<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>
|
<label for="title">{{_('Title')}}</label>
|
||||||
<input type="text" class="form-control" name="title" id="title" value="{{ shelf.name if shelf.name != None }}">
|
<input type="text" class="form-control" name="title" id="title" value="{{ shelf.name if shelf.name != None }}">
|
||||||
</div>
|
</div>
|
||||||
{% if g.user.role_edit_shelfs() %}
|
{% if current_user.role_edit_shelfs() %}
|
||||||
<div class="checkbox">
|
<div class="checkbox">
|
||||||
<label>
|
<label>
|
||||||
<input type="checkbox" name="is_public" {% if shelf.is_public == 1 %}checked{% endif %}> {{_('Share with Everyone')}}
|
<input type="checkbox" name="is_public" {% if shelf.is_public == 1 %}checked{% endif %}> {{_('Share with Everyone')}}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="{{ g.user.locale }}">
|
<html lang="{{ current_user.locale }}">
|
||||||
<head>
|
<head>
|
||||||
<title>{{instance}} | {{title}}</title>
|
<title>{{instance}} | {{title}}</title>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
|
@ -58,7 +58,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="btn-group" role="group" aria-label="Download, send to eReader, reading">
|
<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 %}
|
{% if entry.Books.data|length %}
|
||||||
<div class="btn-group" role="group">
|
<div class="btn-group" role="group">
|
||||||
{% for format in entry.Books.data %}
|
{% for format in entry.Books.data %}
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
{% if g.user.role_admin() %}
|
{% if current_user.role_admin() %}
|
||||||
<h3>{{_('System Statistics')}}</h3>
|
<h3>{{_('System Statistics')}}</h3>
|
||||||
<table id="libs" class="table">
|
<table id="libs" class="table">
|
||||||
<thead>
|
<thead>
|
||||||
|
|
|
@ -5,10 +5,10 @@
|
||||||
{% block body %}
|
{% block body %}
|
||||||
<div class="discover">
|
<div class="discover">
|
||||||
<h2>{{_('Tasks')}}</h2>
|
<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>
|
<thead>
|
||||||
<tr>
|
<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>
|
<th data-halign="right" data-align="right" data-field="user" data-sortable="true">{{_('User')}}</th>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<th data-halign="right" data-align="right" data-field="taskMessage" data-sortable="true">{{_('Task')}}</th>
|
<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="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="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>
|
<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>
|
<th data-halign="right" data-align="right" data-formatter="TaskActions" data-switchable="false">{{_('Actions')}}</th>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<th data-field="id" data-visible="false"></th>
|
<th data-field="id" data-visible="false"></th>
|
||||||
|
@ -27,8 +27,8 @@
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block modal %}
|
{% block modal %}
|
||||||
{{ delete_book() }}
|
{{ delete_book(current_user.role_delete_books()) }}
|
||||||
{% if g.user.role_admin() %}
|
{% if current_user.role_admin() %}
|
||||||
<div class="modal fade" id="cancelTaskModal" role="dialog" aria-labelledby="metaCancelTaskLabel">
|
<div class="modal fade" id="cancelTaskModal" role="dialog" aria-labelledby="metaCancelTaskLabel">
|
||||||
<div class="modal-dialog">
|
<div class="modal-dialog">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<form role="form" method="POST" autocomplete="off">
|
<form role="form" method="POST" autocomplete="off">
|
||||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||||||
<div class="col-md-10 col-lg-8">
|
<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">
|
<div class="form-group required">
|
||||||
<label for="name">{{_('Username')}}</label>
|
<label for="name">{{_('Username')}}</label>
|
||||||
<input type="text" class="form-control" name="name" id="name" value="{{ content.name if content.name != None }}" autocomplete="off">
|
<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>
|
<label for="email">{{_('Email')}}</label>
|
||||||
<input type="email" class="form-control" name="email" id="email" value="{{ content.email if content.email != None }}" autocomplete="off">
|
<input type="email" class="form-control" name="email" id="email" value="{{ content.email if content.email != None }}" autocomplete="off">
|
||||||
</div>
|
</div>
|
||||||
{% if ( g.user and g.user.role_passwd() or g.user.role_admin() ) and not content.role_anonymous() %}
|
{% if ( current_user and current_user.role_passwd() or current_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_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>
|
<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 %}
|
{% endif %}
|
||||||
<div class="form-group">
|
<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 %}>
|
<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>
|
<label for="Show_detail_random">{{_('Show Random Books in Detail View')}}</label>
|
||||||
</div>
|
</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_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>
|
<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 %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-6">
|
<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() %}
|
{% if not content.role_anonymous() %}
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<input type="checkbox" name="admin_role" id="admin_role" {% if content.role_admin() %}checked{% endif %}>
|
<input type="checkbox" name="admin_role" id="admin_role" {% if content.role_admin() %}checked{% endif %}>
|
||||||
|
@ -143,7 +143,7 @@
|
||||||
{% if not profile %}
|
{% if not profile %}
|
||||||
<div class="btn btn-default" data-back="{{ url_for('admin.admin') }}" id="back">{{_('Cancel')}}</div>
|
<div class="btn btn-default" data-back="{{ url_for('admin.admin') }}" id="back">{{_('Cancel')}}</div>
|
||||||
{% endif %}
|
{% 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>
|
<div class="btn btn-danger" id="btndeluser" data-value="{{ content.id }}" data-remote="false" >{{_('Delete User')}}</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -125,7 +125,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<table id="user-table" class="table table-no-bordered table-striped"
|
<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>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th data-name="edit" data-buttontext="{{_('Edit User')}}" data-visible="{{visiblility.get('edit')}}" data-formatter="singleUserFormatter">{{_('Edit')}}</th>
|
<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-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-table/bootstrap-editable.min.js') }}"></script>
|
||||||
<script src="{{ url_for('static', filename='js/libs/bootstrap-select.min.js')}}"></script>
|
<script src="{{ url_for('static', filename='js/libs/bootstrap-select.min.js')}}"></script>
|
||||||
{% if not g.user.locale == 'en' %}
|
{% if not current_user.locale == 'en' %}
|
||||||
<script src="{{ url_for('static', filename='js/libs/bootstrap-select/defaults-' + g.user.locale + '.min.js') }}" charset="UTF-8"></script>
|
<script src="{{ url_for('static', filename='js/libs/bootstrap-select/defaults-' + current_user.locale + '.min.js') }}" charset="UTF-8"></script>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<script src="{{ url_for('static', filename='js/table.js') }}"></script>
|
<script src="{{ url_for('static', filename='js/table.js') }}"></script>
|
||||||
{% endblock %}
|
{% 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
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import base64
|
|
||||||
import binascii
|
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
|
|
||||||
from sqlalchemy.sql.expression import func
|
from sqlalchemy.sql.expression import func
|
||||||
from werkzeug.security import check_password_hash
|
from werkzeug.security import check_password_hash
|
||||||
from flask_login import login_required, login_user
|
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):
|
def login_required_if_no_ano(func):
|
||||||
@wraps(func)
|
@wraps(func)
|
||||||
|
@ -36,6 +36,51 @@ def login_required_if_no_ano(func):
|
||||||
|
|
||||||
return decorated_view
|
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):
|
def _fetch_user_by_name(username):
|
||||||
return ub.session.query(ub.User).filter(func.lower(ub.User.name) == username.lower()).first()
|
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
|
@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()
|
user = ub.session.query(ub.User).filter(ub.User.id == int(user_id)).first()
|
||||||
|
return user
|
||||||
|
|
||||||
|
|
||||||
@lm.request_loader
|
@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:
|
if config.config_allow_reverse_proxy_header_login:
|
||||||
rp_header_name = config.config_reverse_proxy_login_header_name
|
rp_header_name = config.config_reverse_proxy_login_header_name
|
||||||
if rp_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:
|
if rp_header_username:
|
||||||
user = _fetch_user_by_name(rp_header_username)
|
user = _fetch_user_by_name(rp_header_username)
|
||||||
if user:
|
if user:
|
||||||
login_user(user)
|
login_user(user)
|
||||||
return 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 lazy_gettext as N_
|
||||||
from flask_babel import get_locale
|
from flask_babel import get_locale
|
||||||
from flask_login import login_user, logout_user, login_required, current_user
|
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.exc import IntegrityError, InvalidRequestError, OperationalError
|
||||||
from sqlalchemy.sql.expression import text, func, false, not_, and_, or_
|
from sqlalchemy.sql.expression import text, func, false, not_, and_, or_
|
||||||
from sqlalchemy.orm.attributes import flag_modified
|
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 .render_template import render_title_template
|
||||||
from .kobo_sync_status import change_archived_books
|
from .kobo_sync_status import change_archived_books
|
||||||
from . import limiter
|
from . import limiter
|
||||||
from flask_limiter import RateLimitExceeded
|
from .services.worker import WorkerThread
|
||||||
|
from .tasks_status import render_task_status
|
||||||
|
|
||||||
|
|
||||||
feature_support = {
|
feature_support = {
|
||||||
|
@ -394,7 +396,7 @@ def render_books_list(data, sort_param, book_id, page):
|
||||||
elif data == "archived":
|
elif data == "archived":
|
||||||
return render_archived_books(page, order)
|
return render_archived_books(page, order)
|
||||||
elif data == "search":
|
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))
|
offset = int(int(config.config_books_per_page) * (page - 1))
|
||||||
return render_search_results(term, offset, order, config.config_books_per_page)
|
return render_search_results(term, offset, order, config.config_books_per_page)
|
||||||
elif data == "advsearch":
|
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-Login>=0.3.2,<0.6.3
|
||||||
Flask-Principal>=0.3.2,<0.5.1
|
Flask-Principal>=0.3.2,<0.5.1
|
||||||
backports_abc>=0.4
|
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
|
iso-639>=0.4.5,<0.5.0
|
||||||
PyPDF>=3.0.0,<3.3.0
|
PyPDF>=3.0.0,<3.3.0
|
||||||
pytz>=2016.10
|
pytz>=2016.10
|
||||||
|
|
Loading…
Reference in New Issue
Block a user