Commit 7aabfc57 authored by Ozzieisaacs's avatar Ozzieisaacs

Merge branch 'master' into Develop

# Conflicts:
#	cps/admin.py
#	cps/config_sql.py
#	cps/templates/config_edit.html
#	cps/web.py
parents 22dde5d1 56505457
...@@ -164,7 +164,6 @@ def view_configuration(): ...@@ -164,7 +164,6 @@ def view_configuration():
@login_required @login_required
@admin_required @admin_required
def update_view_configuration(): def update_view_configuration():
reboot_required = False
to_save = request.form.to_dict() to_save = request.form.to_dict()
_config_string = lambda x: config.set_from_dictionary(to_save, x, lambda y: y.strip() if y else y) _config_string = lambda x: config.set_from_dictionary(to_save, x, lambda y: y.strip() if y else y)
...@@ -172,7 +171,8 @@ def update_view_configuration(): ...@@ -172,7 +171,8 @@ def update_view_configuration():
_config_string("config_calibre_web_title") _config_string("config_calibre_web_title")
_config_string("config_columns_to_ignore") _config_string("config_columns_to_ignore")
reboot_required |= _config_string("config_title_regex") if _config_string("config_title_regex"):
calibre_db.update_title_sort(config)
_config_int("config_read_column") _config_int("config_read_column")
_config_int("config_theme") _config_int("config_theme")
...@@ -191,10 +191,6 @@ def update_view_configuration(): ...@@ -191,10 +191,6 @@ def update_view_configuration():
config.save() config.save()
flash(_(u"Calibre-Web configuration updated"), category="success") flash(_(u"Calibre-Web configuration updated"), category="success")
before_request() before_request()
if reboot_required:
db.dispose()
ub.dispose()
web_server.stop(True)
return view_configuration() return view_configuration()
...@@ -614,12 +610,24 @@ def _configuration_ldap_helper(to_save, gdriveError): ...@@ -614,12 +610,24 @@ def _configuration_ldap_helper(to_save, gdriveError):
return reboot_required, _configuration_result(_('LDAP User Object Filter Has Unmatched Parenthesis'), return reboot_required, _configuration_result(_('LDAP User Object Filter Has Unmatched Parenthesis'),
gdriveError) gdriveError)
if to_save["ldap_import_user_filter"] == '0':
config.config_ldap_member_user_object = ""
else:
if config.config_ldap_member_user_object.count("%s") != 1:
return reboot_required, \
_configuration_result(_('LDAP Member User Filter needs to Have One "%s" Format Identifier'),
gdriveError)
if config.config_ldap_member_user_object.count("(") != config.config_ldap_member_user_object.count(")"):
return reboot_required, _configuration_result(_('LDAP Member User Filter Has Unmatched Parenthesis'),
gdriveError)
if config.config_ldap_cacert_path or config.config_ldap_cert_path or config.config_ldap_key_path: if config.config_ldap_cacert_path or config.config_ldap_cert_path or config.config_ldap_key_path:
if not (os.path.isfile(config.config_ldap_cacert_path) and if not (os.path.isfile(config.config_ldap_cacert_path) and
os.path.isfile(config.config_ldap_cert_path) and os.path.isfile(config.config_ldap_cert_path) and
os.path.isfile(config.config_ldap_key_path)): os.path.isfile(config.config_ldap_key_path)):
return reboot_required, \ return reboot_required, \
_configuration_result(_('LDAP CACertificate, Certificate or Key Location is not Valid, Please Enter Correct Path'), _configuration_result(_('LDAP CACertificate, Certificate or Key Location is not Valid, '
'Please Enter Correct Path'),
gdriveError) gdriveError)
return reboot_required, None return reboot_required, None
......
...@@ -113,6 +113,7 @@ class _Settings(_Base): ...@@ -113,6 +113,7 @@ class _Settings(_Base):
config_ldap_key_path = Column(String, default="") config_ldap_key_path = Column(String, default="")
config_ldap_dn = Column(String, default='dc=example,dc=org') config_ldap_dn = Column(String, default='dc=example,dc=org')
config_ldap_user_object = Column(String, default='uid=%s') config_ldap_user_object = Column(String, default='uid=%s')
config_ldap_member_user_object = Column(String, default='') #
config_ldap_openldap = Column(Boolean, default=True) config_ldap_openldap = Column(Boolean, default=True)
config_ldap_group_object_filter = Column(String, default='(&(objectclass=posixGroup)(cn=%s))') config_ldap_group_object_filter = Column(String, default='(&(objectclass=posixGroup)(cn=%s))')
config_ldap_group_members_field = Column(String, default='memberUid') config_ldap_group_members_field = Column(String, default='memberUid')
......
...@@ -935,8 +935,6 @@ def convert_bookformat(book_id): ...@@ -935,8 +935,6 @@ def convert_bookformat(book_id):
@edit_required @edit_required
def edit_list_book(param): def edit_list_book(param):
vals = request.form.to_dict() vals = request.form.to_dict()
# calibre_db.update_title_sort(config)
#calibre_db.session.connection().connection.connection.create_function('uuid4', 0, lambda: str(uuid4()))
book = calibre_db.get_book(vals['pk']) book = calibre_db.get_book(vals['pk'])
if param =='series_index': if param =='series_index':
edit_book_series_index(vals['value'], book) edit_book_series_index(vals['value'], book)
......
...@@ -862,6 +862,7 @@ def HandleUserRequest(dummy=None): ...@@ -862,6 +862,7 @@ def HandleUserRequest(dummy=None):
@kobo.route("/v1/products/<dummy>/recommendations", methods=["GET", "POST"]) @kobo.route("/v1/products/<dummy>/recommendations", methods=["GET", "POST"])
@kobo.route("/v1/products/<dummy>/nextread", methods=["GET", "POST"]) @kobo.route("/v1/products/<dummy>/nextread", methods=["GET", "POST"])
@kobo.route("/v1/products/<dummy>/reviews", methods=["GET", "POST"]) @kobo.route("/v1/products/<dummy>/reviews", methods=["GET", "POST"])
@kobo.route("/v1/products/books/external/<dummy>", methods=["GET", "POST"])
@kobo.route("/v1/products/books/series/<dummy>", methods=["GET", "POST"]) @kobo.route("/v1/products/books/series/<dummy>", methods=["GET", "POST"])
@kobo.route("/v1/products/books/<dummy>", methods=["GET", "POST"]) @kobo.route("/v1/products/books/<dummy>", methods=["GET", "POST"])
@kobo.route("/v1/products/dailydeal", methods=["GET", "POST"]) @kobo.route("/v1/products/dailydeal", methods=["GET", "POST"])
......
...@@ -25,7 +25,7 @@ import sys ...@@ -25,7 +25,7 @@ import sys
import datetime import datetime
from functools import wraps from functools import wraps
from flask import Blueprint, request, render_template, Response, g, make_response from flask import Blueprint, request, render_template, Response, g, make_response, abort
from flask_login import current_user from flask_login import current_user
from sqlalchemy.sql.expression import func, text, or_, and_ from sqlalchemy.sql.expression import func, text, or_, and_
from werkzeug.security import check_password_hash from werkzeug.security import check_password_hash
...@@ -33,7 +33,7 @@ from werkzeug.security import check_password_hash ...@@ -33,7 +33,7 @@ from werkzeug.security import check_password_hash
from . import constants, logger, config, db, calibre_db, ub, services, get_locale, isoLanguages from . import constants, logger, config, db, calibre_db, ub, services, get_locale, isoLanguages
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, download_required from .web import render_read_books, download_required, load_user_from_request
from flask_babel import gettext as _ from flask_babel import gettext as _
from babel import Locale as LC from babel import Locale as LC
from babel.core import UnknownLocaleError from babel.core import UnknownLocaleError
...@@ -383,8 +383,13 @@ def feed_shelf(book_id): ...@@ -383,8 +383,13 @@ 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
@download_required
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
# workaround, loading the user from the request and checking it's 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)
if "Kobo" in request.headers.get('User-Agent'): if "Kobo" in request.headers.get('User-Agent'):
client = "kobo" client = "kobo"
else: else:
...@@ -418,7 +423,10 @@ def feed_search(term): ...@@ -418,7 +423,10 @@ def feed_search(term):
def check_auth(username, password): def check_auth(username, password):
if sys.version_info.major == 3: if sys.version_info.major == 3:
try:
username = username.encode('windows-1252') username = username.encode('windows-1252')
except UnicodeEncodeError:
username = username.encode('utf-8')
user = ub.session.query(ub.User).filter(func.lower(ub.User.nickname) == user = ub.session.query(ub.User).filter(func.lower(ub.User.nickname) ==
username.decode('utf-8').lower()).first() username.decode('utf-8').lower()).first()
return bool(user and check_password_hash(str(user.password), password)) return bool(user and check_password_hash(str(user.password), password))
......
...@@ -117,7 +117,7 @@ def bind_user(username, password): ...@@ -117,7 +117,7 @@ def bind_user(username, password):
return None, error return None, error
except LDAPException as ex: except LDAPException as ex:
if ex.message == 'Invalid credentials': if ex.message == 'Invalid credentials':
error = ("LDAP admin login failed") error = "LDAP admin login failed"
return None, error return None, error
if ex.message == "Can't contact LDAP server": if ex.message == "Can't contact LDAP server":
# log.warning('LDAP Server down: %s', ex) # log.warning('LDAP Server down: %s', ex)
......
...@@ -23,16 +23,12 @@ ...@@ -23,16 +23,12 @@
<h3>{{_("In Library")}}</h3> <h3>{{_("In Library")}}</h3>
{% endif %} {% endif %}
<div class="filterheader hidden-xs hidden-sm"> <div class="filterheader hidden-xs hidden-sm">
<a id="new" class="btn btn-primary" href="{{url_for('web.books_list', data='author', book_id=id, sort_param='new')}}"><span class="glyphicon glyphicon-book"></span> <span class="glyphicon glyphicon-calendar"></span><span class="glyphicon glyphicon-sort-by-order"></span></a> <a id="new" data-toggle="tooltip" title="{{_('Sort according to book date, newest first')}}" class="btn btn-primary" href="{{url_for('web.books_list', data='author', book_id=id, sort_param='new')}}"><span class="glyphicon glyphicon-book"></span> <span class="glyphicon glyphicon-calendar"></span><span class="glyphicon glyphicon-sort-by-order"></span></a>
<a id="old" class="btn btn-primary" href="{{url_for('web.books_list', data='author', book_id=id, sort_param='old')}}"><span class="glyphicon glyphicon-book"></span> <span class="glyphicon glyphicon-calendar"></span><span class="glyphicon glyphicon-sort-by-order-alt"></span></a> <a id="old" data-toggle="tooltip" title="{{_('Sort according to book date, oldest first')}}" class="btn btn-primary" href="{{url_for('web.books_list', data='author', book_id=id, sort_param='old')}}"><span class="glyphicon glyphicon-book"></span> <span class="glyphicon glyphicon-calendar"></span><span class="glyphicon glyphicon-sort-by-order-alt"></span></a>
<a id="asc" class="btn btn-primary" href="{{url_for('web.books_list', data='author', book_id=id, sort_param='abc')}}"><span class="glyphicon glyphicon-font"></span><span class="glyphicon glyphicon-sort-by-alphabet"></span></a> <a id="asc" data-toggle="tooltip" title="{{_('Sort title in alphabetical order')}}" class="btn btn-primary" href="{{url_for('web.books_list', data='author', book_id=id, sort_param='abc')}}"><span class="glyphicon glyphicon-font"></span><span class="glyphicon glyphicon-sort-by-alphabet"></span></a>
<a id="desc" class="btn btn-primary" href="{{url_for('web.books_list', data='author', book_id=id, sort_param='zyx')}}"><span class="glyphicon glyphicon-font"></span><span class="glyphicon glyphicon-sort-by-alphabet-alt"></span></a> <a id="desc" data-toggle="tooltip" title="{{_('Sort title in reverse alphabetical order')}}" class="btn btn-primary" href="{{url_for('web.books_list', data='author', book_id=id, sort_param='zyx')}}"><span class="glyphicon glyphicon-font"></span><span class="glyphicon glyphicon-sort-by-alphabet-alt"></span></a>
<a id="pub_new" class="btn btn-primary" href="{{url_for('web.books_list', data='author', book_id=id, sort_param='pubnew')}}"><span class="glyphicon glyphicon-calendar"></span><span class="glyphicon glyphicon-sort-by-order"></span></a> <a id="pub_new" data-toggle="tooltip" title="{{_('Sort according to publishing date, newest first')}}" class="btn btn-primary" href="{{url_for('web.books_list', data='author', book_id=id, sort_param='pubnew')}}"><span class="glyphicon glyphicon-calendar"></span><span class="glyphicon glyphicon-sort-by-order"></span></a>
<a id="pub_old" class="btn btn-primary" href="{{url_for('web.books_list', data='author', book_id=id, sort_param='pubold')}}"><span class="glyphicon glyphicon-calendar"></span><span class="glyphicon glyphicon-sort-by-order-alt"></span></a> <a id="pub_old" data-toggle="tooltip" title="{{_('Sort according to publishing date, oldest first')}}" class="btn btn-primary" href="{{url_for('web.books_list', data='author', book_id=id, sort_param='pubold')}}"><span class="glyphicon glyphicon-calendar"></span><span class="glyphicon glyphicon-sort-by-order-alt"></span></a>
<!--div class="btn-group character" role="group">
<a id="no_shelf" class="btn btn-primary" href="{{url_for('web.books_list', data='author', book_id=id, sort_param='pubold')}}"><span class="glyphicon glyphicon-list"></span><b>?</b></a>
<div id="all" class="btn btn-primary">{{_('All')}}</div>
</div-->
</div> </div>
<div class="row display-flex"> <div class="row display-flex">
{% if entries[0] %} {% if entries[0] %}
......
...@@ -60,10 +60,10 @@ ...@@ -60,10 +60,10 @@
<label for="config_google_drive_watch_changes_response">{{_('Metadata Watch Channel ID')}}</label> <label for="config_google_drive_watch_changes_response">{{_('Metadata Watch Channel ID')}}</label>
<div class="form-group input-group required"> <div class="form-group input-group required">
<input type="text" class="form-control" name="config_google_drive_watch_changes_response" id="config_google_drive_watch_changes_response" value="{{ config.config_google_drive_watch_changes_response['id'] }} expires on {{ config.config_google_drive_watch_changes_response['expiration'] | strftime }}" autocomplete="off" disabled=""> <input type="text" class="form-control" name="config_google_drive_watch_changes_response" id="config_google_drive_watch_changes_response" value="{{ config.config_google_drive_watch_changes_response['id'] }} expires on {{ config.config_google_drive_watch_changes_response['expiration'] | strftime }}" autocomplete="off" disabled="">
<span class="input-group-btn"><a href="{{ url_for('gdrive.revoke_watch_gdrive') }}" class="btn btn-primary">{{_('Revoke')}}</a></span> <span class="input-group-btn"><a href="{{ url_for('gdrive.revoke_watch_gdrive') }}" id="watch_revoke" class="btn btn-primary">{{_('Revoke')}}</a></span>
</div> </div>
{% else %} {% else %}
<a href="{{ url_for('gdrive.watch_gdrive') }}" class="btn btn-primary">Enable watch of metadata.db</a> <a href="{{ url_for('gdrive.watch_gdrive') }}" id="enable_gdrive_watch" class="btn btn-primary">Enable watch of metadata.db</a>
{% endif %} {% endif %}
{% endif %} {% endif %}
{% endif %} {% endif %}
...@@ -331,6 +331,20 @@ ...@@ -331,6 +331,20 @@
<label for="config_ldap_group_members_field">{{_('LDAP Group Members Field')}}</label> <label for="config_ldap_group_members_field">{{_('LDAP Group Members Field')}}</label>
<input type="text" class="form-control" id="config_ldap_group_members_field" name="config_ldap_group_members_field" value="{% if config.config_ldap_group_members_field != None %}{{ config.config_ldap_group_members_field }}{% endif %}" autocomplete="off"> <input type="text" class="form-control" id="config_ldap_group_members_field" name="config_ldap_group_members_field" value="{% if config.config_ldap_group_members_field != None %}{{ config.config_ldap_group_members_field }}{% endif %}" autocomplete="off">
</div> </div>
<div class="form-group">
<label for="ldap_import_user_filter">{{_('LDAP Authentication')}}</label>
<select name="ldap_import_user_filter" id="ldap_import_user_filter" class="form-control" data-control="ldap_member_user_object">
<option value="0" {% if config.config_ldap_member_user_object == "" %}selected{% endif %}>{{ _('Autodetect') }}</option>
<option value="1" {% if config.config_ldap_member_user_object %}selected{% endif %}>{{ _('Custom Filter') }}</option>
</select>
</div>
<div data-related="ldap_member_user_object-1">
<div class="form-group">
<label for="config_ldap_member_user_object">{{_('LDAP Member User Filter')}}</label>
<input type="text" class="form-control" id="config_ldap_member_user_object" name="config_ldap_member_user_object" value="{% if config.config_ldap_member_user_object != None %}{{ config.config_ldap_member_user_object }}{% endif %}" autocomplete="off">
</div>
</div>
</div> </div>
{% endif %} {% endif %}
{% if feature_support['oauth'] %} {% if feature_support['oauth'] %}
......
...@@ -62,15 +62,18 @@ ...@@ -62,15 +62,18 @@
<div class="discover load-more"> <div class="discover load-more">
<h2 class="{{title}}">{{_(title)}}</h2> <h2 class="{{title}}">{{_(title)}}</h2>
<div class="filterheader hidden-xs hidden-sm"> <div class="filterheader hidden-xs hidden-sm">
<a data-toggle="tooltip" id="new" class="btn btn-primary" href="{{url_for('web.books_list', data=page, book_id=id, sort_param='new')}}"><span class="glyphicon glyphicon-book"></span> <span class="glyphicon glyphicon-calendar"></span><span class="glyphicon glyphicon-sort-by-order"></span></a> <a data-toggle="tooltip" title="{{_('Sort according to book date, newest first')}}" id="new" class="btn btn-primary" href="{{url_for('web.books_list', data=page, book_id=id, sort_param='new')}}"><span class="glyphicon glyphicon-book"></span> <span class="glyphicon glyphicon-calendar"></span><span class="glyphicon glyphicon-sort-by-order"></span></a>
<a id="old" class="btn btn-primary" href="{{url_for('web.books_list', data=page, book_id=id, sort_param='old')}}"><span class="glyphicon glyphicon-book"></span> <span class="glyphicon glyphicon-calendar"></span><span class="glyphicon glyphicon-sort-by-order-alt"></span></a> <a data-toggle="tooltip" title="{{_('Sort according to book date, oldest first')}}" id="old" class="btn btn-primary" href="{{url_for('web.books_list', data=page, book_id=id, sort_param='old')}}"><span class="glyphicon glyphicon-book"></span> <span class="glyphicon glyphicon-calendar"></span><span class="glyphicon glyphicon-sort-by-order-alt"></span></a>
<a id="asc" class="btn btn-primary" href="{{url_for('web.books_list', data=page, book_id=id, sort_param='abc')}}"><span class="glyphicon glyphicon-font"></span><span class="glyphicon glyphicon-sort-by-alphabet"></span></a> <a data-toggle="tooltip" title="{{_('Sort title in alphabetical order')}}" id="asc" class="btn btn-primary" href="{{url_for('web.books_list', data=page, book_id=id, sort_param='abc')}}"><span class="glyphicon glyphicon-font"></span><span class="glyphicon glyphicon-sort-by-alphabet"></span></a>
<a id="desc" class="btn btn-primary" href="{{url_for('web.books_list', data=page, book_id=id, sort_param='zyx')}}"><span class="glyphicon glyphicon-font"></span><span class="glyphicon glyphicon-sort-by-alphabet-alt"></span></a> <a data-toggle="tooltip" title="{{_('Sort title in reverse alphabetical order')}}" id="desc" class="btn btn-primary" href="{{url_for('web.books_list', data=page, book_id=id, sort_param='zyx')}}"><span class="glyphicon glyphicon-font"></span><span class="glyphicon glyphicon-sort-by-alphabet-alt"></span></a>
<a id="pub_new" class="btn btn-primary" href="{{url_for('web.books_list', data=page, book_id=id, sort_param='pubnew')}}"><span class="glyphicon glyphicon-calendar"></span><span class="glyphicon glyphicon-sort-by-order"></span></a> <a data-toggle="tooltip" title="{{_('Sort authors in alphabetical order')}}" id="auth_az" class="btn btn-primary" href="{{url_for('web.books_list', data=page, book_id=id, sort_param='authaz')}}"><span class="glyphicon glyphicon-user"></span><span class="glyphicon glyphicon-sort-by-alphabet"></span></a>
<a id="pub_old" class="btn btn-primary" href="{{url_for('web.books_list', data=page, book_id=id, sort_param='pubold')}}"><span class="glyphicon glyphicon-calendar"></span><span class="glyphicon glyphicon-sort-by-order-alt"></span></a> <a data-toggle="tooltip" title="{{_('Sort authors in reverse alphabetical order')}}" id="auth_za" class="btn btn-primary" href="{{url_for('web.books_list', data=page, book_id=id, sort_param='authza')}}"><span class="glyphicon glyphicon-user"></span><span class="glyphicon glyphicon-sort-by-alphabet-alt"></span></a>
<!--div class="btn-group character"> <a data-toggle="tooltip" title="{{_('Sort according to publishing date, newest first')}}" id="pub_new" class="btn btn-primary" href="{{url_for('web.books_list', data=page, book_id=id, sort_param='pubnew')}}"><span class="glyphicon glyphicon-calendar"></span><span class="glyphicon glyphicon-sort-by-order"></span></a>
<a id="no_shelf" class="btn btn-primary" href="{{url_for('web.books_list', data=page, book_id=id, sort_param='pubold')}}"><span class="glyphicon glyphicon-list"></span> <b>{{_('Group by series')}}</b></a> <a data-toggle="tooltip" title="{{_('Sort according to publishing date, oldest first')}}" id="pub_old" class="btn btn-primary" href="{{url_for('web.books_list', data=page, book_id=id, sort_param='pubold')}}"><span class="glyphicon glyphicon-calendar"></span><span class="glyphicon glyphicon-sort-by-order-alt"></span></a>
</div--> {% if page == 'series' %}
<a data-toggle="tooltip" title="{{_('Sort ascending according to series index')}}" id="series_asc" class="btn btn-primary" href="{{url_for('web.books_list', data=page, book_id=id, sort_param='seriesasc')}}"><span class="glyphicon glyphicon-sort-by-order"></span></a>
<a data-toggle="tooltip" title="{{_('Sort descending according to series index')}}" id="series_desc" class="btn btn-primary" href="{{url_for('web.books_list', data=page, book_id=id, sort_param='seriesdesc')}}"><span class="glyphicon glyphicon-sort-by-order-alt"></span></a>
{% endif %}
</div> </div>
<div class="row display-flex"> <div class="row display-flex">
......
...@@ -26,12 +26,14 @@ ...@@ -26,12 +26,14 @@
{% endif %} {% endif %}
{% endif %} {% endif %}
<div class="filterheader hidden-xs hidden-sm"><!-- ToDo: Implement filter for search results --> <div class="filterheader hidden-xs hidden-sm"><!-- ToDo: Implement filter for search results -->
<a id="new" class="btn btn-primary" href="{{url_for('web.books_list', data=page, sort_param='new', query=query)}}"><span class="glyphicon glyphicon-sort-by-order"></span></a> <a id="new" data-toggle="tooltip" title="{{_('Sort according to book date, newest first')}}" class="btn btn-primary" href="{{url_for('web.books_list', data=page, sort_param='new', query=query)}}"><span class="glyphicon glyphicon-sort-by-order"></span></a>
<a id="old" class="btn btn-primary" href="{{url_for('web.books_list', data=page, sort_param='old', query=query)}}"><span class="glyphicon glyphicon-sort-by-order-alt"></span></a> <a id="old" data-toggle="tooltip" title="{{_('Sort according to book date, oldest first')}}" class="btn btn-primary" href="{{url_for('web.books_list', data=page, sort_param='old', query=query)}}"><span class="glyphicon glyphicon-sort-by-order-alt"></span></a>
<a id="asc" class="btn btn-primary" href="{{url_for('web.books_list', data=page, sort_param='abc', query=query)}}"><span class="glyphicon glyphicon-font"></span><span class="glyphicon glyphicon-sort-by-alphabet"></span></a> <a id="asc" data-toggle="tooltip" title="{{_('Sort title in alphabetical order')}}" class="btn btn-primary" href="{{url_for('web.books_list', data=page, sort_param='abc', query=query)}}"><span class="glyphicon glyphicon-font"></span><span class="glyphicon glyphicon-sort-by-alphabet"></span></a>
<a id="desc" class="btn btn-primary" href="{{url_for('web.books_list', data=page, sort_param='zyx', query=query)}}"><span class="glyphicon glyphicon-font"></span><span class="glyphicon glyphicon-sort-by-alphabet-alt"></span></a> <a id="desc" data-toggle="tooltip" title="{{_('Sort title in reverse alphabetical order')}}" class="btn btn-primary" href="{{url_for('web.books_list', data=page, sort_param='zyx', query=query)}}"><span class="glyphicon glyphicon-font"></span><span class="glyphicon glyphicon-sort-by-alphabet-alt"></span></a>
<a id="pub_new" class="btn btn-primary" href="{{url_for('web.books_list', data=page, sort_param='pubnew', query=query)}}"><span class="glyphicon glyphicon-calendar"></span><span class="glyphicon glyphicon-sort-by-order"></span></a> <a id="auth_az" data-toggle="tooltip" title="{{_('Sort authors in alphabetical order')}}" class="btn btn-primary" href="{{url_for('web.books_list', data=page, sort_param='authaz', query=query)}}"><span class="glyphicon glyphicon-user"></span><span class="glyphicon glyphicon-sort-by-alphabet"></span></a>
<a id="pub_old" class="btn btn-primary" href="{{url_for('web.books_list', data=page, sort_param='pubold', query=query)}}"><span class="glyphicon glyphicon-calendar"></span><span class="glyphicon glyphicon-sort-by-order-alt"></span></a> <a id="auth_za" data-toggle="tooltip" title="{{_('Sort authors in reverse alphabetical order')}}" class="btn btn-primary" href="{{url_for('web.books_list', data=page, sort_param='authza', query=query)}}"><span class="glyphicon glyphicon-user"></span><span class="glyphicon glyphicon-sort-by-alphabet-alt"></span></a>
<a id="pub_new" data-toggle="tooltip" title="{{_('Sort according to publishing date, newest first')}}" class="btn btn-primary" href="{{url_for('web.books_list', data=page, sort_param='pubnew', query=query)}}"><span class="glyphicon glyphicon-calendar"></span><span class="glyphicon glyphicon-sort-by-order"></span></a>
<a id="pub_old" data-toggle="tooltip" title="{{_('Sort according to publishing date, oldest first')}}" class="btn btn-primary" href="{{url_for('web.books_list', data=page, sort_param='pubold', query=query)}}"><span class="glyphicon glyphicon-calendar"></span><span class="glyphicon glyphicon-sort-by-order-alt"></span></a>
</div> </div>
{% endif %} {% endif %}
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
...@@ -323,31 +323,34 @@ def import_ldap_users(): ...@@ -323,31 +323,34 @@ def import_ldap_users():
showtext['text'] = _(u'Error: No user returned in response of LDAP server') showtext['text'] = _(u'Error: No user returned in response of LDAP server')
return json.dumps(showtext) return json.dumps(showtext)
imported = 0
for username in new_users: for username in new_users:
user = username.decode('utf-8') user = username.decode('utf-8')
if '=' in user: if '=' in user:
match = re.search("([a-zA-Z0-9-]+)=%s", config.config_ldap_user_object, re.IGNORECASE | re.UNICODE) # if member object field is empty take user object as filter
if match: try:
match_filter = match.group(1) if config.config_ldap_member_user_object:
match = re.search(match_filter + "=([\d\s\w-]+)", user, re.IGNORECASE | re.UNICODE) user_identifier = extract_user_identifier(user, config.config_ldap_member_user_object)
if match:
user = match.group(1)
else: else:
log.warning("Could Not Parse LDAP User: %s", user) user_identifier = extract_user_identifier(user, config.config_ldap_user_object)
except Exception as e:
log.warning(e)
continue continue
else: else:
log.warning("Could Not Parse LDAP User: %s", user) user_identifier = user
continue
if ub.session.query(ub.User).filter(ub.User.nickname == user.lower()).first(): if ub.session.query(ub.User).filter(ub.User.nickname == user_identifier.lower()).first():
log.warning("LDAP User: %s Already in Database", user) log.warning("LDAP User: %s Already in Database", user_identifier)
continue continue
user_data = services.ldap.get_object_details(user=user, user_data = services.ldap.get_object_details(user=user_identifier,
group=None, group=None,
query_filter=None, query_filter=None,
dn_only=False) dn_only=False)
if user_data: if user_data:
content = ub.User() content = ub.User()
content.nickname = user # user_login_field = extract_dynamic_field_from_filter(user, config.config_ldap_user_object)
content.nickname = user_identifier # user_data[user_login_field][0].decode('utf-8')
content.password = '' # dummy password which will be replaced by ldap one content.password = '' # dummy password which will be replaced by ldap one
if 'mail' in user_data: if 'mail' in user_data:
content.email = user_data['mail'][0].decode('utf-8') content.email = user_data['mail'][0].decode('utf-8')
...@@ -365,6 +368,7 @@ def import_ldap_users(): ...@@ -365,6 +368,7 @@ def import_ldap_users():
ub.session.add(content) ub.session.add(content)
try: try:
ub.session.commit() ub.session.commit()
imported +=1
except Exception as e: except Exception as e:
log.warning("Failed to create LDAP user: %s - %s", user, e) log.warning("Failed to create LDAP user: %s - %s", user, e)
ub.session.rollback() ub.session.rollback()
...@@ -373,10 +377,28 @@ def import_ldap_users(): ...@@ -373,10 +377,28 @@ def import_ldap_users():
log.warning("LDAP User: %s Not Found", user) log.warning("LDAP User: %s Not Found", user)
showtext['text'] = _(u'At Least One LDAP User Not Found in Database') showtext['text'] = _(u'At Least One LDAP User Not Found in Database')
if not showtext: if not showtext:
showtext['text'] = _(u'User Successfully Imported') showtext['text'] = _(u'{} User Successfully Imported'.format(imported))
return json.dumps(showtext) return json.dumps(showtext)
def extract_user_data_from_field(user, field):
match = re.search(field + "=([\d\s\w-]+)", user, re.IGNORECASE | re.UNICODE)
if match:
return match.group(1)
else:
raise Exception("Could Not Parse LDAP User: %s", user)
# CN=Firstname LastName,OU=Laba,OU=...,DC=...,DC=...
# CN=user displayname,OU=ouname1,OU=ouname2,OU=ouname3,DC=domain,DC=domain
def extract_user_identifier(user, filter):
match = re.search("([a-zA-Z0-9-]+)=%s", filter, re.IGNORECASE | re.UNICODE)
if match:
dynamic_field = match.group(1)
else:
raise Exception("Could Not Parse LDAP User: %s", user)
return extract_user_data_from_field(user, dynamic_field)
# ################################### data provider functions ######################################################### # ################################### data provider functions #########################################################
...@@ -631,6 +653,10 @@ def render_books_list(data, sort, book_id, page): ...@@ -631,6 +653,10 @@ def render_books_list(data, sort, book_id, page):
order = [db.Books.author_sort.asc()] order = [db.Books.author_sort.asc()]
if sort == 'authza': if sort == 'authza':
order = [db.Books.author_sort.desc()] order = [db.Books.author_sort.desc()]
if sort == 'seriesasc':
order = [db.Books.series_index.asc()]
if sort == 'seriesdesc':
order = [db.Books.series_index.desc()]
if data == "rated": if data == "rated":
if current_user.check_visibility(constants.SIDEBAR_BEST_RATED): if current_user.check_visibility(constants.SIDEBAR_BEST_RATED):
...@@ -813,7 +839,7 @@ def render_ratings_books(page, book_id, order): ...@@ -813,7 +839,7 @@ def render_ratings_books(page, book_id, order):
entries, random, pagination = calibre_db.fill_indexpage(page, 0, entries, random, pagination = calibre_db.fill_indexpage(page, 0,
db.Books, db.Books,
db.Books.ratings.any(db.Ratings.id == book_id), db.Books.ratings.any(db.Ratings.id == book_id),
[db.Books.timestamp.desc(), order[0]]) [order[0]])
if name and name.rating <= 10: if name and name.rating <= 10:
return render_title_template('index.html', random=random, pagination=pagination, entries=entries, id=book_id, return render_title_template('index.html', random=random, pagination=pagination, entries=entries, id=book_id,
title=_(u"Rating: %(rating)s stars", rating=int(name.rating / 2)), page="ratings") title=_(u"Rating: %(rating)s stars", rating=int(name.rating / 2)), page="ratings")
...@@ -827,7 +853,7 @@ def render_formats_books(page, book_id, order): ...@@ -827,7 +853,7 @@ def render_formats_books(page, book_id, order):
entries, random, pagination = calibre_db.fill_indexpage(page, 0, entries, random, pagination = calibre_db.fill_indexpage(page, 0,
db.Books, db.Books,
db.Books.data.any(db.Data.format == book_id.upper()), db.Books.data.any(db.Data.format == book_id.upper()),
[db.Books.timestamp.desc(), order[0]]) [order[0]])
return render_title_template('index.html', random=random, pagination=pagination, entries=entries, id=book_id, return render_title_template('index.html', random=random, pagination=pagination, entries=entries, id=book_id,
title=_(u"File format: %(format)s", format=name.format), page="formats") title=_(u"File format: %(format)s", format=name.format), page="formats")
else: else:
...@@ -860,7 +886,7 @@ def render_language_books(page, name, order): ...@@ -860,7 +886,7 @@ def render_language_books(page, name, order):
entries, random, pagination = calibre_db.fill_indexpage(page, 0, entries, random, pagination = calibre_db.fill_indexpage(page, 0,
db.Books, db.Books,
db.Books.languages.any(db.Languages.lang_code == name), db.Books.languages.any(db.Languages.lang_code == name),
[db.Books.timestamp.desc(), order[0]]) [order[0]])
return render_title_template('index.html', random=random, entries=entries, pagination=pagination, id=name, return render_title_template('index.html', random=random, entries=entries, pagination=pagination, id=name,
title=_(u"Language: %(name)s", name=lang_name), page="language") title=_(u"Language: %(name)s", name=lang_name), page="language")
...@@ -998,7 +1024,7 @@ def books_list(data, sort_param, book_id, page): ...@@ -998,7 +1024,7 @@ def books_list(data, sort_param, book_id, page):
@login_required @login_required
def books_table(): def books_table():
visibility = current_user.view_settings.get('table', {}) visibility = current_user.view_settings.get('table', {})
return render_title_template('book_table.html', title=_(u"Books list"), page="book_table", return render_title_template('book_table.html', title=_(u"Books List"), page="book_table",
visiblility=visibility) visiblility=visibility)
@web.route("/ajax/listbooks") @web.route("/ajax/listbooks")
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment