1
0
mirror of https://github.com/janeczku/calibre-web synced 2024-12-01 05:49:58 +00:00

Merge branch 'master' into Develop

# Conflicts:
#	cps/admin.py
#	cps/config_sql.py
#	cps/templates/config_edit.html
#	cps/web.py
This commit is contained in:
Ozzieisaacs 2020-12-03 16:03:37 +01:00
commit 7aabfc573b
33 changed files with 5589 additions and 5029 deletions

View File

@ -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():
_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():
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):
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

View File

@ -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')

View File

@ -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)

View File

@ -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"])

View File

@ -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
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):
@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):
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))

View File

@ -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)

View File

@ -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] %}

View File

@ -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 @@
<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'] %}

View File

@ -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">

View File

@ -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 %}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -21,7 +21,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: Calibre-Web\n" "Project-Id-Version: Calibre-Web\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2020-11-14 13:15+0100\n" "POT-Creation-Date: 2020-12-01 14:10+0100\n"
"PO-Revision-Date: 2020-06-07 06:47+0200\n" "PO-Revision-Date: 2020-06-07 06:47+0200\n"
"Last-Translator: Dekani <dekani1500@gmail.com>\n" "Last-Translator: Dekani <dekani1500@gmail.com>\n"
"Language: fr\n" "Language: fr\n"
@ -40,268 +40,268 @@ msgstr "installé"
msgid "not installed" msgid "not installed"
msgstr "non installé" msgstr "non installé"
#: cps/about.py:96 #: cps/about.py:99
msgid "Statistics" msgid "Statistics"
msgstr "Statistiques" msgstr "Statistiques"
#: cps/admin.py:93 #: cps/admin.py:94
msgid "Server restarted, please reload page" msgid "Server restarted, please reload page"
msgstr "Serveur redémarré, merci de rafraîchir la page" msgstr "Serveur redémarré, merci de rafraîchir la page"
#: cps/admin.py:95 #: cps/admin.py:96
msgid "Performing shutdown of server, please close window" msgid "Performing shutdown of server, please close window"
msgstr "Arrêt du serveur en cours, merci de fermer la fenêtre" msgstr "Arrêt du serveur en cours, merci de fermer la fenêtre"
#: cps/admin.py:103 #: cps/admin.py:104
msgid "Reconnect successful" msgid "Reconnect successful"
msgstr "Reconnecté avec succès" msgstr "Reconnecté avec succès"
#: cps/admin.py:106 #: cps/admin.py:107
msgid "Unknown command" msgid "Unknown command"
msgstr "Commande inconnue" msgstr "Commande inconnue"
#: cps/admin.py:116 cps/editbooks.py:611 cps/editbooks.py:623 #: cps/admin.py:117 cps/editbooks.py:611 cps/editbooks.py:623
#: cps/editbooks.py:723 cps/editbooks.py:725 cps/editbooks.py:786 #: cps/editbooks.py:726 cps/editbooks.py:728 cps/editbooks.py:789
#: cps/editbooks.py:802 cps/updater.py:510 cps/uploader.py:98 #: cps/editbooks.py:805 cps/updater.py:510 cps/uploader.py:98
#: cps/uploader.py:108 #: cps/uploader.py:108
msgid "Unknown" msgid "Unknown"
msgstr "Inconnu" msgstr "Inconnu"
#: cps/admin.py:137 #: cps/admin.py:138
msgid "Admin page" msgid "Admin page"
msgstr "Page admin" msgstr "Page admin"
#: cps/admin.py:159 #: cps/admin.py:160
msgid "UI Configuration" msgid "UI Configuration"
msgstr "Configuration de linterface utilisateur" msgstr "Configuration de linterface utilisateur"
#: cps/admin.py:191 cps/admin.py:718 #: cps/admin.py:192 cps/admin.py:729
msgid "Calibre-Web configuration updated" msgid "Calibre-Web configuration updated"
msgstr "Configuration de Calibre-Web mise à jour" msgstr "Configuration de Calibre-Web mise à jour"
#: cps/admin.py:436 cps/admin.py:442 cps/admin.py:453 cps/admin.py:464 #: cps/admin.py:438 cps/admin.py:444 cps/admin.py:455 cps/admin.py:466
#: cps/templates/modal_dialogs.html:29 #: cps/templates/modal_dialogs.html:29
msgid "Deny" msgid "Deny"
msgstr "Refuser" msgstr "Refuser"
#: cps/admin.py:438 cps/admin.py:444 cps/admin.py:455 cps/admin.py:466 #: cps/admin.py:440 cps/admin.py:446 cps/admin.py:457 cps/admin.py:468
#: cps/templates/modal_dialogs.html:28 #: cps/templates/modal_dialogs.html:28
msgid "Allow" msgid "Allow"
msgstr "Autoriser" msgstr "Autoriser"
#: cps/admin.py:512 #: cps/admin.py:514
msgid "client_secrets.json Is Not Configured For Web Application" msgid "client_secrets.json Is Not Configured For Web Application"
msgstr "client_secrets.json n'est pas configuré pour l'application Web" msgstr "client_secrets.json n'est pas configuré pour l'application Web"
#: cps/admin.py:551 #: cps/admin.py:554
msgid "Logfile Location is not Valid, Please Enter Correct Path" msgid "Logfile Location is not Valid, Please Enter Correct Path"
msgstr "L'emplacement du fichier logfile est incorrect, veuillez saisir un chemin valide" msgstr "L'emplacement du fichier logfile est incorrect, veuillez saisir un chemin valide"
#: cps/admin.py:556 #: cps/admin.py:560
msgid "Access Logfile Location is not Valid, Please Enter Correct Path" msgid "Access Logfile Location is not Valid, Please Enter Correct Path"
msgstr "L'emplacement du fichier Access Logfile est incorrect, veuillez saisir un chemin valide" msgstr "L'emplacement du fichier Access Logfile est incorrect, veuillez saisir un chemin valide"
#: cps/admin.py:582 #: cps/admin.py:586
msgid "Please Enter a LDAP Provider, Port, DN and User Object Identifier" msgid "Please Enter a LDAP Provider, Port, DN and User Object Identifier"
msgstr "Veuillez saisir un fournisseur LDAP, Port, DN et l'identifiant objet de l'utilisateur" msgstr "Veuillez saisir un fournisseur LDAP, Port, DN et l'identifiant objet de l'utilisateur"
#: cps/admin.py:595 #: cps/admin.py:601
#, python-format #, python-format
msgid "LDAP Group Object Filter Needs to Have One \"%s\" Format Identifier" msgid "LDAP Group Object Filter Needs to Have One \"%s\" Format Identifier"
msgstr "Le filtre objet du groupe LDAP a besoin d'un identifiant de format \"%s\"" msgstr "Le filtre objet du groupe LDAP a besoin d'un identifiant de format \"%s\""
#: cps/admin.py:598 #: cps/admin.py:604
msgid "LDAP Group Object Filter Has Unmatched Parenthesis" msgid "LDAP Group Object Filter Has Unmatched Parenthesis"
msgstr "Le filtre objet du groupe LDAP a une parenthèse non gérée" msgstr "Le filtre objet du groupe LDAP a une parenthèse non gérée"
#: cps/admin.py:602 #: cps/admin.py:609
#, python-format #, python-format
msgid "LDAP User Object Filter needs to Have One \"%s\" Format Identifier" msgid "LDAP User Object Filter needs to Have One \"%s\" Format Identifier"
msgstr "Le filtre objet de l'utilisateur LDAP a besoin d'un identifiant de format \"%s\"" msgstr "Le filtre objet de l'utilisateur LDAP a besoin d'un identifiant de format \"%s\""
#: cps/admin.py:605 #: cps/admin.py:612
msgid "LDAP User Object Filter Has Unmatched Parenthesis" msgid "LDAP User Object Filter Has Unmatched Parenthesis"
msgstr "Le filtre objet de l'utilisateur LDAP a une parenthèse non gérée" msgstr "Le filtre objet de l'utilisateur LDAP a une parenthèse non gérée"
#: cps/admin.py:609 #: cps/admin.py:617
msgid "LDAP Certificate Location is not Valid, Please Enter Correct Path" msgid "LDAP Certificate Location is not Valid, Please Enter Correct Path"
msgstr "L'emplacement du certificat LDAP est incorrect, veuillez saisir un chemin valide" msgstr "L'emplacement du certificat LDAP est incorrect, veuillez saisir un chemin valide"
#: cps/admin.py:631 #: cps/admin.py:642
msgid "Keyfile Location is not Valid, Please Enter Correct Path" msgid "Keyfile Location is not Valid, Please Enter Correct Path"
msgstr "L'emplacement du fichier Keyfile est incorrect, veuillez saisir un chemin valide" msgstr "L'emplacement du fichier Keyfile est incorrect, veuillez saisir un chemin valide"
#: cps/admin.py:635 #: cps/admin.py:646
msgid "Certfile Location is not Valid, Please Enter Correct Path" msgid "Certfile Location is not Valid, Please Enter Correct Path"
msgstr "L'emplacement du fichier Certfile est incorrect, veuillez saisir un chemin valide" msgstr "L'emplacement du fichier Certfile est incorrect, veuillez saisir un chemin valide"
#: cps/admin.py:701 cps/admin.py:800 cps/admin.py:890 cps/admin.py:939 #: cps/admin.py:712 cps/admin.py:811 cps/admin.py:901 cps/admin.py:950
#: cps/shelf.py:100 cps/shelf.py:161 cps/shelf.py:202 cps/shelf.py:260 #: cps/shelf.py:100 cps/shelf.py:161 cps/shelf.py:202 cps/shelf.py:260
#: cps/shelf.py:309 cps/shelf.py:338 cps/shelf.py:368 cps/shelf.py:392 #: cps/shelf.py:309 cps/shelf.py:338 cps/shelf.py:368 cps/shelf.py:392
msgid "Settings DB is not Writeable" msgid "Settings DB is not Writeable"
msgstr "" msgstr ""
#: cps/admin.py:713 #: cps/admin.py:724
msgid "DB Location is not Valid, Please Enter Correct Path" msgid "DB Location is not Valid, Please Enter Correct Path"
msgstr "L'emplacement DB est incorrect, veuillez saisir un chemin valide" msgstr "L'emplacement DB est incorrect, veuillez saisir un chemin valide"
#: cps/admin.py:715 #: cps/admin.py:726
msgid "DB is not Writeable" msgid "DB is not Writeable"
msgstr "La DB n'est pas accessible en écriture" msgstr "La DB n'est pas accessible en écriture"
#: cps/admin.py:748 #: cps/admin.py:759
msgid "Basic Configuration" msgid "Basic Configuration"
msgstr "Configuration principale" msgstr "Configuration principale"
#: cps/admin.py:763 cps/web.py:1508 #: cps/admin.py:774 cps/web.py:1508
msgid "Please fill out all fields!" msgid "Please fill out all fields!"
msgstr "Veuillez compléter tous les champs !" msgstr "Veuillez compléter tous les champs !"
#: cps/admin.py:766 cps/admin.py:778 cps/admin.py:784 cps/admin.py:908 #: cps/admin.py:777 cps/admin.py:789 cps/admin.py:795 cps/admin.py:919
msgid "Add new user" msgid "Add new user"
msgstr "Ajouter un nouvel utilisateur" msgstr "Ajouter un nouvel utilisateur"
#: cps/admin.py:775 cps/web.py:1754 #: cps/admin.py:786 cps/web.py:1754
msgid "E-mail is not from valid domain" msgid "E-mail is not from valid domain"
msgstr "Cette adresse de courriel nappartient pas à un domaine valide" msgstr "Cette adresse de courriel nappartient pas à un domaine valide"
#: cps/admin.py:782 cps/admin.py:797 #: cps/admin.py:793 cps/admin.py:808
msgid "Found an existing account for this e-mail address or nickname." msgid "Found an existing account for this e-mail address or nickname."
msgstr "Un compte existant a été trouvé pour cette adresse de courriel ou pour ce surnom." msgstr "Un compte existant a été trouvé pour cette adresse de courriel ou pour ce surnom."
#: cps/admin.py:793 #: cps/admin.py:804
#, python-format #, python-format
msgid "User '%(user)s' created" msgid "User '%(user)s' created"
msgstr "Utilisateur '%(user)s' créé" msgstr "Utilisateur '%(user)s' créé"
#: cps/admin.py:809 #: cps/admin.py:820
#, python-format #, python-format
msgid "User '%(nick)s' deleted" msgid "User '%(nick)s' deleted"
msgstr "Utilisateur '%(nick)s' supprimé" msgstr "Utilisateur '%(nick)s' supprimé"
#: cps/admin.py:812 #: cps/admin.py:823
msgid "No admin user remaining, can't delete user" msgid "No admin user remaining, can't delete user"
msgstr "Aucun utilisateur admin restant, impossible de supprimer lutilisateur" msgstr "Aucun utilisateur admin restant, impossible de supprimer lutilisateur"
#: cps/admin.py:818 #: cps/admin.py:829
msgid "No admin user remaining, can't remove admin role" msgid "No admin user remaining, can't remove admin role"
msgstr "Aucun utilisateur admin restant, impossible de supprimer le rôle admin" msgstr "Aucun utilisateur admin restant, impossible de supprimer le rôle admin"
#: cps/admin.py:854 cps/web.py:1796 #: cps/admin.py:865 cps/web.py:1796
msgid "Found an existing account for this e-mail address." msgid "Found an existing account for this e-mail address."
msgstr "Un compte existant a été trouvé pour cette adresse de courriel." msgstr "Un compte existant a été trouvé pour cette adresse de courriel."
#: cps/admin.py:863 cps/admin.py:877 cps/admin.py:980 cps/web.py:1772 #: cps/admin.py:874 cps/admin.py:888 cps/admin.py:991 cps/web.py:1772
#, python-format #, python-format
msgid "Edit User %(nick)s" msgid "Edit User %(nick)s"
msgstr "Éditer l'utilisateur %(nick)s" msgstr "Éditer l'utilisateur %(nick)s"
#: cps/admin.py:869 cps/web.py:1765 #: cps/admin.py:880 cps/web.py:1765
msgid "This username is already taken" msgid "This username is already taken"
msgstr "Cet utilisateur est déjà pris" msgstr "Cet utilisateur est déjà pris"
#: cps/admin.py:884 #: cps/admin.py:895
#, python-format #, python-format
msgid "User '%(nick)s' updated" msgid "User '%(nick)s' updated"
msgstr "Utilisateur '%(nick)s' mis à jour" msgstr "Utilisateur '%(nick)s' mis à jour"
#: cps/admin.py:887 #: cps/admin.py:898
msgid "An unknown error occured." msgid "An unknown error occured."
msgstr "Oups ! Une erreur inconnue a eu lieu." msgstr "Oups ! Une erreur inconnue a eu lieu."
#: cps/admin.py:917 cps/templates/admin.html:71 #: cps/admin.py:928 cps/templates/admin.html:71
msgid "Edit E-mail Server Settings" msgid "Edit E-mail Server Settings"
msgstr "Modifier les paramètres du serveur de courriels" msgstr "Modifier les paramètres du serveur de courriels"
#: cps/admin.py:946 #: cps/admin.py:957
#, python-format #, python-format
msgid "Test e-mail successfully send to %(kindlemail)s" msgid "Test e-mail successfully send to %(kindlemail)s"
msgstr "Courriel de test envoyé avec succès sur %(kindlemail)s" msgstr "Courriel de test envoyé avec succès sur %(kindlemail)s"
#: cps/admin.py:949 #: cps/admin.py:960
#, python-format #, python-format
msgid "There was an error sending the Test e-mail: %(res)s" msgid "There was an error sending the Test e-mail: %(res)s"
msgstr "Il y a eu une erreur pendant lenvoi du courriel de test : %(res)s" msgstr "Il y a eu une erreur pendant lenvoi du courriel de test : %(res)s"
#: cps/admin.py:951 #: cps/admin.py:962
msgid "Please configure your e-mail address first..." msgid "Please configure your e-mail address first..."
msgstr "Veuillez d'abord configurer votre adresse de courriel..." msgstr "Veuillez d'abord configurer votre adresse de courriel..."
#: cps/admin.py:953 #: cps/admin.py:964
msgid "E-mail server settings updated" msgid "E-mail server settings updated"
msgstr "Les paramètres du serveur de courriels ont été mis à jour" msgstr "Les paramètres du serveur de courriels ont été mis à jour"
#: cps/admin.py:964 #: cps/admin.py:975
msgid "User not found" msgid "User not found"
msgstr "L'utilisateur n'a pas été trouvé" msgstr "L'utilisateur n'a pas été trouvé"
#: cps/admin.py:991 #: cps/admin.py:1002
#, python-format #, python-format
msgid "Password for user %(user)s reset" msgid "Password for user %(user)s reset"
msgstr "Le mot de passe de lutilisateur %(user)s a été réinitialisé" msgstr "Le mot de passe de lutilisateur %(user)s a été réinitialisé"
#: cps/admin.py:994 cps/web.py:1532 cps/web.py:1596 #: cps/admin.py:1005 cps/web.py:1532 cps/web.py:1596
msgid "An unknown error occurred. Please try again later." msgid "An unknown error occurred. Please try again later."
msgstr "Une erreur inconnue est survenue. Veuillez réessayer plus tard." msgstr "Une erreur inconnue est survenue. Veuillez réessayer plus tard."
#: cps/admin.py:997 cps/web.py:1470 #: cps/admin.py:1008 cps/web.py:1470
msgid "Please configure the SMTP mail settings first..." msgid "Please configure the SMTP mail settings first..."
msgstr "Veuillez configurer les paramètres SMTP au préalable..." msgstr "Veuillez configurer les paramètres SMTP au préalable..."
#: cps/admin.py:1009 #: cps/admin.py:1020
msgid "Logfile viewer" msgid "Logfile viewer"
msgstr "Visualiseur de fichier journal" msgstr "Visualiseur de fichier journal"
#: cps/admin.py:1049 #: cps/admin.py:1081
msgid "Requesting update package" msgid "Requesting update package"
msgstr "Demande de mise à jour" msgstr "Demande de mise à jour"
#: cps/admin.py:1050 #: cps/admin.py:1082
msgid "Downloading update package" msgid "Downloading update package"
msgstr "Téléchargement de la mise à jour" msgstr "Téléchargement de la mise à jour"
#: cps/admin.py:1051 #: cps/admin.py:1083
msgid "Unzipping update package" msgid "Unzipping update package"
msgstr "Décompression de la mise à jour" msgstr "Décompression de la mise à jour"
#: cps/admin.py:1052 #: cps/admin.py:1084
msgid "Replacing files" msgid "Replacing files"
msgstr "Remplacement des fichiers" msgstr "Remplacement des fichiers"
#: cps/admin.py:1053 #: cps/admin.py:1085
msgid "Database connections are closed" msgid "Database connections are closed"
msgstr "Les connexions à la base de données ont été fermées" msgstr "Les connexions à la base de données ont été fermées"
#: cps/admin.py:1054 #: cps/admin.py:1086
msgid "Stopping server" msgid "Stopping server"
msgstr "Arrêt du serveur" msgstr "Arrêt du serveur"
#: cps/admin.py:1055 #: cps/admin.py:1087
msgid "Update finished, please press okay and reload page" msgid "Update finished, please press okay and reload page"
msgstr "Mise à jour terminée, merci dappuyer sur okay et de rafraîchir la page" msgstr "Mise à jour terminée, merci dappuyer sur okay et de rafraîchir la page"
#: cps/admin.py:1056 cps/admin.py:1057 cps/admin.py:1058 cps/admin.py:1059 #: cps/admin.py:1088 cps/admin.py:1089 cps/admin.py:1090 cps/admin.py:1091
#: cps/admin.py:1060 #: cps/admin.py:1092
msgid "Update failed:" msgid "Update failed:"
msgstr "La mise à jour a échoué :" msgstr "La mise à jour a échoué :"
#: cps/admin.py:1056 cps/updater.py:320 cps/updater.py:521 cps/updater.py:523 #: cps/admin.py:1088 cps/updater.py:320 cps/updater.py:521 cps/updater.py:523
msgid "HTTP Error" msgid "HTTP Error"
msgstr "Erreur HTTP" msgstr "Erreur HTTP"
#: cps/admin.py:1057 cps/updater.py:322 cps/updater.py:525 #: cps/admin.py:1089 cps/updater.py:322 cps/updater.py:525
msgid "Connection error" msgid "Connection error"
msgstr "Erreur de connexion" msgstr "Erreur de connexion"
#: cps/admin.py:1058 cps/updater.py:324 cps/updater.py:527 #: cps/admin.py:1090 cps/updater.py:324 cps/updater.py:527
msgid "Timeout while establishing connection" msgid "Timeout while establishing connection"
msgstr "Délai d'attente dépassé lors de l'établissement de connexion" msgstr "Délai d'attente dépassé lors de l'établissement de connexion"
#: cps/admin.py:1059 cps/updater.py:326 cps/updater.py:529 #: cps/admin.py:1091 cps/updater.py:326 cps/updater.py:529
msgid "General error" msgid "General error"
msgstr "Erreur générale" msgstr "Erreur générale"
#: cps/admin.py:1060 #: cps/admin.py:1092
msgid "Update File Could Not be Saved in Temp Dir" msgid "Update File Could Not be Saved in Temp Dir"
msgstr "Le fichier de mise à jour ne peut pas être sauvegardé dans le répertoire temporaire" msgstr "Le fichier de mise à jour ne peut pas être sauvegardé dans le répertoire temporaire"
@ -335,12 +335,12 @@ msgstr "modifier les métadonnées"
msgid "%(langname)s is not a valid language" msgid "%(langname)s is not a valid language"
msgstr "%(langname)s n'est pas une langue valide" msgstr "%(langname)s n'est pas une langue valide"
#: cps/editbooks.py:512 cps/editbooks.py:768 #: cps/editbooks.py:512 cps/editbooks.py:771
#, python-format #, python-format
msgid "File extension '%(ext)s' is not allowed to be uploaded to this server" msgid "File extension '%(ext)s' is not allowed to be uploaded to this server"
msgstr "Lextension de fichier '%(ext)s' nest pas autorisée pour être déposée sur ce serveur" msgstr "Lextension de fichier '%(ext)s' nest pas autorisée pour être déposée sur ce serveur"
#: cps/editbooks.py:516 cps/editbooks.py:772 #: cps/editbooks.py:516 cps/editbooks.py:775
msgid "File to be uploaded must have an extension" msgid "File to be uploaded must have an extension"
msgstr "Pour être déposé le fichier doit avoir une extension" msgstr "Pour être déposé le fichier doit avoir une extension"
@ -354,7 +354,7 @@ msgstr "Impossible de créer le chemin %(path)s (Permission refusée)."
msgid "Failed to store file %(file)s." msgid "Failed to store file %(file)s."
msgstr "Échec de la sauvegarde du fichier %(file)s." msgstr "Échec de la sauvegarde du fichier %(file)s."
#: cps/editbooks.py:551 cps/editbooks.py:903 #: cps/editbooks.py:551 cps/editbooks.py:906
#, python-format #, python-format
msgid "Database error: %(error)s." msgid "Database error: %(error)s."
msgstr "Erreur de la base de données: %(error)s." msgstr "Erreur de la base de données: %(error)s."
@ -364,47 +364,47 @@ msgstr "Erreur de la base de données: %(error)s."
msgid "File format %(ext)s added to %(book)s" msgid "File format %(ext)s added to %(book)s"
msgstr "Le format de fichier %(ext)s a été ajouté à %(book)s" msgstr "Le format de fichier %(ext)s a été ajouté à %(book)s"
#: cps/editbooks.py:672 #: cps/editbooks.py:675
msgid "Identifiers are not Case Sensitive, Overwriting Old Identifier" msgid "Identifiers are not Case Sensitive, Overwriting Old Identifier"
msgstr "" msgstr ""
#: cps/editbooks.py:709 #: cps/editbooks.py:712
msgid "Metadata successfully updated" msgid "Metadata successfully updated"
msgstr "Les métadonnées ont bien été mises à jour" msgstr "Les métadonnées ont bien été mises à jour"
#: cps/editbooks.py:718 #: cps/editbooks.py:721
msgid "Error editing book, please check logfile for details" msgid "Error editing book, please check logfile for details"
msgstr "Erreur dédition du livre, veuillez consulter le journal (log) pour plus de détails" msgstr "Erreur dédition du livre, veuillez consulter le journal (log) pour plus de détails"
#: cps/editbooks.py:780 #: cps/editbooks.py:783
#, python-format #, python-format
msgid "File %(filename)s could not saved to temp dir" msgid "File %(filename)s could not saved to temp dir"
msgstr "Le fichier %(filename)s ne peut pas être sauvegardé dans le répertoire temporaire" msgstr "Le fichier %(filename)s ne peut pas être sauvegardé dans le répertoire temporaire"
#: cps/editbooks.py:790 #: cps/editbooks.py:793
msgid "Uploaded book probably exists in the library, consider to change before upload new: " msgid "Uploaded book probably exists in the library, consider to change before upload new: "
msgstr "Le fichier téléchargé existe probablement dans la librairie, veuillez le modifier avant de le télécharger de nouveau: " msgstr "Le fichier téléchargé existe probablement dans la librairie, veuillez le modifier avant de le télécharger de nouveau: "
#: cps/editbooks.py:878 #: cps/editbooks.py:881
#, python-format #, python-format
msgid "Failed to Move Cover File %(file)s: %(error)s" msgid "Failed to Move Cover File %(file)s: %(error)s"
msgstr "Impossible de déplacer le fichier de couverture %(file)s: %(error)s" msgstr "Impossible de déplacer le fichier de couverture %(file)s: %(error)s"
#: cps/editbooks.py:889 #: cps/editbooks.py:892
#, python-format #, python-format
msgid "File %(file)s uploaded" msgid "File %(file)s uploaded"
msgstr "Le fichier %(file)s a été téléchargé" msgstr "Le fichier %(file)s a été téléchargé"
#: cps/editbooks.py:915 #: cps/editbooks.py:918
msgid "Source or destination format for conversion missing" msgid "Source or destination format for conversion missing"
msgstr "Le format de conversion de la source ou de la destination est manquant" msgstr "Le format de conversion de la source ou de la destination est manquant"
#: cps/editbooks.py:923 #: cps/editbooks.py:926
#, python-format #, python-format
msgid "Book successfully queued for converting to %(book_format)s" msgid "Book successfully queued for converting to %(book_format)s"
msgstr "Le livre a été mis avec succès en file de traitement pour conversion vers %(book_format)s" msgstr "Le livre a été mis avec succès en file de traitement pour conversion vers %(book_format)s"
#: cps/editbooks.py:927 #: cps/editbooks.py:930
#, python-format #, python-format
msgid "There was an error converting this book: %(res)s" msgid "There was an error converting this book: %(res)s"
msgstr "Une erreur est survenue au cours de la conversion du livre : %(res)s" msgstr "Une erreur est survenue au cours de la conversion du livre : %(res)s"
@ -417,151 +417,151 @@ msgstr "La configuration de Google Drive nest pas terminée, essayez de désa
msgid "Callback domain is not verified, please follow steps to verify domain in google developer console" msgid "Callback domain is not verified, please follow steps to verify domain in google developer console"
msgstr "Le domaine de retour dappel (Callback domain) est non vérifié, veuillez suivre les étapes nécessaires pour vérifier le domaine dans la console de développement de Google" msgstr "Le domaine de retour dappel (Callback domain) est non vérifié, veuillez suivre les étapes nécessaires pour vérifier le domaine dans la console de développement de Google"
#: cps/helper.py:79 #: cps/helper.py:82
#, python-format #, python-format
msgid "%(format)s format not found for book id: %(book)d" msgid "%(format)s format not found for book id: %(book)d"
msgstr "le format %(format)s est introuvable pour le livre : %(book)d" msgstr "le format %(format)s est introuvable pour le livre : %(book)d"
#: cps/helper.py:85 cps/tasks/convert.py:50 #: cps/helper.py:88 cps/tasks/convert.py:50
#, python-format #, python-format
msgid "%(format)s not found on Google Drive: %(fn)s" msgid "%(format)s not found on Google Drive: %(fn)s"
msgstr "le %(format)s est introuvable sur Google Drive : %(fn)s" msgstr "le %(format)s est introuvable sur Google Drive : %(fn)s"
#: cps/helper.py:90 #: cps/helper.py:93
#, python-format #, python-format
msgid "%(format)s not found: %(fn)s" msgid "%(format)s not found: %(fn)s"
msgstr "%(format)s introuvable : %(fn)s" msgstr "%(format)s introuvable : %(fn)s"
#: cps/helper.py:95 cps/helper.py:228 cps/templates/detail.html:41 #: cps/helper.py:98 cps/helper.py:231 cps/templates/detail.html:41
#: cps/templates/detail.html:45 #: cps/templates/detail.html:45
msgid "Send to Kindle" msgid "Send to Kindle"
msgstr "Envoyer vers Kindle" msgstr "Envoyer vers Kindle"
#: cps/helper.py:96 cps/helper.py:112 cps/helper.py:230 #: cps/helper.py:99 cps/helper.py:115 cps/helper.py:233
msgid "This e-mail has been sent via Calibre-Web." msgid "This e-mail has been sent via Calibre-Web."
msgstr "Ce courriel a été envoyé depuis Calibre-Web." msgstr "Ce courriel a été envoyé depuis Calibre-Web."
#: cps/helper.py:110 #: cps/helper.py:113
msgid "Calibre-Web test e-mail" msgid "Calibre-Web test e-mail"
msgstr "Courriel de test de Calibre-Web" msgstr "Courriel de test de Calibre-Web"
#: cps/helper.py:111 #: cps/helper.py:114
msgid "Test e-mail" msgid "Test e-mail"
msgstr "Courriel de test" msgstr "Courriel de test"
#: cps/helper.py:128 #: cps/helper.py:131
msgid "Get Started with Calibre-Web" msgid "Get Started with Calibre-Web"
msgstr "Bien démarrer avec Calibre-Web" msgstr "Bien démarrer avec Calibre-Web"
#: cps/helper.py:133 #: cps/helper.py:136
#, python-format #, python-format
msgid "Registration e-mail for user: %(name)s" msgid "Registration e-mail for user: %(name)s"
msgstr "Courriel dinscription pour lutilisateur : %(name)s" msgstr "Courriel dinscription pour lutilisateur : %(name)s"
#: cps/helper.py:153 cps/helper.py:157 cps/helper.py:161 cps/helper.py:170 #: cps/helper.py:156 cps/helper.py:160 cps/helper.py:164 cps/helper.py:173
#: cps/helper.py:174 cps/helper.py:178 #: cps/helper.py:177 cps/helper.py:181
#, python-format #, python-format
msgid "Send %(format)s to Kindle" msgid "Send %(format)s to Kindle"
msgstr "Envoyer %(format)s vers le Kindle" msgstr "Envoyer %(format)s vers le Kindle"
#: cps/helper.py:183 cps/helper.py:189 #: cps/helper.py:186 cps/helper.py:192
#, python-format #, python-format
msgid "Convert %(orig)s to %(format)s and send to Kindle" msgid "Convert %(orig)s to %(format)s and send to Kindle"
msgstr "Convertir de %(orig)s vers %(format)s et envoyer au Kindle" msgstr "Convertir de %(orig)s vers %(format)s et envoyer au Kindle"
#: cps/helper.py:230 #: cps/helper.py:233
#, python-format #, python-format
msgid "E-mail: %(book)s" msgid "E-mail: %(book)s"
msgstr "Courriel : %(book)s" msgstr "Courriel : %(book)s"
#: cps/helper.py:232 #: cps/helper.py:235
msgid "The requested file could not be read. Maybe wrong permissions?" msgid "The requested file could not be read. Maybe wrong permissions?"
msgstr "Le fichier demandé na pu être lu. Problème de permission daccès ?" msgstr "Le fichier demandé na pu être lu. Problème de permission daccès ?"
#: cps/helper.py:329 #: cps/helper.py:332
#, python-format #, python-format
msgid "Deleting bookfolder for book %(id)s failed, path has subfolders: %(path)s" msgid "Deleting bookfolder for book %(id)s failed, path has subfolders: %(path)s"
msgstr "" msgstr ""
#: cps/helper.py:335 #: cps/helper.py:338
#, python-format #, python-format
msgid "Deleting book %(id)s failed: %(message)s" msgid "Deleting book %(id)s failed: %(message)s"
msgstr "La suppression du livre %(id)s a échoué: %(message)s" msgstr "La suppression du livre %(id)s a échoué: %(message)s"
#: cps/helper.py:345 #: cps/helper.py:348
#, python-format #, python-format
msgid "Deleting book %(id)s, book path not valid: %(path)s" msgid "Deleting book %(id)s, book path not valid: %(path)s"
msgstr "Suppression du livre %(id)s, le chemin du livre est invalide : %(path)s" msgstr "Suppression du livre %(id)s, le chemin du livre est invalide : %(path)s"
#: cps/helper.py:400 #: cps/helper.py:403
#, python-format #, python-format
msgid "Rename title from: '%(src)s' to '%(dest)s' failed with error: %(error)s" msgid "Rename title from: '%(src)s' to '%(dest)s' failed with error: %(error)s"
msgstr "Renommer le titre de : '%(src)s' à '%(dest)s' a échoué avec lerreur : %(error)s" msgstr "Renommer le titre de : '%(src)s' à '%(dest)s' a échoué avec lerreur : %(error)s"
#: cps/helper.py:415 #: cps/helper.py:418
#, python-format #, python-format
msgid "Rename file in path '%(src)s' to '%(dest)s' failed with error: %(error)s" msgid "Rename file in path '%(src)s' to '%(dest)s' failed with error: %(error)s"
msgstr "La modification du nom de fichier du chemin : '%(src)s' vers '%(dest)s' a échoué avec lerreur : %(error)s" msgstr "La modification du nom de fichier du chemin : '%(src)s' vers '%(dest)s' a échoué avec lerreur : %(error)s"
#: cps/helper.py:440 cps/helper.py:450 cps/helper.py:458 #: cps/helper.py:443 cps/helper.py:453 cps/helper.py:461
#, python-format #, python-format
msgid "File %(file)s not found on Google Drive" msgid "File %(file)s not found on Google Drive"
msgstr "Le fichier %(file)s n'a pas été trouvé dans Google Drive" msgstr "Le fichier %(file)s n'a pas été trouvé dans Google Drive"
#: cps/helper.py:479 #: cps/helper.py:482
#, python-format #, python-format
msgid "Book path %(path)s not found on Google Drive" msgid "Book path %(path)s not found on Google Drive"
msgstr "Le chemin du livre %(path)s n'a pas été trouvé dans Google Drive" msgstr "Le chemin du livre %(path)s n'a pas été trouvé dans Google Drive"
#: cps/helper.py:588 #: cps/helper.py:591
msgid "Error Downloading Cover" msgid "Error Downloading Cover"
msgstr "Erreur lors du téléchargement de la couverture" msgstr "Erreur lors du téléchargement de la couverture"
#: cps/helper.py:591 #: cps/helper.py:594
msgid "Cover Format Error" msgid "Cover Format Error"
msgstr "Erreur de format de couverture" msgstr "Erreur de format de couverture"
#: cps/helper.py:606 #: cps/helper.py:609
msgid "Failed to create path for cover" msgid "Failed to create path for cover"
msgstr "Impossible de créer le chemin pour la couverture" msgstr "Impossible de créer le chemin pour la couverture"
#: cps/helper.py:611 #: cps/helper.py:614
msgid "Cover-file is not a valid image file, or could not be stored" msgid "Cover-file is not a valid image file, or could not be stored"
msgstr "Le fichier couverture n'est pas un fichier image valide, ou ne peut pas être stocké" msgstr "Le fichier couverture n'est pas un fichier image valide, ou ne peut pas être stocké"
#: cps/helper.py:622 #: cps/helper.py:625
msgid "Only jpg/jpeg/png/webp files are supported as coverfile" msgid "Only jpg/jpeg/png/webp files are supported as coverfile"
msgstr "Seuls les fichiers jpg/jpeg/png/webp sont supportés comme fichier de couverture" msgstr "Seuls les fichiers jpg/jpeg/png/webp sont supportés comme fichier de couverture"
#: cps/helper.py:636 #: cps/helper.py:639
msgid "Only jpg/jpeg files are supported as coverfile" msgid "Only jpg/jpeg files are supported as coverfile"
msgstr "Seuls les fichiers jpg/jpeg sont supportés comme fichier de couverture" msgstr "Seuls les fichiers jpg/jpeg sont supportés comme fichier de couverture"
#: cps/helper.py:684 #: cps/helper.py:687
msgid "Unrar binary file not found" msgid "Unrar binary file not found"
msgstr "Fichier binaire Unrar non trouvé" msgstr "Fichier binaire Unrar non trouvé"
#: cps/helper.py:698 #: cps/helper.py:701
msgid "Error excecuting UnRar" msgid "Error excecuting UnRar"
msgstr "Une erreur est survenue lors de l'exécution d'UnRar" msgstr "Une erreur est survenue lors de l'exécution d'UnRar"
#: cps/helper.py:747 #: cps/helper.py:750
msgid "Waiting" msgid "Waiting"
msgstr "En attente" msgstr "En attente"
#: cps/helper.py:749 #: cps/helper.py:752
msgid "Failed" msgid "Failed"
msgstr "Echoué" msgstr "Echoué"
#: cps/helper.py:751 #: cps/helper.py:754
msgid "Started" msgid "Started"
msgstr "Débuté" msgstr "Débuté"
#: cps/helper.py:753 #: cps/helper.py:756
msgid "Finished" msgid "Finished"
msgstr "Terminé" msgstr "Terminé"
#: cps/helper.py:755 #: cps/helper.py:758
msgid "Unknown Status" msgid "Unknown Status"
msgstr "Statut inconnu" msgstr "Statut inconnu"
@ -857,7 +857,7 @@ msgstr "Livres archivés"
msgid "Show archived books" msgid "Show archived books"
msgstr "Afficher les livres archivés" msgstr "Afficher les livres archivés"
#: cps/ub.py:120 #: cps/ub.py:120 cps/web.py:1001
msgid "Books List" msgid "Books List"
msgstr "" msgstr ""
@ -986,10 +986,6 @@ msgstr "Recherche avancée"
msgid "Search" msgid "Search"
msgstr "Chercher" msgstr "Chercher"
#: cps/web.py:1001
msgid "Books list"
msgstr ""
#: cps/web.py:1139 #: cps/web.py:1139
msgid "Ratings list" msgid "Ratings list"
msgstr "Liste des évaluations" msgstr "Liste des évaluations"
@ -1299,60 +1295,64 @@ msgstr "Éditer la configuration principale"
msgid "Edit UI Configuration" msgid "Edit UI Configuration"
msgstr "Configuration de linterface utilisateur" msgstr "Configuration de linterface utilisateur"
#: cps/templates/admin.html:137 #: cps/templates/admin.html:136
msgid "Administration" msgid "Administration"
msgstr "Administration" msgstr "Administration"
#: cps/templates/admin.html:137
msgid "Download Debug Package"
msgstr ""
#: cps/templates/admin.html:138 #: cps/templates/admin.html:138
msgid "View Logs" msgid "View Logs"
msgstr "Afficher les fichiers journaux" msgstr "Afficher les fichiers journaux"
#: cps/templates/admin.html:139 #: cps/templates/admin.html:141
msgid "Reconnect Calibre Database" msgid "Reconnect Calibre Database"
msgstr "Reconnecter la base de données Calibre" msgstr "Reconnecter la base de données Calibre"
#: cps/templates/admin.html:140 #: cps/templates/admin.html:142
msgid "Restart" msgid "Restart"
msgstr "Redémarrer Calibre-Web" msgstr "Redémarrer Calibre-Web"
#: cps/templates/admin.html:141 #: cps/templates/admin.html:143
msgid "Shutdown" msgid "Shutdown"
msgstr "Arrêter Calibre-Web" msgstr "Arrêter Calibre-Web"
#: cps/templates/admin.html:147 #: cps/templates/admin.html:149
msgid "Update" msgid "Update"
msgstr "Mise à jour de Calibre-Web" msgstr "Mise à jour de Calibre-Web"
#: cps/templates/admin.html:151 #: cps/templates/admin.html:153
msgid "Version" msgid "Version"
msgstr "Version" msgstr "Version"
#: cps/templates/admin.html:152 #: cps/templates/admin.html:154
msgid "Details" msgid "Details"
msgstr "Détails" msgstr "Détails"
#: cps/templates/admin.html:158 #: cps/templates/admin.html:160
msgid "Current version" msgid "Current version"
msgstr "Version actuelle" msgstr "Version actuelle"
#: cps/templates/admin.html:164 #: cps/templates/admin.html:166
msgid "Check for Update" msgid "Check for Update"
msgstr "Rechercher les mises à jour" msgstr "Rechercher les mises à jour"
#: cps/templates/admin.html:165 #: cps/templates/admin.html:167
msgid "Perform Update" msgid "Perform Update"
msgstr "Effectuer la mise à jour" msgstr "Effectuer la mise à jour"
#: cps/templates/admin.html:177 #: cps/templates/admin.html:179
msgid "Are you sure you want to restart?" msgid "Are you sure you want to restart?"
msgstr "Voulez-vous vraiment redémarrer Calibre-Web?" msgstr "Voulez-vous vraiment redémarrer Calibre-Web?"
#: cps/templates/admin.html:182 cps/templates/admin.html:196 #: cps/templates/admin.html:184 cps/templates/admin.html:198
#: cps/templates/admin.html:216 cps/templates/shelf.html:80 #: cps/templates/admin.html:218 cps/templates/shelf.html:80
msgid "OK" msgid "OK"
msgstr "OK" msgstr "OK"
#: cps/templates/admin.html:183 cps/templates/admin.html:197 #: cps/templates/admin.html:185 cps/templates/admin.html:199
#: cps/templates/book_edit.html:192 cps/templates/book_table.html:84 #: cps/templates/book_edit.html:192 cps/templates/book_table.html:84
#: cps/templates/config_edit.html:391 cps/templates/config_view_edit.html:151 #: cps/templates/config_edit.html:391 cps/templates/config_view_edit.html:151
#: cps/templates/email_edit.html:47 cps/templates/email_edit.html:101 #: cps/templates/email_edit.html:47 cps/templates/email_edit.html:101
@ -1361,11 +1361,11 @@ msgstr "OK"
msgid "Cancel" msgid "Cancel"
msgstr "Annuler" msgstr "Annuler"
#: cps/templates/admin.html:195 #: cps/templates/admin.html:197
msgid "Are you sure you want to shutdown?" msgid "Are you sure you want to shutdown?"
msgstr "Voulez-vous vraiment arrêter Calibre-Web?" msgstr "Voulez-vous vraiment arrêter Calibre-Web?"
#: cps/templates/admin.html:207 #: cps/templates/admin.html:209
msgid "Updating, please do not reload this page" msgid "Updating, please do not reload this page"
msgstr "Mise à jour en cours, ne pas rafraîchir la page" msgstr "Mise à jour en cours, ne pas rafraîchir la page"
@ -2304,6 +2304,14 @@ msgstr "Le flux de sortie ne peut pas être affiché"
msgid "Show Access Log: " msgid "Show Access Log: "
msgstr "Afficher le journal d'accès : " msgstr "Afficher le journal d'accès : "
#: cps/templates/logviewer.html:18
msgid "Download Calibre-Web Log"
msgstr ""
#: cps/templates/logviewer.html:21
msgid "Download Access Log"
msgstr ""
#: cps/templates/modal_dialogs.html:6 #: cps/templates/modal_dialogs.html:6
msgid "Select Allowed/Denied Tags" msgid "Select Allowed/Denied Tags"
msgstr "Sélectionner les étiquettes autorisées/refusées" msgstr "Sélectionner les étiquettes autorisées/refusées"

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -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
if ub.session.query(ub.User).filter(ub.User.nickname == user_identifier.lower()).first():
log.warning("LDAP User: %s Already in Database", user_identifier)
continue continue
if ub.session.query(ub.User).filter(ub.User.nickname == user.lower()).first(): user_data = services.ldap.get_object_details(user=user_identifier,
log.warning("LDAP User: %s Already in Database", user)
continue
user_data = services.ldap.get_object_details(user=user,
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():
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():
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):
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):
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):
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):
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):
@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")

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff