mirror of
https://github.com/janeczku/calibre-web
synced 2024-11-24 18:47:23 +00:00
Merge branch 'master' into Develop
# Conflicts: # cps/editbooks.py
This commit is contained in:
commit
48f4b12c0e
@ -36,10 +36,6 @@ from flask_principal import Principal
|
||||
from . import config_sql, logger, cache_buster, cli, ub, db
|
||||
from .reverseproxy import ReverseProxied
|
||||
from .server import WebServer
|
||||
try:
|
||||
from werkzeug.middleware.proxy_fix import ProxyFix
|
||||
except ImportError:
|
||||
from werkzeug.contrib.fixers import ProxyFix
|
||||
|
||||
mimetypes.init()
|
||||
mimetypes.add_type('application/xhtml+xml', '.xhtml')
|
||||
@ -80,10 +76,7 @@ log = logger.create()
|
||||
from . import services
|
||||
|
||||
def create_app():
|
||||
try:
|
||||
app.wsgi_app = ReverseProxied(ProxyFix(app.wsgi_app, x_for=1, x_host=1))
|
||||
except (ValueError, TypeError):
|
||||
app.wsgi_app = ReverseProxied(ProxyFix(app.wsgi_app))
|
||||
app.wsgi_app = ReverseProxied(app.wsgi_app)
|
||||
# For python2 convert path to unicode
|
||||
if sys.version_info < (3, 0):
|
||||
app.static_folder = app.static_folder.decode('utf-8')
|
||||
@ -95,7 +88,7 @@ def create_app():
|
||||
log.info('Starting Calibre Web...')
|
||||
Principal(app)
|
||||
lm.init_app(app)
|
||||
app.secret_key = os.getenv('SECRET_KEY', 'A0Zr98j/3yX R~XHH!jmN]LWX/,?RT')
|
||||
app.secret_key = os.getenv('SECRET_KEY', config_sql.get_flask_session_key(ub.session))
|
||||
|
||||
web_server.init_app(app, config)
|
||||
db.setup_db(config)
|
||||
|
@ -22,7 +22,7 @@ import os
|
||||
import json
|
||||
import sys
|
||||
|
||||
from sqlalchemy import exc, Column, String, Integer, SmallInteger, Boolean
|
||||
from sqlalchemy import exc, Column, String, Integer, SmallInteger, Boolean, BLOB
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
|
||||
from . import constants, cli, logger, ub
|
||||
@ -31,6 +31,15 @@ from . import constants, cli, logger, ub
|
||||
log = logger.create()
|
||||
_Base = declarative_base()
|
||||
|
||||
class _Flask_Settings(_Base):
|
||||
__tablename__ = 'flask_settings'
|
||||
|
||||
id = Column(Integer, primary_key=True)
|
||||
flask_session_key = Column(BLOB, default="")
|
||||
|
||||
def __init__(self, key):
|
||||
self.flask_session_key = key
|
||||
|
||||
|
||||
# Baseclass for representing settings in app.db with email server settings and Calibre database settings
|
||||
# (application settings)
|
||||
@ -304,7 +313,7 @@ def _migrate_table(session, orm_class):
|
||||
log.debug("%s: %s", column_name, err.args[0])
|
||||
if column.default is not None:
|
||||
if sys.version_info < (3, 0):
|
||||
if isinstance(column.default.arg,unicode):
|
||||
if isinstance(column.default.arg, unicode):
|
||||
column.default.arg = column.default.arg.encode('utf-8')
|
||||
if column.default is None:
|
||||
column_default = ""
|
||||
@ -340,6 +349,7 @@ def _migrate_database(session):
|
||||
# make sure the table is created, if it does not exist
|
||||
_Base.metadata.create_all(session.bind)
|
||||
_migrate_table(session, _Settings)
|
||||
_migrate_table(session, _Flask_Settings)
|
||||
|
||||
|
||||
def load_configuration(session):
|
||||
@ -357,3 +367,11 @@ def load_configuration(session):
|
||||
update({"denied_tags": conf.config_mature_content_tags}, synchronize_session=False)
|
||||
session.commit()
|
||||
return conf
|
||||
|
||||
def get_flask_session_key(session):
|
||||
flask_settings = session.query(_Flask_Settings).one_or_none()
|
||||
if flask_settings == None:
|
||||
flask_settings = _Flask_Settings(os.urandom(32))
|
||||
session.add(flask_settings)
|
||||
session.commit()
|
||||
return flask_settings.flask_session_key
|
||||
|
@ -127,7 +127,7 @@ def selected_roles(dictionary):
|
||||
BookMeta = namedtuple('BookMeta', 'file_path, extension, title, author, cover, description, tags, series, '
|
||||
'series_id, languages')
|
||||
|
||||
STABLE_VERSION = {'version': '0.6.7 Beta'}
|
||||
STABLE_VERSION = {'version': '0.6.8 Beta'}
|
||||
|
||||
NIGHTLY_VERSION = {}
|
||||
NIGHTLY_VERSION[0] = '$Format:%H$'
|
||||
|
@ -30,6 +30,7 @@ from uuid import uuid4
|
||||
from flask import Blueprint, request, flash, redirect, url_for, abort, Markup, Response
|
||||
from flask_babel import gettext as _
|
||||
from flask_login import current_user, login_required
|
||||
from sqlalchemy.exc import OperationalError
|
||||
|
||||
from . import constants, logger, isoLanguages, gdriveutils, uploader, helper
|
||||
from . import config, get_locale, db, ub, worker
|
||||
@ -444,10 +445,16 @@ def upload_single_file(request, book, book_id):
|
||||
if is_format:
|
||||
log.warning('Book format %s already existing', file_ext.upper())
|
||||
else:
|
||||
try:
|
||||
db_format = db.Data(book_id, file_ext.upper(), file_size, file_name)
|
||||
db.session.add(db_format)
|
||||
db.session.commit()
|
||||
db.update_title_sort(config)
|
||||
except OperationalError as e:
|
||||
db.session.rollback()
|
||||
log.error('Database error: %s', e)
|
||||
flash(_(u"Database error: %(error)s.", error=e), category="error")
|
||||
return redirect(url_for('web.show_book', book_id=book.id))
|
||||
|
||||
# Queue uploader info
|
||||
uploadText=_(u"File format %(ext)s added to %(book)s", ext=file_ext.upper(), book=book.title)
|
||||
@ -455,7 +462,8 @@ def upload_single_file(request, book, book_id):
|
||||
"<a href=\"" + url_for('web.show_book', book_id=book.id) + "\">" + uploadText + "</a>")
|
||||
|
||||
return uploader.process(
|
||||
saved_filename, *os.path.splitext(requested_file.filename))
|
||||
saved_filename, *os.path.splitext(requested_file.filename),
|
||||
rarExcecutable=config.config_rarfile_location)
|
||||
|
||||
|
||||
def upload_cover(request, book):
|
||||
@ -653,6 +661,7 @@ def upload():
|
||||
abort(404)
|
||||
if request.method == 'POST' and 'btn-upload' in request.files:
|
||||
for requested_file in request.files.getlist("btn-upload"):
|
||||
try:
|
||||
# create the function for sorting...
|
||||
db.update_title_sort(config)
|
||||
db.session.connection().connection.connection.create_function('uuid4', 0, lambda: str(uuid4()))
|
||||
@ -714,16 +723,11 @@ def upload():
|
||||
return Response(json.dumps({"location": url_for("web.index")}), mimetype='application/json')
|
||||
try:
|
||||
copyfile(meta.file_path, saved_filename)
|
||||
except OSError:
|
||||
log.error("Failed to store file %s (Permission denied)", saved_filename)
|
||||
flash(_(u"Failed to store file %(file)s (Permission denied).", file=saved_filename), category="error")
|
||||
return Response(json.dumps({"location": url_for("web.index")}), mimetype='application/json')
|
||||
try:
|
||||
os.unlink(meta.file_path)
|
||||
except OSError:
|
||||
log.error("Failed to delete file %(file)s (Permission denied)", meta.file_path)
|
||||
flash(_(u"Failed to delete file %(file)s (Permission denied).", file= meta.file_path),
|
||||
category="warning")
|
||||
except OSError as e:
|
||||
log.error("Failed to move file %s: %s", saved_filename, e)
|
||||
flash(_(u"Failed to Move File %(file)s: %(error)s", file=saved_filename, error=e), category="error")
|
||||
return Response(json.dumps({"location": url_for("web.index")}), mimetype='application/json')
|
||||
|
||||
if meta.cover is None:
|
||||
has_cover = 0
|
||||
@ -802,7 +806,15 @@ def upload():
|
||||
|
||||
# move cover to final directory, including book id
|
||||
if has_cover:
|
||||
move(meta.cover, os.path.join(filepath+ ' ({})'.format(book_id), "cover.jpg"))
|
||||
try:
|
||||
new_coverpath = os.path.join(filepath+ ' ({})'.format(book_id), "cover.jpg")
|
||||
copyfile(meta.cover, new_coverpath)
|
||||
os.unlink(meta.cover)
|
||||
except OSError as e:
|
||||
log.error("Failed to move cover file %s: %s", new_coverpath, e)
|
||||
flash(_(u"Failed to Move Cover File %(file)s: %(error)s", file=new_coverpath,
|
||||
error=e),
|
||||
category="error")
|
||||
db.session.commit()
|
||||
if config.config_use_google_drive:
|
||||
gdriveutils.updateGdriveCalibreFromLocal()
|
||||
@ -825,9 +837,12 @@ def upload():
|
||||
else:
|
||||
resp = {"location": url_for('web.show_book', book_id=db_book.id)}
|
||||
return Response(json.dumps(resp), mimetype='application/json')
|
||||
except OperationalError as e:
|
||||
db.session.rollback()
|
||||
log.error("Database error: %s", e)
|
||||
flash(_(u"Database error: %(error)s.", error=e), category="error")
|
||||
return Response(json.dumps({"location": url_for("web.index")}), mimetype='application/json')
|
||||
|
||||
|
||||
@editbook.route("/admin/book/convert/<int:book_id>", methods=['POST'])
|
||||
@login_required_if_no_ano
|
||||
@edit_required
|
||||
|
@ -291,6 +291,7 @@ def delete_book_file(book, calibrepath, book_format=None):
|
||||
for file in os.listdir(path):
|
||||
if file.upper().endswith("."+book_format):
|
||||
os.remove(os.path.join(path, file))
|
||||
return True, None
|
||||
else:
|
||||
if os.path.isdir(path):
|
||||
if len(next(os.walk(path))[1]):
|
||||
|
@ -90,15 +90,15 @@
|
||||
<label for="config_port">{{_('Server Port')}}</label>
|
||||
<input type="number" min="1" max="65535" class="form-control" name="config_port" id="config_port" value="{% if config.config_port != None %}{{ config.config_port }}{% endif %}" autocomplete="off" required>
|
||||
</div>
|
||||
<label for="config_certfile">{{_('SSL certfile location (leave it empty for non-SSL Servers)')}}</label>
|
||||
<div class="form-group input-group">
|
||||
<label for="config_certfile" class="sr-only">{{_('SSL certfile location (leave it empty for non-SSL Servers)')}}</label>
|
||||
<input type="text" class="form-control" id="config_certfile" name="config_certfile" value="{% if config.config_certfile != None %}{{ config.config_certfile }}{% endif %}" autocomplete="off">
|
||||
<span class="input-group-btn">
|
||||
<button type="button" id="certfile_path" class="btn btn-default"><span class="glyphicon glyphicon-folder-open"></span></button>
|
||||
</span>
|
||||
</div>
|
||||
<label for="config_calibre_dir" >{{_('SSL Keyfile location (leave it empty for non-SSL Servers)')}}</label>
|
||||
<div class="form-group input-group">
|
||||
<label for="config_calibre_dir" class="sr-only">{{_('SSL Keyfile location (leave it empty for non-SSL Servers)')}}</label>
|
||||
<input type="text" class="form-control" id="config_keyfile" name="config_keyfile" value="{% if config.config_keyfile != None %}{{ config.config_keyfile }}{% endif %}" autocomplete="off">
|
||||
<span class="input-group-btn">
|
||||
<button type="button" id="keyfile_path" class="btn btn-default"><span class="glyphicon glyphicon-folder-open"></span></button>
|
||||
@ -349,23 +349,23 @@
|
||||
<label for="config_calibre">{{_('Calibre E-Book Converter Settings')}}</label>
|
||||
<input type="text" class="form-control" id="config_calibre" name="config_calibre" value="{% if config.config_calibre != None %}{{ config.config_calibre }}{% endif %}" autocomplete="off">
|
||||
</div>
|
||||
<label for="config_converterpath">{{_('Path to Calibre E-Book Converter')}}</label>
|
||||
<div class="form-group input-group">
|
||||
<label for="config_converterpath" class="sr-only">{{_('Path to Calibre E-Book Converter')}}</label>
|
||||
<input type="text" class="form-control" id="config_converterpath" name="config_converterpath" value="{% if config.config_converterpath != None %}{{ config.config_converterpath }}{% endif %}" autocomplete="off">
|
||||
<span class="input-group-btn">
|
||||
<button type="button" id="converter_path" class="btn btn-default"><span class="glyphicon glyphicon-folder-open"></span></button>
|
||||
</span>
|
||||
</div>
|
||||
<label for="config_kepubifypath">{{_('Path to Kepubify E-Book Converter')}}</label>
|
||||
<div class="form-group input-group">
|
||||
<label for="config_kepubifypath" class="sr-only">{{_('Path to Kepubify E-Book Converter')}}</label>
|
||||
<input type="text" class="form-control" id="config_kepubifypath" name="config_kepubifypath" value="{% if config.config_kepubifypath != None %}{{ config.config_kepubifypath }}{% endif %}" autocomplete="off">
|
||||
<span class="input-group-btn">
|
||||
<button type="button" id="kepubify_path" class="btn btn-default"><span class="glyphicon glyphicon-folder-open"></span></button>
|
||||
</span>
|
||||
</div>
|
||||
{% if feature_support['rar'] %}
|
||||
<label for="config_rarfile_location">{{_('Location of Unrar binary')}}</label>
|
||||
<div class="form-group input-group">
|
||||
<label for="config_rarfile_location" class="sr-only">{{_('Location of Unrar binary')}}</label>
|
||||
<input type="text" class="form-control" id="config_rarfile_location" name="config_rarfile_location" value="{% if config.config_rarfile_location != None %}{{ config.config_rarfile_location }}{% endif %}" autocomplete="off">
|
||||
<span class="input-group-btn">
|
||||
<button type="button" id="unrar_path" class="btn btn-default"><span class="glyphicon glyphicon-folder-open"></span></button>
|
||||
|
@ -6,8 +6,8 @@
|
||||
{% block body %}
|
||||
<div class="discover">
|
||||
<h2>{{title}}</h2>
|
||||
<form role="form" method="POST" autocomplete="off">
|
||||
<div class="panel-group col-md-10 col-lg-6">
|
||||
<form role="form" method="POST" autocomplete="off" class="col-md-10 col-lg-6">
|
||||
<div class="panel-group">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h4 class="panel-title">
|
||||
|
@ -35,8 +35,8 @@
|
||||
<label for="mail_from">{{_('From E-mail')}}</label>
|
||||
<input type="text" class="form-control" name="mail_from" id="mail_from" value="{{content.mail_from}}">
|
||||
</div>
|
||||
<label for="mail_size">{{_('Attachment Size Limit')}}</label>
|
||||
<div class="form-group input-group">
|
||||
<label for="mail_size" class="sr-only">{{_('Attachment Size Limit')}}</label>
|
||||
<input type="number" min="1" max="600" class="form-control" name="attachment_size" id="mail_size" value="{% if config.mail_size != None %}{{ config.mail_size }}{% endif %}">
|
||||
<span class="input-group-btn">
|
||||
<button type="button" id="certfile_path" class="btn btn-default" disabled>MB</button>
|
||||
|
Binary file not shown.
@ -6,7 +6,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Calibre-Web\n"
|
||||
"Report-Msgid-Bugs-To: https://github.com/janeczku/Calibre-Web\n"
|
||||
"POT-Creation-Date: 2020-05-01 17:15+0200\n"
|
||||
"POT-Creation-Date: 2020-05-04 20:19+0200\n"
|
||||
"PO-Revision-Date: 2017-04-04 15:09+0200\n"
|
||||
"Last-Translator: ElQuimm <quimm@webtaste.com>\n"
|
||||
"Language: it\n"
|
||||
@ -172,7 +172,7 @@ msgstr "Configurazione del server e-mail aggiornata"
|
||||
|
||||
#: cps/admin.py:821
|
||||
msgid "User not found"
|
||||
msgstr ""
|
||||
msgstr "Utente non trovato"
|
||||
|
||||
#: cps/admin.py:842
|
||||
#, python-format
|
||||
@ -185,7 +185,7 @@ msgstr "Non rimarrebbe nessun utente amministratore, non posso eliminare l'utent
|
||||
|
||||
#: cps/admin.py:851
|
||||
msgid "No admin user remaining, can't remove admin role"
|
||||
msgstr ""
|
||||
msgstr "Non rimarrebbe nessun utente amministratore, non posso eliminare il ruolo di amministratore"
|
||||
|
||||
#: cps/admin.py:887 cps/web.py:1515
|
||||
msgid "Found an existing account for this e-mail address."
|
||||
@ -285,11 +285,11 @@ msgstr "non configurato"
|
||||
|
||||
#: cps/editbooks.py:239
|
||||
msgid "Book Format Successfully Deleted"
|
||||
msgstr ""
|
||||
msgstr "Il formato del libro è stato eliminato con successo"
|
||||
|
||||
#: cps/editbooks.py:242
|
||||
msgid "Book Successfully Deleted"
|
||||
msgstr ""
|
||||
msgstr "Il libro é stato eliminato con successo"
|
||||
|
||||
#: cps/editbooks.py:253 cps/editbooks.py:489
|
||||
msgid "Error opening eBook. File does not exist or file is not accessible"
|
||||
@ -321,12 +321,12 @@ msgstr "Impossibile creare la cartella %(path)s (autorizzazione negata)."
|
||||
#: cps/editbooks.py:434
|
||||
#, python-format
|
||||
msgid "Failed to store file %(file)s."
|
||||
msgstr "Il salvataggio del file %(file)s è fallito."
|
||||
msgstr "Il salvataggio del file %(file)s non è riuscito."
|
||||
|
||||
#: cps/editbooks.py:451
|
||||
#, python-format
|
||||
msgid "File format %(ext)s added to %(book)s"
|
||||
msgstr "Ho aggiunto l'estensione %(ext)s al libro %(book)s"
|
||||
msgstr "Ho aggiunto il formato %(ext)s al libro %(book)s"
|
||||
|
||||
#: cps/editbooks.py:606
|
||||
msgid "Metadata successfully updated"
|
||||
@ -362,7 +362,7 @@ msgstr "Il file %(file)s è stato caricato"
|
||||
|
||||
#: cps/editbooks.py:833
|
||||
msgid "Source or destination format for conversion missing"
|
||||
msgstr "Il formato sorgente o quello di destinazione, necessari alla conversione, mancano"
|
||||
msgstr "Mancano o il formato sorgente o quello di destinazione, necessari alla conversione"
|
||||
|
||||
#: cps/editbooks.py:841
|
||||
#, python-format
|
||||
@ -446,17 +446,17 @@ msgstr "Il file richiesto non può essere letto. I permessi sono corretti?"
|
||||
#: cps/helper.py:299
|
||||
#, python-format
|
||||
msgid "Deleting book %(id)s failed, path has subfolders: %(path)s"
|
||||
msgstr ""
|
||||
msgstr "L'eliminazione del libro %(id)s non è riuscita, poiché il percorso ha delle sottocartelle: %(path)s"
|
||||
|
||||
#: cps/helper.py:309
|
||||
#, python-format
|
||||
msgid "Deleting book %(id)s failed: %(message)s"
|
||||
msgstr ""
|
||||
msgstr "L'eliminazione del libro %(id)s non è riuscita: %(message)s"
|
||||
|
||||
#: cps/helper.py:319
|
||||
#, python-format
|
||||
msgid "Deleting book %(id)s failed, book path not valid: %(path)s"
|
||||
msgstr ""
|
||||
msgstr "L'eliminazione del libro %(id)s non è riuscita, poiché il percorso non è valido: %(path)s"
|
||||
|
||||
#: cps/helper.py:354
|
||||
#, python-format
|
||||
@ -489,7 +489,7 @@ msgstr "Errore nel creare la cartella per la copertina"
|
||||
|
||||
#: cps/helper.py:555
|
||||
msgid "Cover-file is not a valid image file, or could not be stored"
|
||||
msgstr ""
|
||||
msgstr "Il file della copertina non è in un formato immagine valido o non può essere salvato"
|
||||
|
||||
#: cps/helper.py:566
|
||||
msgid "Only jpg/jpeg/png/webp files are supported as coverfile"
|
||||
@ -501,11 +501,11 @@ msgstr "Solamente i file nei formati jpg/jpeg sono supportati per le copertine"
|
||||
|
||||
#: cps/helper.py:622
|
||||
msgid "Unrar binary file not found"
|
||||
msgstr ""
|
||||
msgstr "Non ho trovato il file binario di UnRar"
|
||||
|
||||
#: cps/helper.py:635
|
||||
msgid "Error excecuting UnRar"
|
||||
msgstr ""
|
||||
msgstr "Errore nell'eseguire UnRar"
|
||||
|
||||
#: cps/helper.py:691
|
||||
msgid "Waiting"
|
||||
@ -558,19 +558,19 @@ msgstr "Registra con %(provider)s"
|
||||
|
||||
#: cps/oauth_bb.py:154
|
||||
msgid "Failed to log in with GitHub."
|
||||
msgstr "Accesso con GitHub non riuscito."
|
||||
msgstr "Accesso con GitHub non è riuscito."
|
||||
|
||||
#: cps/oauth_bb.py:159
|
||||
msgid "Failed to fetch user info from GitHub."
|
||||
msgstr "Fallito il recupero delle informazioni dell'utente da GitHub."
|
||||
msgstr "Il recupero delle informazioni dell'utente da GitHub non è riuscito."
|
||||
|
||||
#: cps/oauth_bb.py:170
|
||||
msgid "Failed to log in with Google."
|
||||
msgstr "Fallito l'accesso con Google."
|
||||
msgstr "L'accesso con Google non è riuscito."
|
||||
|
||||
#: cps/oauth_bb.py:175
|
||||
msgid "Failed to fetch user info from Google."
|
||||
msgstr "Fallito il recupero delle informazioni dell'utente da Google."
|
||||
msgstr "Il recupero delle informazioni dell'utente da Google non è riuscito."
|
||||
|
||||
#: cps/oauth_bb.py:225 cps/web.py:1291 cps/web.py:1431
|
||||
#, python-format
|
||||
@ -584,7 +584,7 @@ msgstr "Collegamento a %(oauth)s avvenuto con successo"
|
||||
|
||||
#: cps/oauth_bb.py:241
|
||||
msgid "Login failed, No User Linked With OAuth Account"
|
||||
msgstr "Accesso fallito, non c'è un utente collegato all'account OAuth"
|
||||
msgstr "Accesso non riuscito, non c'è un utente collegato all'account OAuth"
|
||||
|
||||
#: cps/oauth_bb.py:283
|
||||
#, python-format
|
||||
@ -594,7 +594,7 @@ msgstr "Scollegamento da %(oauth)s avvenuto con successo"
|
||||
#: cps/oauth_bb.py:287
|
||||
#, python-format
|
||||
msgid "Unlink to %(oauth)s Failed"
|
||||
msgstr "Scollegamento da %(oauth)s fallito"
|
||||
msgstr "Scollegamento da %(oauth)s non riuscito"
|
||||
|
||||
#: cps/oauth_bb.py:290
|
||||
#, python-format
|
||||
@ -818,11 +818,11 @@ msgstr "Mostra la selezione del formato dei file"
|
||||
|
||||
#: cps/ub.py:107 cps/web.py:1150
|
||||
msgid "Archived Books"
|
||||
msgstr ""
|
||||
msgstr "Libri archiviati"
|
||||
|
||||
#: cps/ub.py:109
|
||||
msgid "Show archived books"
|
||||
msgstr ""
|
||||
msgstr "Mostra l'opzione per la selezione dei libri archiviati"
|
||||
|
||||
#: cps/updater.py:294 cps/updater.py:305 cps/updater.py:406 cps/updater.py:420
|
||||
msgid "Unexpected data while reading update information"
|
||||
@ -1360,23 +1360,23 @@ msgstr "Descrizione"
|
||||
|
||||
#: cps/templates/book_edit.html:66
|
||||
msgid "Identifiers"
|
||||
msgstr ""
|
||||
msgstr "Identificatori"
|
||||
|
||||
#: cps/templates/book_edit.html:70 cps/templates/book_edit.html:308
|
||||
msgid "Identifier Type"
|
||||
msgstr ""
|
||||
msgstr "Tipo di identificatore"
|
||||
|
||||
#: cps/templates/book_edit.html:71 cps/templates/book_edit.html:309
|
||||
msgid "Identifier Value"
|
||||
msgstr ""
|
||||
msgstr "Valore dell'identificatore"
|
||||
|
||||
#: cps/templates/book_edit.html:72 cps/templates/book_edit.html:310
|
||||
msgid "Remove"
|
||||
msgstr ""
|
||||
msgstr "Rimuovi"
|
||||
|
||||
#: cps/templates/book_edit.html:76
|
||||
msgid "Add Identifier"
|
||||
msgstr ""
|
||||
msgstr "Aggiungi un identificatore"
|
||||
|
||||
#: cps/templates/book_edit.html:80 cps/templates/search_form.html:33
|
||||
msgid "Tags"
|
||||
@ -1453,11 +1453,11 @@ msgstr "e dal disco rigido"
|
||||
|
||||
#: cps/templates/book_edit.html:209
|
||||
msgid "Important Kobo Note: deleted books will remain on any paired Kobo device."
|
||||
msgstr ""
|
||||
msgstr "Oservazione importante riguardo Kobo: i libri eliminati, rimarranno in ogni lettore Kobo accoppiato."
|
||||
|
||||
#: cps/templates/book_edit.html:210
|
||||
msgid "Books must first be archived and the device synced before a book can safely be deleted."
|
||||
msgstr ""
|
||||
msgstr "Prima di poter elimnare in sicurezza un libro, prima occorre che il libro venga archiviato e che l'apparecchio venga sincronizzato."
|
||||
|
||||
#: cps/templates/book_edit.html:232
|
||||
msgid "Keyword"
|
||||
@ -1775,7 +1775,7 @@ msgstr "Percorso del convertitore"
|
||||
|
||||
#: cps/templates/config_edit.html:349
|
||||
msgid "Location of Unrar binary"
|
||||
msgstr "Percorso di UnRar"
|
||||
msgstr "Percorso del file binario di UnRar"
|
||||
|
||||
#: cps/templates/config_edit.html:368 cps/templates/layout.html:84
|
||||
#: cps/templates/login.html:4 cps/templates/login.html:20
|
||||
@ -1912,15 +1912,15 @@ msgstr "da leggere"
|
||||
|
||||
#: cps/templates/detail.html:208
|
||||
msgid "Restore from archive"
|
||||
msgstr ""
|
||||
msgstr "Ripristina dall'archivio"
|
||||
|
||||
#: cps/templates/detail.html:208
|
||||
msgid "Add to archive"
|
||||
msgstr ""
|
||||
msgstr "Aggiungi all'archivio"
|
||||
|
||||
#: cps/templates/detail.html:209
|
||||
msgid "Archived"
|
||||
msgstr ""
|
||||
msgstr "Archiviato"
|
||||
|
||||
#: cps/templates/detail.html:219
|
||||
msgid "Description:"
|
||||
@ -2252,7 +2252,7 @@ msgstr "Scuro"
|
||||
|
||||
#: cps/templates/readcbr.html:121
|
||||
msgid "Scale"
|
||||
msgstr "Adatta"
|
||||
msgstr "Scala"
|
||||
|
||||
#: cps/templates/readcbr.html:124
|
||||
msgid "Best"
|
||||
@ -2396,7 +2396,7 @@ msgstr "Cambia ordine"
|
||||
|
||||
#: cps/templates/shelf.html:67
|
||||
msgid "Are you sure you want to delete this shelf?"
|
||||
msgstr "Vuoi davvero eliminare lo scaffale?"
|
||||
msgstr "Vuoi davvero eliminare questo scaffale?"
|
||||
|
||||
#: cps/templates/shelf.html:70
|
||||
msgid "Shelf will be deleted for all users"
|
||||
|
21
cps/web.py
21
cps/web.py
@ -37,9 +37,9 @@ from flask import Blueprint
|
||||
from flask import render_template, request, redirect, send_from_directory, make_response, g, flash, abort, url_for
|
||||
from flask_babel import gettext as _
|
||||
from flask_login import login_user, logout_user, login_required, current_user
|
||||
from sqlalchemy.exc import IntegrityError, InvalidRequestError
|
||||
from sqlalchemy.exc import IntegrityError, InvalidRequestError, OperationalError
|
||||
from sqlalchemy.sql.expression import text, func, true, false, not_, and_, or_
|
||||
from werkzeug.exceptions import default_exceptions
|
||||
from werkzeug.exceptions import default_exceptions, InternalServerError
|
||||
try:
|
||||
from werkzeug.exceptions import FailedDependency
|
||||
except ImportError:
|
||||
@ -119,9 +119,16 @@ for ex in default_exceptions:
|
||||
if feature_support['ldap']:
|
||||
# Only way of catching the LDAPException upon logging in with LDAP server down
|
||||
@app.errorhandler(services.ldap.LDAPException)
|
||||
def handle_exception(e):
|
||||
log.debug('LDAP server not accessible while trying to login to opds feed')
|
||||
return error_http(FailedDependency())
|
||||
def handle_LDAP_exception(e):
|
||||
log.debug('LDAP server not accssible while trying to login to opds feed %s', e)
|
||||
return error_http(e)
|
||||
|
||||
# @app.errorhandler(InvalidRequestError)
|
||||
#@app.errorhandler(OperationalError)
|
||||
#def handle_db_exception(e):
|
||||
# db.session.rollback()
|
||||
# log.error('Database request error: %s',e)
|
||||
# return internal_error(InternalServerError(e))
|
||||
|
||||
|
||||
web = Blueprint('web', __name__)
|
||||
@ -435,6 +442,10 @@ def toggle_read(book_id):
|
||||
db.session.commit()
|
||||
except KeyError:
|
||||
log.error(u"Custom Column No.%d is not exisiting in calibre database", config.config_read_column)
|
||||
except OperationalError as e:
|
||||
db.session.rollback()
|
||||
log.error(u"Read status could not set: %e", e)
|
||||
|
||||
return ""
|
||||
|
||||
@web.route("/ajax/togglearchived/<int:book_id>", methods=['POST'])
|
||||
|
66
test/Calibre-Web TestSummary.html
Normal file → Executable file
66
test/Calibre-Web TestSummary.html
Normal file → Executable file
@ -36,17 +36,17 @@
|
||||
<div class="col-xs-12 col-sm-6">
|
||||
<div class="row">
|
||||
<div class="col-xs-6 col-md-6 col-sm-offset-3" style="margin-top:50px;">
|
||||
<p class='text-justify attribute'><strong>Start Time: </strong>2020-05-01 13:35:57</p>
|
||||
<p class='text-justify attribute'><strong>Start Time: </strong>2020-05-05 19:02:03</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-6 col-md-6 col-sm-offset-3">
|
||||
<p class='text-justify attribute'><strong>Stop Time: </strong>2020-05-01 14:32:26</p>
|
||||
<p class='text-justify attribute'><strong>Stop Time: </strong>2020-05-05 19:58:37</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-6 col-md-6 col-sm-offset-3">
|
||||
<p class='text-justify attribute'><strong>Duration: </strong>47:49 min</p>
|
||||
<p class='text-justify attribute'><strong>Duration: </strong>47:42 min</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -1829,8 +1829,8 @@ AssertionError: False is not true : logfile config value is not empty after rese
|
||||
<tr class="result['header']['style']">
|
||||
<td>test_updater.test_updater</td>
|
||||
<td class="text-center">7</td>
|
||||
<td class="text-center">6</td>
|
||||
<td class="text-center">0</td>
|
||||
<td class="text-center">5</td>
|
||||
<td class="text-center">1</td>
|
||||
<td class="text-center">0</td>
|
||||
<td class="text-center">1</td>
|
||||
<td class="text-center">
|
||||
@ -1867,11 +1867,33 @@ AssertionError: False is not true : logfile config value is not empty after rese
|
||||
|
||||
|
||||
|
||||
<tr id='pt18.4' class='hiddenRow bg-success'>
|
||||
<tr id='ft18.4' class='none bg-danger'>
|
||||
<td>
|
||||
<div class='testcase'>test_check_update_stable_versions</div>
|
||||
</td>
|
||||
<td colspan='6' align='center'>PASS</td>
|
||||
<td colspan='6'>
|
||||
<div class="text-center">
|
||||
<a class="popup_link text-center" onfocus='blur()' onclick="showTestDetail('div_ft18.4')">FAIL</a>
|
||||
</div>
|
||||
<!--css div popup start-->
|
||||
<div id='div_ft18.4' class="popup_window test_output" style="display:none;">
|
||||
<div class='close_button pull-right'>
|
||||
<button type="button" class="close" aria-label="Close" onfocus='this.blur();'
|
||||
onclick="document.getElementById('div_ft18.4').style.display='none'"><span
|
||||
aria-hidden="true">×</span></button>
|
||||
</div>
|
||||
<div class="text-left pull-left">
|
||||
<pre class="text-left">Traceback (most recent call last):
|
||||
File "/home/matthias/Entwicklung/calibre-web-test/test/test_updater.py", line 150, in test_check_update_stable_versions
|
||||
self.check_updater('latest version installed', "alert-warning")
|
||||
File "/home/matthias/Entwicklung/calibre-web-test/test/test_updater.py", line 60, in check_updater
|
||||
self.assertTrue(self.check_element_on_page((By.CLASS_NAME, className)))
|
||||
AssertionError: False is not true</pre>
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
<!--css div popup end-->
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
|
||||
@ -1924,8 +1946,8 @@ AssertionError: False is not true : logfile config value is not empty after rese
|
||||
<tr class="result['header']['style']">
|
||||
<td>test_user_template.test_user_template</td>
|
||||
<td class="text-center">19</td>
|
||||
<td class="text-center">18</td>
|
||||
<td class="text-center">1</td>
|
||||
<td class="text-center">19</td>
|
||||
<td class="text-center">0</td>
|
||||
<td class="text-center">0</td>
|
||||
<td class="text-center">0</td>
|
||||
<td class="text-center">
|
||||
@ -2088,31 +2110,11 @@ AssertionError: False is not true : logfile config value is not empty after rese
|
||||
|
||||
|
||||
|
||||
<tr id='ft19.18' class='none bg-danger'>
|
||||
<tr id='pt19.18' class='hiddenRow bg-success'>
|
||||
<td>
|
||||
<div class='testcase'>test_series_user_template</div>
|
||||
</td>
|
||||
<td colspan='6'>
|
||||
<div class="text-center">
|
||||
<a class="popup_link text-center" onfocus='blur()' onclick="showTestDetail('div_ft19.18')">FAIL</a>
|
||||
</div>
|
||||
<!--css div popup start-->
|
||||
<div id='div_ft19.18' class="popup_window test_output" style="display:none;">
|
||||
<div class='close_button pull-right'>
|
||||
<button type="button" class="close" aria-label="Close" onfocus='this.blur();'
|
||||
onclick="document.getElementById('div_ft19.18').style.display='none'"><span
|
||||
aria-hidden="true">×</span></button>
|
||||
</div>
|
||||
<div class="text-left pull-left">
|
||||
<pre class="text-left">Traceback (most recent call last):
|
||||
File "/home/matthias/Entwicklung/calibre-web-test/test/test_user_template.py", line 193, in test_series_user_template
|
||||
self.assertTrue(self.check_element_on_page((By.ID, "nav_hot")))
|
||||
AssertionError: False is not true</pre>
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
<!--css div popup end-->
|
||||
</td>
|
||||
<td colspan='6' align='center'>PASS</td>
|
||||
</tr>
|
||||
|
||||
|
||||
@ -2574,7 +2576,7 @@ AssertionError: False is not true</pre>
|
||||
|
||||
<tr>
|
||||
<th>SQLAlchemy-Utils</th>
|
||||
<td>0.36.4</td>
|
||||
<td>0.36.5</td>
|
||||
<td>test_OAuth_login</td>
|
||||
</tr>
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user