mirror of
https://github.com/janeczku/calibre-web
synced 2024-11-24 18:47:23 +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:
commit
7aabfc573b
24
cps/admin.py
24
cps/admin.py
@ -164,7 +164,6 @@ def view_configuration():
|
||||
@login_required
|
||||
@admin_required
|
||||
def update_view_configuration():
|
||||
reboot_required = False
|
||||
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)
|
||||
@ -172,7 +171,8 @@ def update_view_configuration():
|
||||
|
||||
_config_string("config_calibre_web_title")
|
||||
_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_theme")
|
||||
@ -191,10 +191,6 @@ def update_view_configuration():
|
||||
config.save()
|
||||
flash(_(u"Calibre-Web configuration updated"), category="success")
|
||||
before_request()
|
||||
if reboot_required:
|
||||
db.dispose()
|
||||
ub.dispose()
|
||||
web_server.stop(True)
|
||||
|
||||
return view_configuration()
|
||||
|
||||
@ -612,14 +608,26 @@ def _configuration_ldap_helper(to_save, gdriveError):
|
||||
gdriveError)
|
||||
if config.config_ldap_user_object.count("(") != config.config_ldap_user_object.count(")"):
|
||||
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 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_key_path)):
|
||||
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)
|
||||
return reboot_required, None
|
||||
|
||||
|
@ -113,6 +113,7 @@ class _Settings(_Base):
|
||||
config_ldap_key_path = Column(String, default="")
|
||||
config_ldap_dn = Column(String, default='dc=example,dc=org')
|
||||
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_group_object_filter = Column(String, default='(&(objectclass=posixGroup)(cn=%s))')
|
||||
config_ldap_group_members_field = Column(String, default='memberUid')
|
||||
|
@ -935,8 +935,6 @@ def convert_bookformat(book_id):
|
||||
@edit_required
|
||||
def edit_list_book(param):
|
||||
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'])
|
||||
if param =='series_index':
|
||||
edit_book_series_index(vals['value'], book)
|
||||
|
@ -862,6 +862,7 @@ def HandleUserRequest(dummy=None):
|
||||
@kobo.route("/v1/products/<dummy>/recommendations", 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/books/external/<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/dailydeal", methods=["GET", "POST"])
|
||||
|
16
cps/opds.py
16
cps/opds.py
@ -25,7 +25,7 @@ import sys
|
||||
import datetime
|
||||
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 sqlalchemy.sql.expression import func, text, or_, and_
|
||||
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 .helper import get_download_link, get_book_cover
|
||||
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 babel import Locale as LC
|
||||
from babel.core import UnknownLocaleError
|
||||
@ -383,8 +383,13 @@ def feed_shelf(book_id):
|
||||
|
||||
@opds.route("/opds/download/<book_id>/<book_format>/")
|
||||
@requires_basic_auth_if_no_ano
|
||||
@download_required
|
||||
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'):
|
||||
client = "kobo"
|
||||
else:
|
||||
@ -418,7 +423,10 @@ def feed_search(term):
|
||||
|
||||
def check_auth(username, password):
|
||||
if sys.version_info.major == 3:
|
||||
username = username.encode('windows-1252')
|
||||
try:
|
||||
username = username.encode('windows-1252')
|
||||
except UnicodeEncodeError:
|
||||
username = username.encode('utf-8')
|
||||
user = ub.session.query(ub.User).filter(func.lower(ub.User.nickname) ==
|
||||
username.decode('utf-8').lower()).first()
|
||||
return bool(user and check_password_hash(str(user.password), password))
|
||||
|
@ -117,7 +117,7 @@ def bind_user(username, password):
|
||||
return None, error
|
||||
except LDAPException as ex:
|
||||
if ex.message == 'Invalid credentials':
|
||||
error = ("LDAP admin login failed")
|
||||
error = "LDAP admin login failed"
|
||||
return None, error
|
||||
if ex.message == "Can't contact LDAP server":
|
||||
# log.warning('LDAP Server down: %s', ex)
|
||||
|
@ -23,16 +23,12 @@
|
||||
<h3>{{_("In Library")}}</h3>
|
||||
{% endif %}
|
||||
<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="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="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="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="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_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>
|
||||
<!--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-->
|
||||
<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" 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" 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" 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" 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" 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>
|
||||
<div class="row display-flex">
|
||||
{% if entries[0] %}
|
||||
|
@ -60,10 +60,10 @@
|
||||
<label for="config_google_drive_watch_changes_response">{{_('Metadata Watch Channel ID')}}</label>
|
||||
<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="">
|
||||
<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>
|
||||
{% 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 %}
|
||||
@ -331,6 +331,20 @@
|
||||
<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">
|
||||
</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>
|
||||
{% endif %}
|
||||
{% if feature_support['oauth'] %}
|
||||
|
@ -62,15 +62,18 @@
|
||||
<div class="discover load-more">
|
||||
<h2 class="{{title}}">{{_(title)}}</h2>
|
||||
<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 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 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 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 class="btn-group character">
|
||||
<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>
|
||||
</div-->
|
||||
<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 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 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 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 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 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>
|
||||
<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 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>
|
||||
{% 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 class="row display-flex">
|
||||
|
@ -26,12 +26,14 @@
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<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="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="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="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="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="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="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" 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" 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" 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="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="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>
|
||||
{% endif %}
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
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
@ -21,7 +21,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Calibre-Web\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"
|
||||
"Last-Translator: Dekani <dekani1500@gmail.com>\n"
|
||||
"Language: fr\n"
|
||||
@ -40,268 +40,268 @@ msgstr "installé"
|
||||
msgid "not installed"
|
||||
msgstr "non installé"
|
||||
|
||||
#: cps/about.py:96
|
||||
#: cps/about.py:99
|
||||
msgid "Statistics"
|
||||
msgstr "Statistiques"
|
||||
|
||||
#: cps/admin.py:93
|
||||
#: cps/admin.py:94
|
||||
msgid "Server restarted, please reload 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"
|
||||
msgstr "Arrêt du serveur en cours, merci de fermer la fenêtre"
|
||||
|
||||
#: cps/admin.py:103
|
||||
#: cps/admin.py:104
|
||||
msgid "Reconnect successful"
|
||||
msgstr "Reconnecté avec succès"
|
||||
|
||||
#: cps/admin.py:106
|
||||
#: cps/admin.py:107
|
||||
msgid "Unknown command"
|
||||
msgstr "Commande inconnue"
|
||||
|
||||
#: cps/admin.py:116 cps/editbooks.py:611 cps/editbooks.py:623
|
||||
#: cps/editbooks.py:723 cps/editbooks.py:725 cps/editbooks.py:786
|
||||
#: cps/editbooks.py:802 cps/updater.py:510 cps/uploader.py:98
|
||||
#: cps/admin.py:117 cps/editbooks.py:611 cps/editbooks.py:623
|
||||
#: cps/editbooks.py:726 cps/editbooks.py:728 cps/editbooks.py:789
|
||||
#: cps/editbooks.py:805 cps/updater.py:510 cps/uploader.py:98
|
||||
#: cps/uploader.py:108
|
||||
msgid "Unknown"
|
||||
msgstr "Inconnu"
|
||||
|
||||
#: cps/admin.py:137
|
||||
#: cps/admin.py:138
|
||||
msgid "Admin page"
|
||||
msgstr "Page admin"
|
||||
|
||||
#: cps/admin.py:159
|
||||
#: cps/admin.py:160
|
||||
msgid "UI Configuration"
|
||||
msgstr "Configuration de l’interface utilisateur"
|
||||
|
||||
#: cps/admin.py:191 cps/admin.py:718
|
||||
#: cps/admin.py:192 cps/admin.py:729
|
||||
msgid "Calibre-Web configuration updated"
|
||||
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
|
||||
msgid "Deny"
|
||||
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
|
||||
msgid "Allow"
|
||||
msgstr "Autoriser"
|
||||
|
||||
#: cps/admin.py:512
|
||||
#: cps/admin.py:514
|
||||
msgid "client_secrets.json Is Not Configured For Web Application"
|
||||
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"
|
||||
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"
|
||||
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"
|
||||
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
|
||||
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\""
|
||||
|
||||
#: cps/admin.py:598
|
||||
#: cps/admin.py:604
|
||||
msgid "LDAP Group Object Filter Has Unmatched Parenthesis"
|
||||
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
|
||||
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\""
|
||||
|
||||
#: cps/admin.py:605
|
||||
#: cps/admin.py:612
|
||||
msgid "LDAP User Object Filter Has Unmatched Parenthesis"
|
||||
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"
|
||||
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"
|
||||
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"
|
||||
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:309 cps/shelf.py:338 cps/shelf.py:368 cps/shelf.py:392
|
||||
msgid "Settings DB is not Writeable"
|
||||
msgstr ""
|
||||
|
||||
#: cps/admin.py:713
|
||||
#: cps/admin.py:724
|
||||
msgid "DB Location is not Valid, Please Enter Correct Path"
|
||||
msgstr "L'emplacement DB est incorrect, veuillez saisir un chemin valide"
|
||||
|
||||
#: cps/admin.py:715
|
||||
#: cps/admin.py:726
|
||||
msgid "DB is not Writeable"
|
||||
msgstr "La DB n'est pas accessible en écriture"
|
||||
|
||||
#: cps/admin.py:748
|
||||
#: cps/admin.py:759
|
||||
msgid "Basic Configuration"
|
||||
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!"
|
||||
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"
|
||||
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"
|
||||
msgstr "Cette adresse de courriel n’appartient 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."
|
||||
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
|
||||
msgid "User '%(user)s' created"
|
||||
msgstr "Utilisateur '%(user)s' créé"
|
||||
|
||||
#: cps/admin.py:809
|
||||
#: cps/admin.py:820
|
||||
#, python-format
|
||||
msgid "User '%(nick)s' deleted"
|
||||
msgstr "Utilisateur '%(nick)s' supprimé"
|
||||
|
||||
#: cps/admin.py:812
|
||||
#: cps/admin.py:823
|
||||
msgid "No admin user remaining, can't delete user"
|
||||
msgstr "Aucun utilisateur admin restant, impossible de supprimer l’utilisateur"
|
||||
|
||||
#: cps/admin.py:818
|
||||
#: cps/admin.py:829
|
||||
msgid "No admin user remaining, can't remove admin role"
|
||||
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."
|
||||
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
|
||||
msgid "Edit User %(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"
|
||||
msgstr "Cet utilisateur est déjà pris"
|
||||
|
||||
#: cps/admin.py:884
|
||||
#: cps/admin.py:895
|
||||
#, python-format
|
||||
msgid "User '%(nick)s' updated"
|
||||
msgstr "Utilisateur '%(nick)s' mis à jour"
|
||||
|
||||
#: cps/admin.py:887
|
||||
#: cps/admin.py:898
|
||||
msgid "An unknown error occured."
|
||||
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"
|
||||
msgstr "Modifier les paramètres du serveur de courriels"
|
||||
|
||||
#: cps/admin.py:946
|
||||
#: cps/admin.py:957
|
||||
#, python-format
|
||||
msgid "Test e-mail successfully send to %(kindlemail)s"
|
||||
msgstr "Courriel de test envoyé avec succès sur %(kindlemail)s"
|
||||
|
||||
#: cps/admin.py:949
|
||||
#: cps/admin.py:960
|
||||
#, python-format
|
||||
msgid "There was an error sending the Test e-mail: %(res)s"
|
||||
msgstr "Il y a eu une erreur pendant l’envoi du courriel de test : %(res)s"
|
||||
|
||||
#: cps/admin.py:951
|
||||
#: cps/admin.py:962
|
||||
msgid "Please configure your e-mail address first..."
|
||||
msgstr "Veuillez d'abord configurer votre adresse de courriel..."
|
||||
|
||||
#: cps/admin.py:953
|
||||
#: cps/admin.py:964
|
||||
msgid "E-mail server settings updated"
|
||||
msgstr "Les paramètres du serveur de courriels ont été mis à jour"
|
||||
|
||||
#: cps/admin.py:964
|
||||
#: cps/admin.py:975
|
||||
msgid "User not found"
|
||||
msgstr "L'utilisateur n'a pas été trouvé"
|
||||
|
||||
#: cps/admin.py:991
|
||||
#: cps/admin.py:1002
|
||||
#, python-format
|
||||
msgid "Password for user %(user)s reset"
|
||||
msgstr "Le mot de passe de l’utilisateur %(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."
|
||||
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..."
|
||||
msgstr "Veuillez configurer les paramètres SMTP au préalable..."
|
||||
|
||||
#: cps/admin.py:1009
|
||||
#: cps/admin.py:1020
|
||||
msgid "Logfile viewer"
|
||||
msgstr "Visualiseur de fichier journal"
|
||||
|
||||
#: cps/admin.py:1049
|
||||
#: cps/admin.py:1081
|
||||
msgid "Requesting update package"
|
||||
msgstr "Demande de mise à jour"
|
||||
|
||||
#: cps/admin.py:1050
|
||||
#: cps/admin.py:1082
|
||||
msgid "Downloading update package"
|
||||
msgstr "Téléchargement de la mise à jour"
|
||||
|
||||
#: cps/admin.py:1051
|
||||
#: cps/admin.py:1083
|
||||
msgid "Unzipping update package"
|
||||
msgstr "Décompression de la mise à jour"
|
||||
|
||||
#: cps/admin.py:1052
|
||||
#: cps/admin.py:1084
|
||||
msgid "Replacing files"
|
||||
msgstr "Remplacement des fichiers"
|
||||
|
||||
#: cps/admin.py:1053
|
||||
#: cps/admin.py:1085
|
||||
msgid "Database connections are closed"
|
||||
msgstr "Les connexions à la base de données ont été fermées"
|
||||
|
||||
#: cps/admin.py:1054
|
||||
#: cps/admin.py:1086
|
||||
msgid "Stopping server"
|
||||
msgstr "Arrêt du serveur"
|
||||
|
||||
#: cps/admin.py:1055
|
||||
#: cps/admin.py:1087
|
||||
msgid "Update finished, please press okay and reload page"
|
||||
msgstr "Mise à jour terminée, merci d’appuyer 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:1060
|
||||
#: cps/admin.py:1088 cps/admin.py:1089 cps/admin.py:1090 cps/admin.py:1091
|
||||
#: cps/admin.py:1092
|
||||
msgid "Update failed:"
|
||||
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"
|
||||
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"
|
||||
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"
|
||||
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"
|
||||
msgstr "Erreur générale"
|
||||
|
||||
#: cps/admin.py:1060
|
||||
#: cps/admin.py:1092
|
||||
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"
|
||||
|
||||
@ -335,12 +335,12 @@ msgstr "modifier les métadonnées"
|
||||
msgid "%(langname)s is not a valid language"
|
||||
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
|
||||
msgid "File extension '%(ext)s' is not allowed to be uploaded to this server"
|
||||
msgstr "L’extension de fichier '%(ext)s' n’est 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"
|
||||
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."
|
||||
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
|
||||
msgid "Database error: %(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"
|
||||
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"
|
||||
msgstr ""
|
||||
|
||||
#: cps/editbooks.py:709
|
||||
#: cps/editbooks.py:712
|
||||
msgid "Metadata successfully updated"
|
||||
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"
|
||||
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
|
||||
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"
|
||||
|
||||
#: cps/editbooks.py:790
|
||||
#: cps/editbooks.py:793
|
||||
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: "
|
||||
|
||||
#: cps/editbooks.py:878
|
||||
#: cps/editbooks.py:881
|
||||
#, python-format
|
||||
msgid "Failed to Move Cover File %(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
|
||||
msgid "File %(file)s uploaded"
|
||||
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"
|
||||
msgstr "Le format de conversion de la source ou de la destination est manquant"
|
||||
|
||||
#: cps/editbooks.py:923
|
||||
#: cps/editbooks.py:926
|
||||
#, python-format
|
||||
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"
|
||||
|
||||
#: cps/editbooks.py:927
|
||||
#: cps/editbooks.py:930
|
||||
#, python-format
|
||||
msgid "There was an error converting this book: %(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 n’est pas terminée, essayez de désa
|
||||
msgid "Callback domain is not verified, please follow steps to verify domain in google developer console"
|
||||
msgstr "Le domaine de retour d’appel (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
|
||||
msgid "%(format)s format not found for book id: %(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
|
||||
msgid "%(format)s not found on 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
|
||||
msgid "%(format)s not found: %(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
|
||||
msgid "Send to 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."
|
||||
msgstr "Ce courriel a été envoyé depuis Calibre-Web."
|
||||
|
||||
#: cps/helper.py:110
|
||||
#: cps/helper.py:113
|
||||
msgid "Calibre-Web test e-mail"
|
||||
msgstr "Courriel de test de Calibre-Web"
|
||||
|
||||
#: cps/helper.py:111
|
||||
#: cps/helper.py:114
|
||||
msgid "Test e-mail"
|
||||
msgstr "Courriel de test"
|
||||
|
||||
#: cps/helper.py:128
|
||||
#: cps/helper.py:131
|
||||
msgid "Get Started with Calibre-Web"
|
||||
msgstr "Bien démarrer avec Calibre-Web"
|
||||
|
||||
#: cps/helper.py:133
|
||||
#: cps/helper.py:136
|
||||
#, python-format
|
||||
msgid "Registration e-mail for user: %(name)s"
|
||||
msgstr "Courriel d’inscription pour l’utilisateur : %(name)s"
|
||||
|
||||
#: cps/helper.py:153 cps/helper.py:157 cps/helper.py:161 cps/helper.py:170
|
||||
#: cps/helper.py:174 cps/helper.py:178
|
||||
#: cps/helper.py:156 cps/helper.py:160 cps/helper.py:164 cps/helper.py:173
|
||||
#: cps/helper.py:177 cps/helper.py:181
|
||||
#, python-format
|
||||
msgid "Send %(format)s to 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
|
||||
msgid "Convert %(orig)s to %(format)s and send to Kindle"
|
||||
msgstr "Convertir de %(orig)s vers %(format)s et envoyer au Kindle"
|
||||
|
||||
#: cps/helper.py:230
|
||||
#: cps/helper.py:233
|
||||
#, python-format
|
||||
msgid "E-mail: %(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?"
|
||||
msgstr "Le fichier demandé n’a pu être lu. Problème de permission d’accès ?"
|
||||
|
||||
#: cps/helper.py:329
|
||||
#: cps/helper.py:332
|
||||
#, python-format
|
||||
msgid "Deleting bookfolder for book %(id)s failed, path has subfolders: %(path)s"
|
||||
msgstr ""
|
||||
|
||||
#: cps/helper.py:335
|
||||
#: cps/helper.py:338
|
||||
#, python-format
|
||||
msgid "Deleting book %(id)s failed: %(message)s"
|
||||
msgstr "La suppression du livre %(id)s a échoué: %(message)s"
|
||||
|
||||
#: cps/helper.py:345
|
||||
#: cps/helper.py:348
|
||||
#, python-format
|
||||
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"
|
||||
|
||||
#: cps/helper.py:400
|
||||
#: cps/helper.py:403
|
||||
#, python-format
|
||||
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 l’erreur : %(error)s"
|
||||
|
||||
#: cps/helper.py:415
|
||||
#: cps/helper.py:418
|
||||
#, python-format
|
||||
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 l’erreur : %(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
|
||||
msgid "File %(file)s not found on 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
|
||||
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"
|
||||
|
||||
#: cps/helper.py:588
|
||||
#: cps/helper.py:591
|
||||
msgid "Error Downloading Cover"
|
||||
msgstr "Erreur lors du téléchargement de la couverture"
|
||||
|
||||
#: cps/helper.py:591
|
||||
#: cps/helper.py:594
|
||||
msgid "Cover Format Error"
|
||||
msgstr "Erreur de format de couverture"
|
||||
|
||||
#: cps/helper.py:606
|
||||
#: cps/helper.py:609
|
||||
msgid "Failed to create path for cover"
|
||||
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"
|
||||
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"
|
||||
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"
|
||||
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"
|
||||
msgstr "Fichier binaire Unrar non trouvé"
|
||||
|
||||
#: cps/helper.py:698
|
||||
#: cps/helper.py:701
|
||||
msgid "Error excecuting UnRar"
|
||||
msgstr "Une erreur est survenue lors de l'exécution d'UnRar"
|
||||
|
||||
#: cps/helper.py:747
|
||||
#: cps/helper.py:750
|
||||
msgid "Waiting"
|
||||
msgstr "En attente"
|
||||
|
||||
#: cps/helper.py:749
|
||||
#: cps/helper.py:752
|
||||
msgid "Failed"
|
||||
msgstr "Echoué"
|
||||
|
||||
#: cps/helper.py:751
|
||||
#: cps/helper.py:754
|
||||
msgid "Started"
|
||||
msgstr "Débuté"
|
||||
|
||||
#: cps/helper.py:753
|
||||
#: cps/helper.py:756
|
||||
msgid "Finished"
|
||||
msgstr "Terminé"
|
||||
|
||||
#: cps/helper.py:755
|
||||
#: cps/helper.py:758
|
||||
msgid "Unknown Status"
|
||||
msgstr "Statut inconnu"
|
||||
|
||||
@ -857,7 +857,7 @@ msgstr "Livres archivés"
|
||||
msgid "Show archived books"
|
||||
msgstr "Afficher les livres archivés"
|
||||
|
||||
#: cps/ub.py:120
|
||||
#: cps/ub.py:120 cps/web.py:1001
|
||||
msgid "Books List"
|
||||
msgstr ""
|
||||
|
||||
@ -986,10 +986,6 @@ msgstr "Recherche avancée"
|
||||
msgid "Search"
|
||||
msgstr "Chercher"
|
||||
|
||||
#: cps/web.py:1001
|
||||
msgid "Books list"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1139
|
||||
msgid "Ratings list"
|
||||
msgstr "Liste des évaluations"
|
||||
@ -1299,60 +1295,64 @@ msgstr "Éditer la configuration principale"
|
||||
msgid "Edit UI Configuration"
|
||||
msgstr "Configuration de l’interface utilisateur"
|
||||
|
||||
#: cps/templates/admin.html:137
|
||||
#: cps/templates/admin.html:136
|
||||
msgid "Administration"
|
||||
msgstr "Administration"
|
||||
|
||||
#: cps/templates/admin.html:137
|
||||
msgid "Download Debug Package"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/admin.html:138
|
||||
msgid "View Logs"
|
||||
msgstr "Afficher les fichiers journaux"
|
||||
|
||||
#: cps/templates/admin.html:139
|
||||
#: cps/templates/admin.html:141
|
||||
msgid "Reconnect Calibre Database"
|
||||
msgstr "Reconnecter la base de données Calibre"
|
||||
|
||||
#: cps/templates/admin.html:140
|
||||
#: cps/templates/admin.html:142
|
||||
msgid "Restart"
|
||||
msgstr "Redémarrer Calibre-Web"
|
||||
|
||||
#: cps/templates/admin.html:141
|
||||
#: cps/templates/admin.html:143
|
||||
msgid "Shutdown"
|
||||
msgstr "Arrêter Calibre-Web"
|
||||
|
||||
#: cps/templates/admin.html:147
|
||||
#: cps/templates/admin.html:149
|
||||
msgid "Update"
|
||||
msgstr "Mise à jour de Calibre-Web"
|
||||
|
||||
#: cps/templates/admin.html:151
|
||||
#: cps/templates/admin.html:153
|
||||
msgid "Version"
|
||||
msgstr "Version"
|
||||
|
||||
#: cps/templates/admin.html:152
|
||||
#: cps/templates/admin.html:154
|
||||
msgid "Details"
|
||||
msgstr "Détails"
|
||||
|
||||
#: cps/templates/admin.html:158
|
||||
#: cps/templates/admin.html:160
|
||||
msgid "Current version"
|
||||
msgstr "Version actuelle"
|
||||
|
||||
#: cps/templates/admin.html:164
|
||||
#: cps/templates/admin.html:166
|
||||
msgid "Check for Update"
|
||||
msgstr "Rechercher les mises à jour"
|
||||
|
||||
#: cps/templates/admin.html:165
|
||||
#: cps/templates/admin.html:167
|
||||
msgid "Perform Update"
|
||||
msgstr "Effectuer la mise à jour"
|
||||
|
||||
#: cps/templates/admin.html:177
|
||||
#: cps/templates/admin.html:179
|
||||
msgid "Are you sure you want to restart?"
|
||||
msgstr "Voulez-vous vraiment redémarrer Calibre-Web?"
|
||||
|
||||
#: cps/templates/admin.html:182 cps/templates/admin.html:196
|
||||
#: cps/templates/admin.html:216 cps/templates/shelf.html:80
|
||||
#: cps/templates/admin.html:184 cps/templates/admin.html:198
|
||||
#: cps/templates/admin.html:218 cps/templates/shelf.html:80
|
||||
msgid "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/config_edit.html:391 cps/templates/config_view_edit.html:151
|
||||
#: cps/templates/email_edit.html:47 cps/templates/email_edit.html:101
|
||||
@ -1361,11 +1361,11 @@ msgstr "OK"
|
||||
msgid "Cancel"
|
||||
msgstr "Annuler"
|
||||
|
||||
#: cps/templates/admin.html:195
|
||||
#: cps/templates/admin.html:197
|
||||
msgid "Are you sure you want to shutdown?"
|
||||
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"
|
||||
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: "
|
||||
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
|
||||
msgid "Select Allowed/Denied Tags"
|
||||
msgstr "Sélectionner les étiquettes autorisées/refusées"
|
||||
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
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
64
cps/web.py
64
cps/web.py
@ -323,31 +323,34 @@ def import_ldap_users():
|
||||
showtext['text'] = _(u'Error: No user returned in response of LDAP server')
|
||||
return json.dumps(showtext)
|
||||
|
||||
imported = 0
|
||||
for username in new_users:
|
||||
user = username.decode('utf-8')
|
||||
if '=' in user:
|
||||
match = re.search("([a-zA-Z0-9-]+)=%s", config.config_ldap_user_object, re.IGNORECASE | re.UNICODE)
|
||||
if match:
|
||||
match_filter = match.group(1)
|
||||
match = re.search(match_filter + "=([\d\s\w-]+)", user, re.IGNORECASE | re.UNICODE)
|
||||
if match:
|
||||
user = match.group(1)
|
||||
# if member object field is empty take user object as filter
|
||||
try:
|
||||
if config.config_ldap_member_user_object:
|
||||
user_identifier = extract_user_identifier(user, config.config_ldap_member_user_object)
|
||||
else:
|
||||
log.warning("Could Not Parse LDAP User: %s", user)
|
||||
continue
|
||||
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
|
||||
if ub.session.query(ub.User).filter(ub.User.nickname == user.lower()).first():
|
||||
log.warning("LDAP User: %s Already in Database", user)
|
||||
else:
|
||||
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
|
||||
user_data = services.ldap.get_object_details(user=user,
|
||||
user_data = services.ldap.get_object_details(user=user_identifier,
|
||||
group=None,
|
||||
query_filter=None,
|
||||
dn_only=False)
|
||||
if user_data:
|
||||
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
|
||||
if 'mail' in user_data:
|
||||
content.email = user_data['mail'][0].decode('utf-8')
|
||||
@ -365,6 +368,7 @@ def import_ldap_users():
|
||||
ub.session.add(content)
|
||||
try:
|
||||
ub.session.commit()
|
||||
imported +=1
|
||||
except Exception as e:
|
||||
log.warning("Failed to create LDAP user: %s - %s", user, e)
|
||||
ub.session.rollback()
|
||||
@ -373,10 +377,28 @@ def import_ldap_users():
|
||||
log.warning("LDAP User: %s Not Found", user)
|
||||
showtext['text'] = _(u'At Least One LDAP User Not Found in Database')
|
||||
if not showtext:
|
||||
showtext['text'] = _(u'User Successfully Imported')
|
||||
showtext['text'] = _(u'{} User Successfully Imported'.format(imported))
|
||||
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 #########################################################
|
||||
|
||||
|
||||
@ -631,6 +653,10 @@ def render_books_list(data, sort, book_id, page):
|
||||
order = [db.Books.author_sort.asc()]
|
||||
if sort == 'authza':
|
||||
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 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,
|
||||
db.Books,
|
||||
db.Books.ratings.any(db.Ratings.id == book_id),
|
||||
[db.Books.timestamp.desc(), order[0]])
|
||||
[order[0]])
|
||||
if name and name.rating <= 10:
|
||||
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")
|
||||
@ -827,7 +853,7 @@ def render_formats_books(page, book_id, order):
|
||||
entries, random, pagination = calibre_db.fill_indexpage(page, 0,
|
||||
db.Books,
|
||||
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,
|
||||
title=_(u"File format: %(format)s", format=name.format), page="formats")
|
||||
else:
|
||||
@ -860,7 +886,7 @@ def render_language_books(page, name, order):
|
||||
entries, random, pagination = calibre_db.fill_indexpage(page, 0,
|
||||
db.Books,
|
||||
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,
|
||||
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
|
||||
def books_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)
|
||||
|
||||
@web.route("/ajax/listbooks")
|
||||
|
400
messages.pot
400
messages.pot
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
Loading…
Reference in New Issue
Block a user