changes for #77

Code cosmetics
#75:
- More debug infos for kindlegen and sending e-mail.
- Button for sending test e-mail.
- timeout of 5min for sending e-mail
pull/85/head
OzzieIsaacs 8 years ago
parent c582ccf79c
commit ba44a15891

1
.gitignore vendored

@ -17,6 +17,7 @@ eggs/
*.db *.db
*.log *.log
config.ini config.ini
cps/static/[0-9]*
.idea/ .idea/
*.bak *.bak

@ -2,6 +2,10 @@
import os import os
import sys import sys
from threading import Thread
from multiprocessing import Queue
import time
base_path = os.path.dirname(os.path.abspath(__file__)) base_path = os.path.dirname(os.path.abspath(__file__))
# Insert local directories into path # Insert local directories into path
@ -15,11 +19,47 @@ from tornado.ioloop import IOLoop
global title_sort global title_sort
def title_sort(title): def title_sort(title):
return title return title
def start_calibreweb(messagequeue):
web.global_queue = messagequeue
if config.DEVELOPMENT: if config.DEVELOPMENT:
web.app.run(host="0.0.0.0", port=config.PORT, debug=True) web.app.run(host="0.0.0.0", port=config.PORT, debug=True)
else: else:
http_server = HTTPServer(WSGIContainer(web.app)) http_server = HTTPServer(WSGIContainer(web.app))
http_server.listen(config.PORT) http_server.listen(config.PORT)
IOLoop.instance().start() IOLoop.instance().start()
print "Tornado finished"
http_server.stop()
def stop_calibreweb():
# Close Database connections for user and data
web.db.session.close()
web.db.engine.dispose()
web.ub.session.close()
web.ub.engine.dispose()
test=IOLoop.instance()
test.add_callback(test.stop)
print("Asked Tornado to exit")
if __name__ == '__main__':
if config.DEVELOPMENT:
web.app.run(host="0.0.0.0",port=config.PORT, debug=True)
else:
while True:
q = Queue()
t = Thread(target=start_calibreweb, args=(q,))
t.start()
while True: #watching queue, if there is no call than sleep, otherwise break
if q.empty():
time.sleep(1)
else:
break
stop_calibreweb()
t.join()

@ -1,18 +1,22 @@
import logging
import uploader
import os
from flask_babel import gettext as _
__author__ = 'lemmsh' __author__ = 'lemmsh'
import logging
logger = logging.getLogger("book_formats") logger = logging.getLogger("book_formats")
import uploader
import os
try: try:
from wand.image import Image from wand.image import Image
from wand import version as ImageVersion
use_generic_pdf_cover = False use_generic_pdf_cover = False
except ImportError, e: except ImportError, e:
logger.warning('cannot import Image, generating pdf covers for pdf uploads will not work: %s', e) logger.warning('cannot import Image, generating pdf covers for pdf uploads will not work: %s', e)
use_generic_pdf_cover = True use_generic_pdf_cover = True
try: try:
from PyPDF2 import PdfFileReader from PyPDF2 import PdfFileReader
from PyPDF2 import __version__ as PyPdfVersion
use_pdf_meta = True use_pdf_meta = True
except ImportError, e: except ImportError, e:
logger.warning('cannot import PyPDF2, extracting pdf metadata will not work: %s', e) logger.warning('cannot import PyPDF2, extracting pdf metadata will not work: %s', e)
@ -37,9 +41,9 @@ def process(tmp_file_path, original_file_name, original_file_extension):
try: try:
if ".PDF" == original_file_extension.upper(): if ".PDF" == original_file_extension.upper():
return pdf_meta(tmp_file_path, original_file_name, original_file_extension) return pdf_meta(tmp_file_path, original_file_name, original_file_extension)
if ".EPUB" == original_file_extension.upper() and use_epub_meta == True: if ".EPUB" == original_file_extension.upper() and use_epub_meta is True:
return epub.get_epub_info(tmp_file_path, original_file_name, original_file_extension) return epub.get_epub_info(tmp_file_path, original_file_name, original_file_extension)
if ".FB2" == original_file_extension.upper() and use_fb2_meta == True: if ".FB2" == original_file_extension.upper() and use_fb2_meta is True:
return fb2.get_fb2_info(tmp_file_path, original_file_name, original_file_extension) return fb2.get_fb2_info(tmp_file_path, original_file_name, original_file_extension)
except Exception, e: except Exception, e:
logger.warning('cannot parse metadata, using default: %s', e) logger.warning('cannot parse metadata, using default: %s', e)
@ -47,7 +51,6 @@ def process(tmp_file_path, original_file_name, original_file_extension):
return default_meta(tmp_file_path, original_file_name, original_file_extension) return default_meta(tmp_file_path, original_file_name, original_file_extension)
def default_meta(tmp_file_path, original_file_name, original_file_extension): def default_meta(tmp_file_path, original_file_name, original_file_extension):
return uploader.BookMeta( return uploader.BookMeta(
file_path=tmp_file_path, file_path=tmp_file_path,
@ -63,13 +66,13 @@ def default_meta(tmp_file_path, original_file_name, original_file_extension):
def pdf_meta(tmp_file_path, original_file_name, original_file_extension): def pdf_meta(tmp_file_path, original_file_name, original_file_extension):
if (use_pdf_meta): if use_pdf_meta:
pdf = PdfFileReader(open(tmp_file_path, 'rb')) pdf = PdfFileReader(open(tmp_file_path, 'rb'))
doc_info = pdf.getDocumentInfo() doc_info = pdf.getDocumentInfo()
else: else:
doc_info = None doc_info = None
if (doc_info is not None): if doc_info is not None:
author = doc_info.author if doc_info.author is not None else "Unknown" author = doc_info.author if doc_info.author is not None else "Unknown"
title = doc_info.title if doc_info.title is not None else original_file_name title = doc_info.title if doc_info.title is not None else original_file_name
subject = doc_info.subject subject = doc_info.subject
@ -88,6 +91,7 @@ def pdf_meta(tmp_file_path, original_file_name, original_file_extension):
series="", series="",
series_id="") series_id="")
def pdf_preview(tmp_file_path, tmp_dir): def pdf_preview(tmp_file_path, tmp_dir):
if use_generic_pdf_cover: if use_generic_pdf_cover:
return None return None
@ -97,3 +101,14 @@ def pdf_preview(tmp_file_path, tmp_dir):
img.compression_quality = 88 img.compression_quality = 88
img.save(filename=os.path.join(tmp_dir, cover_file_name)) img.save(filename=os.path.join(tmp_dir, cover_file_name))
return cover_file_name return cover_file_name
def get_versions():
if not use_generic_pdf_cover:
IVersion=ImageVersion.MAGICK_VERSION
else:
IVersion=_('not installed')
if use_pdf_meta:
PVersion=PyPdfVersion
else:
PVersion=_('not installed')
return {'ImageVersion':IVersion,'PyPdfVersion':PVersion}

@ -9,6 +9,7 @@ CONFIG_FILE= os.path.join(os.path.normpath(os.path.dirname(os.path.realpath(__fi
CFG = ConfigObj(CONFIG_FILE) CFG = ConfigObj(CONFIG_FILE)
CFG.encoding = 'UTF-8' CFG.encoding = 'UTF-8'
def CheckSection(sec): def CheckSection(sec):
""" Check if INI section exists, if not create it """ """ Check if INI section exists, if not create it """
try: try:
@ -18,6 +19,7 @@ def CheckSection(sec):
CFG[sec] = {} CFG[sec] = {}
return False return False
def check_setting_str(config, cfg_name, item_name, def_val, log=True): def check_setting_str(config, cfg_name, item_name, def_val, log=True):
try: try:
my_val = config[cfg_name][item_name].decode('UTF-8') my_val = config[cfg_name][item_name].decode('UTF-8')
@ -68,18 +70,10 @@ if DB_ROOT == "":
print "Calibre database directory (DB_ROOT) is not configured" print "Calibre database directory (DB_ROOT) is not configured"
sys.exit(1) sys.exit(1)
configval={} configval = {"DB_ROOT": DB_ROOT, "APP_DB_ROOT": APP_DB_ROOT, "MAIN_DIR": MAIN_DIR, "LOG_DIR": LOG_DIR, "PORT": PORT,
configval["DB_ROOT"] = DB_ROOT "NEWEST_BOOKS": NEWEST_BOOKS, "DEVELOPMENT": DEVELOPMENT, "TITLE_REGEX": TITLE_REGEX,
configval["APP_DB_ROOT"] = APP_DB_ROOT "PUBLIC_REG": PUBLIC_REG, "UPLOADING": UPLOADING, "ANON_BROWSE": ANON_BROWSE}
configval["MAIN_DIR"] = MAIN_DIR
configval["LOG_DIR"] = LOG_DIR
configval["PORT"] = PORT
configval["NEWEST_BOOKS"] = NEWEST_BOOKS
configval["DEVELOPMENT"] = DEVELOPMENT
configval["TITLE_REGEX"] = TITLE_REGEX
configval["PUBLIC_REG"] = PUBLIC_REG
configval["UPLOADING"] = UPLOADING
configval["ANON_BROWSE"] = ANON_BROWSE
def save_config(configval): def save_config(configval):
new_config = ConfigObj(encoding='UTF-8') new_config = ConfigObj(encoding='UTF-8')

@ -11,6 +11,8 @@ import ast
# calibre sort stuff # calibre sort stuff
title_pat = re.compile(config.TITLE_REGEX, re.IGNORECASE) title_pat = re.compile(config.TITLE_REGEX, re.IGNORECASE)
def title_sort(title): def title_sort(title):
match = title_pat.search(title) match = title_pat.search(title)
if match: if match:
@ -73,6 +75,7 @@ for row in cc:
'value': Column(String)} 'value': Column(String)}
cc_classes[row.id] = type('Custom_Column_' + str(row.id), (Base,), ccdict) cc_classes[row.id] = type('Custom_Column_' + str(row.id), (Base,), ccdict)
class Comments(Base): class Comments(Base):
__tablename__ = 'comments' __tablename__ = 'comments'
@ -100,6 +103,7 @@ class Tags(Base):
def __repr__(self): def __repr__(self):
return u"<Tags('{0})>".format(self.name) return u"<Tags('{0})>".format(self.name)
class Authors(Base): class Authors(Base):
__tablename__ = 'authors' __tablename__ = 'authors'
@ -116,6 +120,7 @@ class Authors(Base):
def __repr__(self): def __repr__(self):
return u"<Authors('{0},{1}{2}')>".format(self.name, self.sort, self.link) return u"<Authors('{0},{1}{2}')>".format(self.name, self.sort, self.link)
class Series(Base): class Series(Base):
__tablename__ = 'series' __tablename__ = 'series'
@ -130,6 +135,7 @@ class Series(Base):
def __repr__(self): def __repr__(self):
return u"<Series('{0},{1}')>".format(self.name, self.sort) return u"<Series('{0},{1}')>".format(self.name, self.sort)
class Ratings(Base): class Ratings(Base):
__tablename__ = 'ratings' __tablename__ = 'ratings'
@ -142,6 +148,7 @@ class Ratings(Base):
def __repr__(self): def __repr__(self):
return u"<Ratings('{0}')>".format(self.rating) return u"<Ratings('{0}')>".format(self.rating)
class Languages(Base): class Languages(Base):
__tablename__ = 'languages' __tablename__ = 'languages'
@ -154,6 +161,7 @@ class Languages(Base):
def __repr__(self): def __repr__(self):
return u"<Languages('{0}')>".format(self.lang_code) return u"<Languages('{0}')>".format(self.lang_code)
class Data(Base): class Data(Base):
__tablename__ = 'data' __tablename__ = 'data'
@ -172,6 +180,7 @@ class Data(Base):
def __repr__(self): def __repr__(self):
return u"<Data('{0},{1}{2}{3}')>".format(self.book, self.format, self.uncompressed_size, self.name) return u"<Data('{0},{1}{2}{3}')>".format(self.book, self.format, self.uncompressed_size, self.name)
class Books(Base): class Books(Base):
__tablename__ = 'books' __tablename__ = 'books'
@ -207,12 +216,19 @@ class Books(Base):
self.has_cover = has_cover self.has_cover = has_cover
def __repr__(self): def __repr__(self):
return u"<Books('{0},{1}{2}{3}{4}{5}{6}{7}{8}')>".format(self.title, self.sort, self.author_sort, self.timestamp, self.pubdate, self.series_index, self.last_modified ,self.path, self.has_cover) return u"<Books('{0},{1}{2}{3}{4}{5}{6}{7}{8}')>".format(self.title, self.sort, self.author_sort,
self.timestamp, self.pubdate, self.series_index,
self.last_modified, self.path, self.has_cover)
for id in cc_ids: for id in cc_ids:
if id[1] == 'bool': if id[1] == 'bool':
setattr(Books, 'custom_column_' + str(id[0]), relationship(cc_classes[id[0]], primaryjoin=(Books.id==cc_classes[id[0]].book), backref='books')) setattr(Books, 'custom_column_' + str(id[0]), relationship(cc_classes[id[0]],
primaryjoin=(Books.id == cc_classes[id[0]].book),
backref='books'))
else: else:
setattr(Books, 'custom_column_' + str(id[0]), relationship(cc_classes[id[0]], secondary=books_custom_column_links[id[0]], backref='books')) setattr(Books, 'custom_column_' + str(id[0]), relationship(cc_classes[id[0]],
secondary = books_custom_column_links[id[0]],
backref='books'))
class Custom_Columns(Base): class Custom_Columns(Base):
__tablename__ = 'custom_columns' __tablename__ = 'custom_columns'
@ -235,5 +251,3 @@ class Custom_Columns(Base):
Session = sessionmaker() Session = sessionmaker()
Session.configure(bind=engine) Session.configure(bind=engine)
session = Session() session = Session()

@ -3,8 +3,9 @@ from lxml import etree
import os import os
import uploader import uploader
def extractCover(zip, coverFile, tmp_file_name): def extractCover(zip, coverFile, tmp_file_name):
if (coverFile is None): if coverFile is None:
return None return None
else: else:
cf = zip.read("OPS/" + coverFile) cf = zip.read("OPS/" + coverFile)
@ -16,7 +17,6 @@ def extractCover(zip, coverFile, tmp_file_name):
return tmp_cover_name return tmp_cover_name
def get_epub_info(tmp_file_path, original_file_name, original_file_extension): def get_epub_info(tmp_file_path, original_file_name, original_file_extension):
ns = { ns = {
'n': 'urn:oasis:names:tc:opendocument:xmlns:container', 'n': 'urn:oasis:names:tc:opendocument:xmlns:container',
@ -37,14 +37,14 @@ def get_epub_info(tmp_file_path, original_file_name, original_file_extension):
epub_metadata = {} epub_metadata = {}
for s in ['title', 'description', 'creator']: for s in ['title', 'description', 'creator']:
tmp = p.xpath('dc:%s/text()'%(s),namespaces=ns) tmp = p.xpath('dc:%s/text()' % s, namespaces=ns)
if (len(tmp) > 0): if len(tmp) > 0:
epub_metadata[s] = p.xpath('dc:%s/text()'%(s),namespaces=ns)[0] epub_metadata[s] = p.xpath('dc:%s/text()' % s, namespaces=ns)[0]
else: else:
epub_metadata[s] = "Unknown" epub_metadata[s] = "Unknown"
coversection = tree.xpath("/pkg:package/pkg:manifest/pkg:item[@id='cover']/@href", namespaces=ns) coversection = tree.xpath("/pkg:package/pkg:manifest/pkg:item[@id='cover']/@href", namespaces=ns)
if (len(coversection) > 0): if len(coversection) > 0:
coverfile = extractCover(zip, coversection[0], tmp_file_path) coverfile = extractCover(zip, coversection[0], tmp_file_path)
else: else:
coverfile = None coverfile = None
@ -53,7 +53,6 @@ def get_epub_info(tmp_file_path, original_file_name, original_file_extension):
else: else:
title = epub_metadata['title'] title = epub_metadata['title']
return uploader.BookMeta( return uploader.BookMeta(
file_path=tmp_file_path, file_path=tmp_file_path,
extension=original_file_extension, extension=original_file_extension,
@ -64,4 +63,3 @@ def get_epub_info(tmp_file_path, original_file_name, original_file_extension):
tags="", tags="",
series="", series="",
series_id="") series_id="")

@ -15,12 +15,15 @@ def get_fb2_info(tmp_file_path, original_file_name, original_file_extension):
tree = etree.fromstring(fb2_file.read()) tree = etree.fromstring(fb2_file.read())
authors = tree.xpath('/fb:FictionBook/fb:description/fb:title-info/fb:author', namespaces=ns) authors = tree.xpath('/fb:FictionBook/fb:description/fb:title-info/fb:author', namespaces=ns)
def get_author(element): def get_author(element):
return element.xpath('fb:first-name/text()', namespaces=ns)[0] + ' ' + element.xpath('fb:middle-name/text()', namespaces=ns)[0] + ' ' + element.xpath('fb:last-name/text()', namespaces=ns)[0] return element.xpath('fb:first-name/text()', namespaces=ns)[0] + ' ' + element.xpath('fb:middle-name/text()',
namespaces=ns)[0] + ' ' + element.xpath('fb:last-name/text()', namespaces=ns)[0]
author = ", ".join(map(get_author, authors)) author = ", ".join(map(get_author, authors))
title = unicode(tree.xpath('/fb:FictionBook/fb:description/fb:title-info/fb:book-title/text()', namespaces=ns)[0]) title = unicode(tree.xpath('/fb:FictionBook/fb:description/fb:title-info/fb:book-title/text()', namespaces=ns)[0])
description = unicode(tree.xpath('/fb:FictionBook/fb:description/fb:publish-info/fb:book-name/text()', namespaces=ns)[0]) description = unicode(tree.xpath('/fb:FictionBook/fb:description/fb:publish-info/fb:book-name/text()',
namespaces=ns)[0])
return uploader.BookMeta( return uploader.BookMeta(
file_path=tmp_file_path, file_path=tmp_file_path,
@ -32,4 +35,3 @@ def get_fb2_info(tmp_file_path, original_file_name, original_file_extension):
tags="", tags="",
series="", series="",
series_id="") series_id="")

@ -4,8 +4,10 @@
import db, ub import db, ub
import config import config
from flask import current_app as app from flask import current_app as app
import logging
import smtplib import smtplib
import tempfile
import socket import socket
import sys import sys
import os import os
@ -21,14 +23,17 @@ from email.generator import Generator
from flask_babel import gettext as _ from flask_babel import gettext as _
import subprocess import subprocess
def update_download(book_id, user_id): def update_download(book_id, user_id):
check = ub.session.query(ub.Downloads).filter(ub.Downloads.user_id == user_id).filter(ub.Downloads.book_id == book_id).first() check = ub.session.query(ub.Downloads).filter(ub.Downloads.user_id == user_id).filter(ub.Downloads.book_id ==
book_id).first()
if not check: if not check:
new_download = ub.Downloads(user_id=user_id, book_id=book_id) new_download = ub.Downloads(user_id=user_id, book_id=book_id)
ub.session.add(new_download) ub.session.add(new_download)
ub.session.commit() ub.session.commit()
def make_mobi(book_id): def make_mobi(book_id):
if sys.platform == "win32": if sys.platform == "win32":
kindlegen = os.path.join(config.MAIN_DIR, "vendor", u"kindlegen.exe") kindlegen = os.path.join(config.MAIN_DIR, "vendor", u"kindlegen.exe")
@ -45,9 +50,17 @@ def make_mobi(book_id):
file_path = os.path.join(config.DB_ROOT, book.path, data.name) file_path = os.path.join(config.DB_ROOT, book.path, data.name)
if os.path.exists(file_path + u".epub"): if os.path.exists(file_path + u".epub"):
p = subprocess.Popen((kindlegen + " \"" + file_path + u".epub\" ").encode(sys.getfilesystemencoding()), shell=True, stdout=subprocess.PIPE, p = subprocess.Popen((kindlegen + " \"" + file_path + u".epub\" ").encode(sys.getfilesystemencoding()),
stderr=subprocess.PIPE, stdin=subprocess.PIPE) shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
check = p.wait() # Poll process for new output until finished
while True:
nextline = p.stdout.readline()
if nextline == '' and p.poll() is not None:
break
if nextline != "\r\n":
app.logger.debug(nextline.strip('\r\n'))
check = p.returncode
if not check or check < 2: if not check or check < 2:
book.data.append(db.Data( book.data.append(db.Data(
name=book.data[0].name, name=book.data[0].name,
@ -64,8 +77,67 @@ def make_mobi(book_id):
app.logger.error("make_mobie: epub not found: %s.epub" % file_path) app.logger.error("make_mobie: epub not found: %s.epub" % file_path)
return None return None
class StderrLogger(object):
buffer=''
def __init__(self):
self.logger = logging.getLogger('cps.web')
def write(self, message):
if message=='\n':
self.logger.debug(self.buffer)
self.buffer=''
else:
self.buffer=self.buffer+message
def send_test_mail(kindle_mail):
settings = ub.get_mail_settings()
msg = MIMEMultipart()
msg['From'] = settings["mail_from"]
msg['To'] = kindle_mail
msg['Subject'] = _('Calibre-web test email')
text = _('This email has been sent via calibre web.')
use_ssl = settings.get('mail_use_ssl', 0)
# convert MIME message to string
fp = StringIO()
gen = Generator(fp, mangle_from_=False)
gen.flatten(msg)
msg = fp.getvalue()
# send email
try:
timeout=600 # set timeout to 5mins
org_stderr = smtplib.stderr
smtplib.stderr = StderrLogger()
mailserver = smtplib.SMTP(settings["mail_server"], settings["mail_port"],timeout)
mailserver.set_debuglevel(1)
if int(use_ssl) == 1:
mailserver.ehlo()
mailserver.starttls()
mailserver.ehlo()
if settings["mail_password"]:
mailserver.login(settings["mail_login"], settings["mail_password"])
mailserver.sendmail(settings["mail_login"], kindle_mail, msg)
mailserver.quit()
smtplib.stderr = org_stderr
except (socket.error, smtplib.SMTPRecipientsRefused, smtplib.SMTPException), e:
app.logger.error(traceback.print_exc())
return _("Failed to send mail: %s" % str(e))
return None
def send_mail(book_id, kindle_mail): def send_mail(book_id, kindle_mail):
'''Send email with attachments''' """Send email with attachments"""
is_mobi = False is_mobi = False
is_azw = False is_azw = False
is_azw3 = False is_azw3 = False
@ -125,8 +197,13 @@ def send_mail(book_id, kindle_mail):
# send email # send email
try: try:
mailserver = smtplib.SMTP(settings["mail_server"],settings["mail_port"]) timeout=600 # set timeout to 5mins
mailserver.set_debuglevel(0)
org_stderr = smtplib.stderr
smtplib.stderr = StderrLogger()
mailserver = smtplib.SMTP(settings["mail_server"], settings["mail_port"],timeout)
mailserver.set_debuglevel(1)
if int(use_ssl) == 1: if int(use_ssl) == 1:
mailserver.ehlo() mailserver.ehlo()
@ -137,6 +214,9 @@ def send_mail(book_id, kindle_mail):
mailserver.login(settings["mail_login"], settings["mail_password"]) mailserver.login(settings["mail_login"], settings["mail_password"])
mailserver.sendmail(settings["mail_login"], kindle_mail, msg) mailserver.sendmail(settings["mail_login"], kindle_mail, msg)
mailserver.quit() mailserver.quit()
smtplib.stderr = org_stderr
except (socket.error, smtplib.SMTPRecipientsRefused, smtplib.SMTPException), e: except (socket.error, smtplib.SMTPRecipientsRefused, smtplib.SMTPException), e:
app.logger.error(traceback.print_exc()) app.logger.error(traceback.print_exc())
return _("Failed to send mail: %s" % str(e)) return _("Failed to send mail: %s" % str(e))
@ -145,7 +225,7 @@ def send_mail(book_id, kindle_mail):
def get_attachment(file_path): def get_attachment(file_path):
'''Get file as MIMEBase message''' """Get file as MIMEBase message"""
try: try:
file_ = open(file_path, 'rb') file_ = open(file_path, 'rb')
@ -163,6 +243,7 @@ def get_attachment(file_path):
'permissions?')) 'permissions?'))
return None return None
def get_valid_filename(value, replace_whitespace=True): def get_valid_filename(value, replace_whitespace=True):
""" """
Returns the given string converted to a string that can be used for a clean Returns the given string converted to a string that can be used for a clean
@ -178,6 +259,7 @@ def get_valid_filename(value, replace_whitespace=True):
value = value.replace(u"\u00DF", "ss") value = value.replace(u"\u00DF", "ss")
return value return value
def get_normalized_author(value): def get_normalized_author(value):
""" """
Normalizes sorted author name Normalizes sorted author name
@ -187,6 +269,7 @@ def get_normalized_author(value):
value = " ".join(value.split(", ")[::-1]) value = " ".join(value.split(", ")[::-1])
return value return value
def update_dir_stucture(book_id): def update_dir_stucture(book_id):
db.session.connection().connection.connection.create_function("title_sort", 1, db.title_sort) db.session.connection().connection.connection.create_function("title_sort", 1, db.title_sort)
book = db.session.query(db.Books).filter(db.Books.id == book_id).first() book = db.session.query(db.Books).filter(db.Books.id == book_id).first()
@ -208,4 +291,3 @@ def update_dir_stucture(book_id):
os.renames(path, new_author_path) os.renames(path, new_author_path)
book.path = new_authordir + os.sep + book.path.split(os.sep)[1] book.path = new_authordir + os.sep + book.path.split(os.sep)[1]
db.session.commit() db.session.commit()

@ -27,7 +27,8 @@
<label for="mail_from">{{_('From e-mail')}}</label> <label for="mail_from">{{_('From e-mail')}}</label>
<input type="text" class="form-control" name="mail_from" id="mail_from" value="{{content.mail_from}}"> <input type="text" class="form-control" name="mail_from" id="mail_from" value="{{content.mail_from}}">
</div> </div>
<button type="submit" class="btn btn-default">{{_('Submit')}}</button> <button type="submit" name="submit" value="submit" class="btn btn-default">{{_('Save settings')}}</button>
<button type="submit" name="test" value="test" class="btn btn-default">{{_('Save settings and send Test E-Mail')}}</button>
<a href="{{ url_for('user_list') }}" class="btn btn-default">{{_('Back')}}</a> <a href="{{ url_for('user_list') }}" class="btn btn-default">{{_('Back')}}</a>
</form> </form>

@ -1,7 +1,45 @@
{% extends "layout.html" %} {% extends "layout.html" %}
{% block body %} {% block body %}
<div class="discover"> <h3>{{_('Linked libraries')}}</h3>
<h2>{{bookcounter}} {{_('Books in this Library')}}</h2>
<h2>{{authorcounter}} {{_('Authors in this Library')}}</h2> <table class="table">
</div> <thead>
<tr>
<th>{{_('Program library')}}</th>
<th>{{_('Installed Version')}}</th>
</tr>
</thead>
<tbody>
<tr>
<th>Python</th>
<td>{{Versions['PythonVersion']}}</td>
</tr>
<tr>
<th>Kindlegen</th>
<td>{{Versions['KindlegenVersion']}}</td>
</tr>
<tr>
<th>ImageMagick</th>
<td>{{Versions['ImageVersion']}}</td>
</tr>
<tr>
<th>PyPDF2</th>
<td>{{Versions['PyPdfVersion']}}</td>
</tr>
</tbody>
</table>
<h3>{{_('Calibre library statistics')}}</h3>
<table class="table">
<tbody>
<tr>
<th>{{bookcounter}}</th>
<td>{{_('Books in this Library')}}</td>
</tr>
<tr>
<th>{{authorcounter}}</th>
<td>{{_('Authors in this Library')}}</td>
</tr>
</tbody>
</table>
{% endblock %} {% endblock %}

@ -40,7 +40,6 @@
{% endfor %} {% endfor %}
</select> </select>
</div> </div>
{% if g.user and g.user.role_admin() and not profile %}
<div class="form-group"> <div class="form-group">
<input type="checkbox" name="show_random" {% if content.random_books %}checked{% endif %}> <input type="checkbox" name="show_random" {% if content.random_books %}checked{% endif %}>
<label for="show_random">{{_('Show random books')}}</label> <label for="show_random">{{_('Show random books')}}</label>
@ -62,6 +61,8 @@
<label for="show_category">{{_('Show category selection')}}</label> <label for="show_category">{{_('Show category selection')}}</label>
</div> </div>
{% if g.user and g.user.role_admin() and not profile %}
<div class="form-group"> <div class="form-group">
<input type="checkbox" name="admin_role" id="admin_role" {% if content.role_admin() %}checked{% endif %}> <input type="checkbox" name="admin_role" id="admin_role" {% if content.role_admin() %}checked{% endif %}>
<label for="admin_role">{{_('Admin user')}}</label> <label for="admin_role">{{_('Admin user')}}</label>

@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PROJECT VERSION\n" "Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2016-11-12 09:44+0100\n" "POT-Creation-Date: 2016-12-23 08:39+0100\n"
"PO-Revision-Date: 2016-07-12 19:54+0200\n" "PO-Revision-Date: 2016-07-12 19:54+0200\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: de\n" "Language: de\n"
@ -18,250 +18,272 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.3.4\n" "Generated-By: Babel 2.3.4\n"
#: cps/helper.py:80 cps/templates/detail.html:113 #: cps/book_formats.py:108 cps/book_formats.py:112 cps/web.py:923
msgid "Send to Kindle" msgid "not installed"
msgstr "An Kindle senden" msgstr "Nicht installiert"
#: cps/helper.py:81 #: cps/helper.py:93
msgid "Calibre-web test email"
msgstr "Calibre-web Test E-Mail"
#: cps/helper.py:94 cps/helper.py:147
msgid "This email has been sent via calibre web." msgid "This email has been sent via calibre web."
msgstr "Die E-Mail wurde via calibre-web versendet" msgstr "Die E-Mail wurde via calibre-web versendet"
#: cps/helper.py:103 cps/helper.py:118 #: cps/helper.py:128 cps/helper.py:216
#, python-format
msgid "Failed to send mail: %s"
msgstr "E-Mail: %s konnte nicht gesendet werden"
#: cps/helper.py:146 cps/templates/detail.html:113
msgid "Send to Kindle"
msgstr "An Kindle senden"
#: cps/helper.py:169 cps/helper.py:184
msgid "Could not find any formats suitable for sending by email" msgid "Could not find any formats suitable for sending by email"
msgstr "" msgstr ""
"Konnte keine Formate finden welche für das versenden per E-Mail geeignet " "Konnte keine Formate finden welche für das versenden per E-Mail geeignet "
"sind" "sind"
#: cps/helper.py:112 #: cps/helper.py:178
msgid "Could not convert epub to mobi" msgid "Could not convert epub to mobi"
msgstr "Konnte .epub nicht nach .mobi convertieren" msgstr "Konnte .epub nicht nach .mobi konvertieren"
#: cps/helper.py:142
#, python-format
msgid "Failed to send mail: %s"
msgstr "E-Mail: %s konnte nicht gesendet werden"
#: cps/helper.py:162 #: cps/helper.py:236
msgid "The requested file could not be read. Maybe wrong permissions?" msgid "The requested file could not be read. Maybe wrong permissions?"
msgstr "Die angeforderte Datei konnte nicht gelesen werden. Falsche Dateirechte?" msgstr "Die angeforderte Datei konnte nicht gelesen werden. Falsche Dateirechte?"
#: cps/web.py:639 #: cps/web.py:717
msgid "Latest Books" msgid "Latest Books"
msgstr "Letzte Bücher" msgstr "Letzte Bücher"
#: cps/web.py:661 #: cps/web.py:742
msgid "Hot Books (most downloaded)" msgid "Hot Books (most downloaded)"
msgstr "Beliebte Bücher (die meisten Downloads)" msgstr "Beliebte Bücher (die meisten Downloads)"
#: cps/templates/index.xml:41 cps/web.py:668 #: cps/templates/index.xml:41 cps/web.py:750
msgid "Random Books" msgid "Random Books"
msgstr "Zufällige Bücher" msgstr "Zufällige Bücher"
#: cps/web.py:679 #: cps/web.py:763
msgid "Author list" msgid "Author list"
msgstr "Autorenliste" msgstr "Autorenliste"
#: cps/web.py:695 #: cps/web.py:780
#, python-format #, python-format
msgid "Author: %(nam)s" msgid "Author: %(nam)s"
msgstr "Autor: %(nam)s" msgstr "Autor: %(nam)s"
#: cps/templates/index.xml:65 cps/web.py:706 #: cps/templates/index.xml:65 cps/web.py:793
msgid "Series list" msgid "Series list"
msgstr "Liste Serien" msgstr "Liste Serien"
#: cps/web.py:714 #: cps/web.py:804
#, python-format #, python-format
msgid "Series: %(serie)s" msgid "Series: %(serie)s"
msgstr "Serie: %(serie)s" msgstr "Serie: %(serie)s"
#: cps/web.py:716 cps/web.py:796 cps/web.py:914 cps/web.py:1524 #: cps/web.py:806 cps/web.py:902 cps/web.py:1061 cps/web.py:1729
msgid "Error opening eBook. File does not exist or file is not accessible:" msgid "Error opening eBook. File does not exist or file is not accessible:"
msgstr "" msgstr ""
"Buch öffnen fehlgeschlagen. Datei existiert nicht, oder ist nicht " "Buch öffnen fehlgeschlagen. Datei existiert nicht, oder ist nicht "
"zugänglich." "zugänglich."
#: cps/web.py:742 #: cps/web.py:837
msgid "Available languages" msgid "Available languages"
msgstr "Verfügbare Sprachen" msgstr "Verfügbare Sprachen"
#: cps/web.py:754 #: cps/web.py:852
#, python-format #, python-format
msgid "Language: %(name)s" msgid "Language: %(name)s"
msgstr "Sprache: %(name)s" msgstr "Sprache: %(name)s"
#: cps/templates/index.xml:57 cps/web.py:765 #: cps/templates/index.xml:57 cps/web.py:865
msgid "Category list" msgid "Category list"
msgstr "Kategorieliste" msgstr "Kategorieliste"
#: cps/web.py:772 #: cps/web.py:875
#, python-format #, python-format
msgid "Category: %(name)s" msgid "Category: %(name)s"
msgstr "Kategorie: %(name)s" msgstr "Kategorie: %(name)s"
#: cps/web.py:810 #: cps/web.py:931
msgid "Statistics" msgid "Statistics"
msgstr "Statistiken" msgstr "Statistiken"
#: cps/web.py:898 cps/web.py:905 cps/web.py:912 #: cps/web.py:939
msgid "Server restarts"
msgstr "Server startet neu"
#: cps/web.py:1037 cps/web.py:1044 cps/web.py:1051 cps/web.py:1058
msgid "Read a Book" msgid "Read a Book"
msgstr "Lese ein Buch" msgstr "Lese ein Buch"
#: cps/web.py:951 cps/web.py:1179 #: cps/web.py:1100 cps/web.py:1365
msgid "Please fill out all fields!" msgid "Please fill out all fields!"
msgstr "Bitte alle Felder ausfüllen!" msgstr "Bitte alle Felder ausfüllen!"
#: cps/web.py:967 #: cps/web.py:1116
msgid "An unknown error occured. Please try again later." msgid "An unknown error occured. Please try again later."
msgstr "Es ist ein unbekannter Fehler aufgetreten. Bitte später erneut versuchen." msgstr "Es ist ein unbekannter Fehler aufgetreten. Bitte später erneut versuchen."
#: cps/web.py:972 #: cps/web.py:1121
msgid "This username or email address is already in use." msgid "This username or email address is already in use."
msgstr "Der Benutzername oder die E-Mailadresse ist in bereits in Benutzung." msgstr "Der Benutzername oder die E-Mailadresse ist in bereits in Benutzung."
#: cps/web.py:975 #: cps/web.py:1124
msgid "register" msgid "register"
msgstr "Registieren" msgstr "Registieren"
#: cps/web.py:990 #: cps/web.py:1140
#, python-format #, python-format
msgid "you are now logged in as: '%(nickname)s'" msgid "you are now logged in as: '%(nickname)s'"
msgstr "Du bist nun eingeloggt als '%(nickname)s'" msgstr "Du bist nun eingeloggt als '%(nickname)s'"
#: cps/web.py:993 #: cps/web.py:1143
msgid "Wrong Username or Password" msgid "Wrong Username or Password"
msgstr "Flascher Benutzername oder Passwort" msgstr "Flascher Benutzername oder Passwort"
#: cps/web.py:995 #: cps/web.py:1145
msgid "login" msgid "login"
msgstr "Login" msgstr "Login"
#: cps/web.py:1011 #: cps/web.py:1162
msgid "Please configure the SMTP mail settings first..." msgid "Please configure the SMTP mail settings first..."
msgstr "Bitte zuerst die SMTP Mail Einstellung konfigurieren ..." msgstr "Bitte zuerst die SMTP Mail Einstellung konfigurieren ..."
#: cps/web.py:1015 #: cps/web.py:1166
#, python-format #, python-format
msgid "Book successfully send to %(kindlemail)s" msgid "Book successfully send to %(kindlemail)s"
msgstr "Buch erfolgreich versandt an %(kindlemail)s" msgstr "Buch erfolgreich versandt an %(kindlemail)s"
#: cps/web.py:1018 #: cps/web.py:1170
#, python-format #, python-format
msgid "There was an error sending this book: %(res)s" msgid "There was an error sending this book: %(res)s"
msgstr "Beim Senden des Buchs trat ein Fehler auf: %(res)s" msgstr "Beim Senden des Buchs trat ein Fehler auf: %(res)s"
#: cps/web.py:1020 #: cps/web.py:1172
msgid "Please configure your kindle email address first..." msgid "Please configure your kindle email address first..."
msgstr "Bitte die Kindle E-Mail Adresse zuuerst konfigurieren..." msgstr "Bitte die Kindle E-Mail Adresse zuuerst konfigurieren..."
#: cps/web.py:1035 #: cps/web.py:1188
#, python-format #, python-format
msgid "Book has been added to shelf: %(sname)s" msgid "Book has been added to shelf: %(sname)s"
msgstr "Das Buch wurde dem Bücherregal: %(sname)s hinzugefügt" msgstr "Das Buch wurde dem Bücherregal: %(sname)s hinzugefügt"
#: cps/web.py:1054 #: cps/web.py:1209
#, python-format #, python-format
msgid "Book has been removed from shelf: %(sname)s" msgid "Book has been removed from shelf: %(sname)s"
msgstr "Das Buch wurde aus dem Bücherregal: %(sname)s entfernt" msgstr "Das Buch wurde aus dem Bücherregal: %(sname)s entfernt"
#: cps/web.py:1070 #: cps/web.py:1226
#, python-format #, python-format
msgid "A shelf with the name '%(title)s' already exists." msgid "A shelf with the name '%(title)s' already exists."
msgstr "Es existiert bereits ein Bücheregal mit dem Titel '%(title)s'" msgstr "Es existiert bereits ein Bücheregal mit dem Titel '%(title)s'"
#: cps/web.py:1075 #: cps/web.py:1231
#, python-format #, python-format
msgid "Shelf %(title)s created" msgid "Shelf %(title)s created"
msgstr "Bücherregal %(title)s erzeugt" msgstr "Bücherregal %(title)s erzeugt"
#: cps/web.py:1077 #: cps/web.py:1233
msgid "There was an error" msgid "There was an error"
msgstr "Es trat ein Fehler auf" msgstr "Es trat ein Fehler auf"
#: cps/web.py:1078 cps/web.py:1080 #: cps/web.py:1234 cps/web.py:1236
msgid "create a shelf" msgid "create a shelf"
msgstr "Bücherregal erzeugen" msgstr "Bücherregal erzeugen"
#: cps/web.py:1096 #: cps/web.py:1256
#, python-format #, python-format
msgid "successfully deleted shelf %(name)s" msgid "successfully deleted shelf %(name)s"
msgstr "Bücherregal %(name)s erfolgreich gelöscht" msgstr "Bücherregal %(name)s erfolgreich gelöscht"
#: cps/web.py:1113 #: cps/web.py:1277
#, python-format #, python-format
msgid "Shelf: '%(name)s'" msgid "Shelf: '%(name)s'"
msgstr "Bücherregal: '%(name)s'" msgstr "Bücherregal: '%(name)s'"
#: cps/web.py:1150 #: cps/web.py:1332
msgid "Found an existing account for this email address." msgid "Found an existing account for this email address."
msgstr "Es existiert ein Benutzerkonto für diese E-Mailadresse" msgstr "Es existiert ein Benutzerkonto für diese E-Mailadresse"
#: cps/web.py:1151 cps/web.py:1153 #: cps/web.py:1334 cps/web.py:1337
#, python-format #, python-format
msgid "%(name)s's profile" msgid "%(name)s's profile"
msgstr "%(name)s's Profil" msgstr "%(name)s's Profil"
#: cps/web.py:1152 #: cps/web.py:1335
msgid "Profile updated" msgid "Profile updated"
msgstr "Profil aktualisiert" msgstr "Profil aktualisiert"
#: cps/web.py:1161 #: cps/web.py:1346
msgid "User list" msgid "User list"
msgstr "Benutzerliste" msgstr "Benutzerliste"
#: cps/templates/user_list.html:32 cps/web.py:1180 #: cps/templates/user_list.html:32 cps/web.py:1366
msgid "Add new user" msgid "Add new user"
msgstr "Neuen Benutzer hinzufügen" msgstr "Neuen Benutzer hinzufügen"
#: cps/web.py:1213 #: cps/web.py:1399
#, python-format #, python-format
msgid "User '%(user)s' created" msgid "User '%(user)s' created"
msgstr "Benutzer '%(user)s' angelegt" msgstr "Benutzer '%(user)s' angelegt"
#: cps/web.py:1217 #: cps/web.py:1403
msgid "Found an existing account for this email address or nickname." msgid "Found an existing account for this email address or nickname."
msgstr "" msgstr ""
"Es existiert ein Benutzerkonto für diese Emailadresse oder den " "Es existiert ein Benutzerkonto für diese Emailadresse oder den "
"Benutzernamen" "Benutzernamen"
#: cps/web.py:1238 #: cps/web.py:1426 cps/web.py:1437
msgid "Mail settings updated" msgid "Mail settings updated"
msgstr "E-Mail Einstellungen aktualisiert" msgstr "E-Mail Einstellungen aktualisiert"
#: cps/web.py:1241 #: cps/web.py:1432
#, python-format
msgid "Test E-Mail successfully send to %(kindlemail)s"
msgstr "Test E-Mail erfolgreich an %(kindlemail)s versendet"
#: cps/web.py:1435
#, python-format
msgid "There was an error sending the Test E-Mail: %(res)s"
msgstr "Fehler beim versenden der Test E-Mail: %(res)s"
#: cps/web.py:1438
msgid "Edit mail settings" msgid "Edit mail settings"
msgstr "E-Mail Einstellungen editieren" msgstr "E-Mail Einstellungen editieren"
#: cps/web.py:1263 #: cps/web.py:1461
#, python-format #, python-format
msgid "User '%(nick)s' deleted" msgid "User '%(nick)s' deleted"
msgstr "Benutzer '%(nick)s' gelöscht" msgstr "Benutzer '%(nick)s' gelöscht"
#: cps/web.py:1318 #: cps/web.py:1516
#, python-format #, python-format
msgid "User '%(nick)s' updated" msgid "User '%(nick)s' updated"
msgstr "Benutzer '%(nick)s' aktualisiert" msgstr "Benutzer '%(nick)s' aktualisiert"
#: cps/web.py:1321 #: cps/web.py:1519
msgid "An unknown error occured." msgid "An unknown error occured."
msgstr "Es ist ein unbekanter Fehler aufgetreten" msgstr "Es ist ein unbekanter Fehler aufgetreten"
#: cps/web.py:1322 #: cps/web.py:1521
#, python-format #, python-format
msgid "Edit User %(nick)s" msgid "Edit User %(nick)s"
msgstr "Benutzer %(nick)s bearbeiten" msgstr "Benutzer %(nick)s bearbeiten"
#: cps/web.py:1556 #: cps/web.py:1759
#, python-format #, python-format
msgid "Failed to create path %s (Permission denied)." msgid "Failed to create path %s (Permission denied)."
msgstr "Fehler beim Erzeugen des Pfads %s (Zugriff verweigert)" msgstr "Fehler beim Erzeugen des Pfads %s (Zugriff verweigert)"
#: cps/web.py:1561 #: cps/web.py:1764
#, python-format #, python-format
msgid "Failed to store file %s (Permission denied)." msgid "Failed to store file %s (Permission denied)."
msgstr "Fehler beim speichern der Datei %s (Zugriff verweigert)" msgstr "Fehler beim speichern der Datei %s (Zugriff verweigert)"
#: cps/web.py:1566 #: cps/web.py:1769
#, python-format #, python-format
msgid "Failed to delete file %s (Permission denied)." msgid "Failed to delete file %s (Permission denied)."
msgstr "Fehler beim Löschen von Datei %s (Zugriff verweigert)" msgstr "Fehler beim Löschen von Datei %s (Zugriff verweigert)"
@ -346,14 +368,14 @@ msgstr "Nein"
msgid "view book after edit" msgid "view book after edit"
msgstr "Buch nach Bearbeitung ansehen" msgstr "Buch nach Bearbeitung ansehen"
#: cps/templates/edit_book.html:105 cps/templates/email_edit.html:30 #: cps/templates/edit_book.html:105 cps/templates/login.html:19
#: cps/templates/login.html:19 cps/templates/search_form.html:33 #: cps/templates/search_form.html:33 cps/templates/shelf_edit.html:15
#: cps/templates/shelf_edit.html:15 cps/templates/user_edit.html:93 #: cps/templates/user_edit.html:94
msgid "Submit" msgid "Submit"
msgstr "Abschicken" msgstr "Abschicken"
#: cps/templates/edit_book.html:106 cps/templates/email_edit.html:31 #: cps/templates/edit_book.html:106 cps/templates/email_edit.html:32
#: cps/templates/user_edit.html:95 #: cps/templates/user_edit.html:96
msgid "Back" msgid "Back"
msgstr "Zurück" msgstr "Zurück"
@ -381,6 +403,14 @@ msgstr "SMTP Passwort"
msgid "From e-mail" msgid "From e-mail"
msgstr "Absenderadresse" msgstr "Absenderadresse"
#: cps/templates/email_edit.html:30
msgid "Save settings"
msgstr "Einstellungen speichern"
#: cps/templates/email_edit.html:31
msgid "Save settings and send Test E-Mail"
msgstr "Einstellungen speichern und Test E-Mail versenden"
#: cps/templates/feed.xml:14 #: cps/templates/feed.xml:14
msgid "Next" msgid "Next"
msgstr "Nächste" msgstr "Nächste"
@ -511,6 +541,14 @@ msgstr "Passwort"
msgid "Remember me" msgid "Remember me"
msgstr "Merken" msgstr "Merken"
#: cps/templates/read.html:136
msgid "Reflow text when sidebars are open."
msgstr "Text umbrechen wenn Seitenleiste geöffnet ist"
#: cps/templates/readpdf.html:29
msgid "PDF.js viewer"
msgstr "PDF.js Viewer"
#: cps/templates/register.html:4 #: cps/templates/register.html:4
msgid "Register a new account" msgid "Register a new account"
msgstr "Neues Benutzerkonto erzeugen" msgstr "Neues Benutzerkonto erzeugen"
@ -551,6 +589,10 @@ msgstr "Tags ausschließen"
msgid "Delete this Shelf" msgid "Delete this Shelf"
msgstr "Lösche dieses Bücherregal" msgstr "Lösche dieses Bücherregal"
#: cps/templates/shelf.html:7
msgid "Edit Shelf name"
msgstr "Bücherregal umbenennen"
#: cps/templates/shelf_edit.html:7 #: cps/templates/shelf_edit.html:7
msgid "Title" msgid "Title"
msgstr "Titel" msgstr "Titel"
@ -559,11 +601,27 @@ msgstr "Titel"
msgid "should the shelf be public?" msgid "should the shelf be public?"
msgstr "Soll das Bücherregal öffentlich sein?" msgstr "Soll das Bücherregal öffentlich sein?"
#: cps/templates/stats.html:4 #: cps/templates/stats.html:3
msgid "Linked libraries"
msgstr "Dynamische Bibliotheken"
#: cps/templates/stats.html:8
msgid "Program library"
msgstr "Programm Bibliotheken"
#: cps/templates/stats.html:9
msgid "Installed Version"
msgstr "Installierte Version"
#: cps/templates/stats.html:32
msgid "Calibre library statistics"
msgstr "Calibre Bibliothek Statistiken"
#: cps/templates/stats.html:37
msgid "Books in this Library" msgid "Books in this Library"
msgstr "Bücher in dieser Bibliothek" msgstr "Bücher in dieser Bibliothek"
#: cps/templates/stats.html:5 #: cps/templates/stats.html:41
msgid "Authors in this Library" msgid "Authors in this Library"
msgstr "Autoren in dieser Bibliothek" msgstr "Autoren in dieser Bibliothek"
@ -579,51 +637,51 @@ msgstr "Zeige nur Bücher mit dieser Sprache"
msgid "Show all" msgid "Show all"
msgstr "Zeige alle" msgstr "Zeige alle"
#: cps/templates/user_edit.html:46 #: cps/templates/user_edit.html:45
msgid "Show random books" msgid "Show random books"
msgstr "Zeige Zufällige Bücher" msgstr "Zeige Zufällige Bücher"
#: cps/templates/user_edit.html:50 #: cps/templates/user_edit.html:49
msgid "Show hot books" msgid "Show hot books"
msgstr "Zeige Auswahl Beliebte Bücher" msgstr "Zeige Auswahl Beliebte Bücher"
#: cps/templates/user_edit.html:54 #: cps/templates/user_edit.html:53
msgid "Show language selection" msgid "Show language selection"
msgstr "Zeige Sprachauswahl" msgstr "Zeige Sprachauswahl"
#: cps/templates/user_edit.html:58 #: cps/templates/user_edit.html:57
msgid "Show series selection" msgid "Show series selection"
msgstr "Zeige Auswahl Serien" msgstr "Zeige Auswahl Serien"
#: cps/templates/user_edit.html:62 #: cps/templates/user_edit.html:61
msgid "Show category selection" msgid "Show category selection"
msgstr "Zeige Kategorie Auswahl" msgstr "Zeige Kategorie Auswahl"
#: cps/templates/user_edit.html:67 #: cps/templates/user_edit.html:68
msgid "Admin user" msgid "Admin user"
msgstr "Admin Benutzer" msgstr "Admin Benutzer"
#: cps/templates/user_edit.html:71 #: cps/templates/user_edit.html:72
msgid "Allow Downloads" msgid "Allow Downloads"
msgstr "Downloads erlauben" msgstr "Downloads erlauben"
#: cps/templates/user_edit.html:75 #: cps/templates/user_edit.html:76
msgid "Allow Uploads" msgid "Allow Uploads"
msgstr "Uploads erlauben" msgstr "Uploads erlauben"
#: cps/templates/user_edit.html:79 #: cps/templates/user_edit.html:80
msgid "Allow Edit" msgid "Allow Edit"
msgstr "Bearbeiten erlauben" msgstr "Bearbeiten erlauben"
#: cps/templates/user_edit.html:83 #: cps/templates/user_edit.html:84
msgid "Allow Changing Password" msgid "Allow Changing Password"
msgstr "Passwort ändern erlauben" msgstr "Passwort ändern erlauben"
#: cps/templates/user_edit.html:89 #: cps/templates/user_edit.html:90
msgid "Delete this user" msgid "Delete this user"
msgstr "Benutzer löschen" msgstr "Benutzer löschen"
#: cps/templates/user_edit.html:100 #: cps/templates/user_edit.html:101
msgid "Recent Downloads" msgid "Recent Downloads"
msgstr "Letzte Downloads" msgstr "Letzte Downloads"
@ -674,6 +732,3 @@ msgstr "SMTP Einstellungen ändern"
msgid "Latin" msgid "Latin"
msgstr "Latein" msgstr "Latein"
#~ msgid "Version"
#~ msgstr "Version"

@ -1,4 +1,4 @@
# Translations template for PROJECT. # French translations for PROJECT.
# Copyright (C) 2016 ORGANIZATION # Copyright (C) 2016 ORGANIZATION
# This file is distributed under the same license as the PROJECT project. # This file is distributed under the same license as the PROJECT project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2016. # FIRST AUTHOR <EMAIL@ADDRESS>, 2016.
@ -7,256 +7,281 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PROJECT VERSION\n" "Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2016-11-13 16:45+0100\n" "POT-Creation-Date: 2016-12-23 08:39+0100\n"
"PO-Revision-Date: 2016-11-13 18:35+0100\n" "PO-Revision-Date: 2016-11-13 18:35+0100\n"
"Last-Translator: Nicolas Roudninski <nicoroud@gmail.com>\n"
"Language: fr\n"
"Language-Team: \n" "Language-Team: \n"
"Plural-Forms: nplurals=2; plural=(n > 1)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.3.4\n" "Generated-By: Babel 2.3.4\n"
"X-Generator: Poedit 1.8.11\n"
"Last-Translator: Nicolas Roudninski <nicoroud@gmail.com>\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
"Language: fr_FR\n"
#: cps/helper.py:80 cps/templates/detail.html:113 #: cps/book_formats.py:108 cps/book_formats.py:112 cps/web.py:923
msgid "Send to Kindle" msgid "not installed"
msgstr "Envoyer ver Kindle" msgstr ""
#: cps/helper.py:93
msgid "Calibre-web test email"
msgstr ""
#: cps/helper.py:81 #: cps/helper.py:94 cps/helper.py:147
msgid "This email has been sent via calibre web." msgid "This email has been sent via calibre web."
msgstr "Ce message a été envoyé depuis calibre web." msgstr "Ce message a été envoyé depuis calibre web."
#: cps/helper.py:103 cps/helper.py:118 #: cps/helper.py:128 cps/helper.py:216
#, python-format
msgid "Failed to send mail: %s"
msgstr "Impossible d'envoyer le courriel : %s"
#: cps/helper.py:146 cps/templates/detail.html:113
msgid "Send to Kindle"
msgstr "Envoyer ver Kindle"
#: cps/helper.py:169 cps/helper.py:184
msgid "Could not find any formats suitable for sending by email" msgid "Could not find any formats suitable for sending by email"
msgstr "Impossible de trouver un format adapté à envoyer par courriel" msgstr "Impossible de trouver un format adapté à envoyer par courriel"
#: cps/helper.py:112 #: cps/helper.py:178
msgid "Could not convert epub to mobi" msgid "Could not convert epub to mobi"
msgstr "Impossible de convertir epub vers mobi" msgstr "Impossible de convertir epub vers mobi"
#: cps/helper.py:142 #: cps/helper.py:236
#, python-format
msgid "Failed to send mail: %s"
msgstr "Impossible d'envoyer le courriel : %s"
#: cps/helper.py:162
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é ne peux pas être lu. Peut-être de mauvaises permissions ?" msgstr ""
"Le fichier demandé ne peux pas être lu. Peut-être de mauvaises "
"permissions ?"
#: cps/web.py:639 #: cps/web.py:717
msgid "Latest Books" msgid "Latest Books"
msgstr "Derniers livres" msgstr "Derniers livres"
#: cps/web.py:661 #: cps/web.py:742
msgid "Hot Books (most downloaded)" msgid "Hot Books (most downloaded)"
msgstr "Livres populaires (les plus téléchargés)" msgstr "Livres populaires (les plus téléchargés)"
#: cps/templates/index.xml:41 cps/web.py:668 #: cps/templates/index.xml:41 cps/web.py:750
msgid "Random Books" msgid "Random Books"
msgstr "Livres au hasard" msgstr "Livres au hasard"
#: cps/web.py:679 #: cps/web.py:763
msgid "Author list" msgid "Author list"
msgstr "Liste des auteurs" msgstr "Liste des auteurs"
#: cps/web.py:695 #: cps/web.py:780
#, python-format #, python-format
msgid "Author: %(nam)s" msgid "Author: %(nam)s"
msgstr "Auteur : %(nam)s" msgstr "Auteur : %(nam)s"
#: cps/templates/index.xml:65 cps/web.py:706 #: cps/templates/index.xml:65 cps/web.py:793
msgid "Series list" msgid "Series list"
msgstr "Liste des séries" msgstr "Liste des séries"
#: cps/web.py:714 #: cps/web.py:804
#, python-format #, python-format
msgid "Series: %(serie)s" msgid "Series: %(serie)s"
msgstr "Séries : %(serie)s" msgstr "Séries : %(serie)s"
#: cps/web.py:716 cps/web.py:796 cps/web.py:914 cps/web.py:1524 #: cps/web.py:806 cps/web.py:902 cps/web.py:1061 cps/web.py:1729
msgid "Error opening eBook. File does not exist or file is not accessible:" msgid "Error opening eBook. File does not exist or file is not accessible:"
msgstr "Erreur d'ouverture du livre numérique. Le fichier n'existe pas ou n'est pas accessible :" msgstr ""
"Erreur d'ouverture du livre numérique. Le fichier n'existe pas ou n'est "
"pas accessible :"
#: cps/web.py:742 #: cps/web.py:837
msgid "Available languages" msgid "Available languages"
msgstr "Langues disponibles" msgstr "Langues disponibles"
#: cps/web.py:754 #: cps/web.py:852
#, python-format #, python-format
msgid "Language: %(name)s" msgid "Language: %(name)s"
msgstr "Langue : %(name)s" msgstr "Langue : %(name)s"
#: cps/templates/index.xml:57 cps/web.py:765 #: cps/templates/index.xml:57 cps/web.py:865
msgid "Category list" msgid "Category list"
msgstr "Liste des catégories" msgstr "Liste des catégories"
#: cps/web.py:772 #: cps/web.py:875
#, python-format #, python-format
msgid "Category: %(name)s" msgid "Category: %(name)s"
msgstr "Catégorie : %(name)s" msgstr "Catégorie : %(name)s"
#: cps/web.py:810 #: cps/web.py:931
msgid "Statistics" msgid "Statistics"
msgstr "Statistiques" msgstr "Statistiques"
#: cps/web.py:898 cps/web.py:905 cps/web.py:912 #: cps/web.py:939
msgid "Server restarts"
msgstr ""
#: cps/web.py:1037 cps/web.py:1044 cps/web.py:1051 cps/web.py:1058
msgid "Read a Book" msgid "Read a Book"
msgstr "Lire un livre" msgstr "Lire un livre"
#: cps/web.py:951 cps/web.py:1179 #: cps/web.py:1100 cps/web.py:1365
msgid "Please fill out all fields!" msgid "Please fill out all fields!"
msgstr "SVP, complétez tous les champs !" msgstr "SVP, complétez tous les champs !"
#: cps/web.py:967 #: cps/web.py:1116
msgid "An unknown error occured. Please try again later." msgid "An unknown error occured. Please try again later."
msgstr "Une erreur a eu lieu. Merci de réessayez plus tard." msgstr "Une erreur a eu lieu. Merci de réessayez plus tard."
#: cps/web.py:972 #: cps/web.py:1121
msgid "This username or email address is already in use." msgid "This username or email address is already in use."
msgstr "Ce nom d'utilisateur ou cette adresse de courriel est déjà utilisée." msgstr "Ce nom d'utilisateur ou cette adresse de courriel est déjà utilisée."
#: cps/web.py:975 #: cps/web.py:1124
msgid "register" msgid "register"
msgstr "S'enregistrer" msgstr "S'enregistrer"
#: cps/web.py:990 #: cps/web.py:1140
#, python-format #, python-format
msgid "you are now logged in as: '%(nickname)s'" msgid "you are now logged in as: '%(nickname)s'"
msgstr "Vous êtes maintenant connecté sous : '%(nickname)s'" msgstr "Vous êtes maintenant connecté sous : '%(nickname)s'"
#: cps/web.py:993 #: cps/web.py:1143
msgid "Wrong Username or Password" msgid "Wrong Username or Password"
msgstr "Mauvais nom d'utilisateur ou mot de passe" msgstr "Mauvais nom d'utilisateur ou mot de passe"
#: cps/web.py:995 #: cps/web.py:1145
msgid "login" msgid "login"
msgstr "Connexion" msgstr "Connexion"
#: cps/web.py:1011 #: cps/web.py:1162
msgid "Please configure the SMTP mail settings first..." msgid "Please configure the SMTP mail settings first..."
msgstr "Veillez configurer les paramètres smtp d'abord..." msgstr "Veillez configurer les paramètres smtp d'abord..."
#: cps/web.py:1015 #: cps/web.py:1166
#, python-format #, python-format
msgid "Book successfully send to %(kindlemail)s" msgid "Book successfully send to %(kindlemail)s"
msgstr "Livres envoyés à %(kindlemail)s avec succès" msgstr "Livres envoyés à %(kindlemail)s avec succès"
#: cps/web.py:1018 #: cps/web.py:1170
#, python-format #, python-format
msgid "There was an error sending this book: %(res)s" msgid "There was an error sending this book: %(res)s"
msgstr "Il y a eu une erreur en envoyant ce livre : %(res)s" msgstr "Il y a eu une erreur en envoyant ce livre : %(res)s"
#: cps/web.py:1020 #: cps/web.py:1172
msgid "Please configure your kindle email address first..." msgid "Please configure your kindle email address first..."
msgstr "Veuillez configurer votre adresse kindle d'abord..." msgstr "Veuillez configurer votre adresse kindle d'abord..."
#: cps/web.py:1035 #: cps/web.py:1188
#, python-format #, python-format
msgid "Book has been added to shelf: %(sname)s" msgid "Book has been added to shelf: %(sname)s"
msgstr "Le livre a bien été ajouté à l'étagère : %(sname)s" msgstr "Le livre a bien été ajouté à l'étagère : %(sname)s"
#: cps/web.py:1054 #: cps/web.py:1209
#, python-format #, python-format
msgid "Book has been removed from shelf: %(sname)s" msgid "Book has been removed from shelf: %(sname)s"
msgstr "Le livre a été supprimé de l'étagère %(sname)s" msgstr "Le livre a été supprimé de l'étagère %(sname)s"
#: cps/web.py:1070 #: cps/web.py:1226
#, python-format #, python-format
msgid "A shelf with the name '%(title)s' already exists." msgid "A shelf with the name '%(title)s' already exists."
msgstr "Une étagère de ce nom '%(title)s' existe déjà." msgstr "Une étagère de ce nom '%(title)s' existe déjà."
#: cps/web.py:1075 #: cps/web.py:1231
#, python-format #, python-format
msgid "Shelf %(title)s created" msgid "Shelf %(title)s created"
msgstr "Étagère %(title)s créée" msgstr "Étagère %(title)s créée"
#: cps/web.py:1077 #: cps/web.py:1233
msgid "There was an error" msgid "There was an error"
msgstr "Il y a eu une erreur" msgstr "Il y a eu une erreur"
#: cps/web.py:1078 cps/web.py:1080 #: cps/web.py:1234 cps/web.py:1236
msgid "create a shelf" msgid "create a shelf"
msgstr "Créer une étagère" msgstr "Créer une étagère"
#: cps/web.py:1096 #: cps/web.py:1256
#, python-format #, python-format
msgid "successfully deleted shelf %(name)s" msgid "successfully deleted shelf %(name)s"
msgstr "L'étagère %(name)s a été supprimé avec succès" msgstr "L'étagère %(name)s a été supprimé avec succès"
#: cps/web.py:1113 #: cps/web.py:1277
#, python-format #, python-format
msgid "Shelf: '%(name)s'" msgid "Shelf: '%(name)s'"
msgstr "Étagère : '%(name)s'" msgstr "Étagère : '%(name)s'"
#: cps/web.py:1150 #: cps/web.py:1332
msgid "Found an existing account for this email address." msgid "Found an existing account for this email address."
msgstr "Un compte avec cette adresse de courriel existe déjà." msgstr "Un compte avec cette adresse de courriel existe déjà."
#: cps/web.py:1151 cps/web.py:1153 #: cps/web.py:1334 cps/web.py:1337
#, python-format #, python-format
msgid "%(name)s's profile" msgid "%(name)s's profile"
msgstr "Profil de %(name)s" msgstr "Profil de %(name)s"
#: cps/web.py:1152 #: cps/web.py:1335
msgid "Profile updated" msgid "Profile updated"
msgstr "Profil mis à jour" msgstr "Profil mis à jour"
#: cps/web.py:1161 #: cps/web.py:1346
msgid "User list" msgid "User list"
msgstr "Liste des ustilisateurs" msgstr "Liste des ustilisateurs"
#: cps/templates/user_list.html:32 cps/web.py:1180 #: cps/templates/user_list.html:32 cps/web.py:1366
msgid "Add new user" msgid "Add new user"
msgstr "Ajouter un nouvel utilisateur" msgstr "Ajouter un nouvel utilisateur"
#: cps/web.py:1213 #: cps/web.py:1399
#, 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/web.py:1217 #: cps/web.py:1403
msgid "Found an existing account for this email address or nickname." msgid "Found an existing account for this email address or nickname."
msgstr "Un compte avec cette adresse de courriel ou ce surnom existe déjà." msgstr "Un compte avec cette adresse de courriel ou ce surnom existe déjà."
#: cps/web.py:1238 #: cps/web.py:1426 cps/web.py:1437
msgid "Mail settings updated" msgid "Mail settings updated"
msgstr "Paramètres de courriel mis à jour" msgstr "Paramètres de courriel mis à jour"
#: cps/web.py:1241 #: cps/web.py:1432
#, python-format
msgid "Test E-Mail successfully send to %(kindlemail)s"
msgstr ""
#: cps/web.py:1435
#, python-format
msgid "There was an error sending the Test E-Mail: %(res)s"
msgstr ""
#: cps/web.py:1438
msgid "Edit mail settings" msgid "Edit mail settings"
msgstr "Éditer les paramètres de courriel" msgstr "Éditer les paramètres de courriel"
#: cps/web.py:1263 #: cps/web.py:1461
#, 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/web.py:1318 #: cps/web.py:1516
#, 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/web.py:1321 #: cps/web.py:1519
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/web.py:1322 #: cps/web.py:1521
#, 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/web.py:1556 #: cps/web.py:1759
#, python-format #, python-format
msgid "Failed to create path %s (Permission denied)." msgid "Failed to create path %s (Permission denied)."
msgstr "Impossible de créer le chemin %s (permission refusée)" msgstr "Impossible de créer le chemin %s (permission refusée)"
#: cps/web.py:1561 #: cps/web.py:1764
#, python-format #, python-format
msgid "Failed to store file %s (Permission denied)." msgid "Failed to store file %s (Permission denied)."
msgstr "Impossible d'enregistrer le fichier %s (permission refusée)" msgstr "Impossible d'enregistrer le fichier %s (permission refusée)"
#: cps/web.py:1566 #: cps/web.py:1769
#, python-format #, python-format
msgid "Failed to delete file %s (Permission denied)." msgid "Failed to delete file %s (Permission denied)."
msgstr "Impossible de supprimer le fichier %s (permission refusée)" msgstr "Impossible de supprimer le fichier %s (permission refusée)"
@ -341,14 +366,14 @@ msgstr "Non"
msgid "view book after edit" msgid "view book after edit"
msgstr "Voir le livre après l'édition" msgstr "Voir le livre après l'édition"
#: cps/templates/edit_book.html:105 cps/templates/email_edit.html:30 #: cps/templates/edit_book.html:105 cps/templates/login.html:19
#: cps/templates/login.html:19 cps/templates/search_form.html:33 #: cps/templates/search_form.html:33 cps/templates/shelf_edit.html:15
#: cps/templates/shelf_edit.html:15 cps/templates/user_edit.html:93 #: cps/templates/user_edit.html:94
msgid "Submit" msgid "Submit"
msgstr "Soumettre" msgstr "Soumettre"
#: cps/templates/edit_book.html:106 cps/templates/email_edit.html:31 #: cps/templates/edit_book.html:106 cps/templates/email_edit.html:32
#: cps/templates/user_edit.html:95 #: cps/templates/user_edit.html:96
msgid "Back" msgid "Back"
msgstr "Retour" msgstr "Retour"
@ -376,6 +401,14 @@ msgstr "Mot de passe smtp"
msgid "From e-mail" msgid "From e-mail"
msgstr "Expéditeur des courriels" msgstr "Expéditeur des courriels"
#: cps/templates/email_edit.html:30
msgid "Save settings"
msgstr ""
#: cps/templates/email_edit.html:31
msgid "Save settings and send Test E-Mail"
msgstr ""
#: cps/templates/feed.xml:14 #: cps/templates/feed.xml:14
msgid "Next" msgid "Next"
msgstr "Suivant" msgstr "Suivant"
@ -506,6 +539,14 @@ msgstr "Mot de passe"
msgid "Remember me" msgid "Remember me"
msgstr "Se rappeler de moi" msgstr "Se rappeler de moi"
#: cps/templates/read.html:136
msgid "Reflow text when sidebars are open."
msgstr ""
#: cps/templates/readpdf.html:29
msgid "PDF.js viewer"
msgstr ""
#: cps/templates/register.html:4 #: cps/templates/register.html:4
msgid "Register a new account" msgid "Register a new account"
msgstr "Enregistrer un nouveau compte" msgstr "Enregistrer un nouveau compte"
@ -546,6 +587,10 @@ msgstr "Exclure des étiquettes"
msgid "Delete this Shelf" msgid "Delete this Shelf"
msgstr "Effacer cette étagère" msgstr "Effacer cette étagère"
#: cps/templates/shelf.html:7
msgid "Edit Shelf name"
msgstr ""
#: cps/templates/shelf_edit.html:7 #: cps/templates/shelf_edit.html:7
msgid "Title" msgid "Title"
msgstr "Titre" msgstr "Titre"
@ -554,11 +599,27 @@ msgstr "Titre"
msgid "should the shelf be public?" msgid "should the shelf be public?"
msgstr "Cette étagère doit-elle être publique ?" msgstr "Cette étagère doit-elle être publique ?"
#: cps/templates/stats.html:4 #: cps/templates/stats.html:3
msgid "Linked libraries"
msgstr ""
#: cps/templates/stats.html:8
msgid "Program library"
msgstr ""
#: cps/templates/stats.html:9
msgid "Installed Version"
msgstr ""
#: cps/templates/stats.html:32
msgid "Calibre library statistics"
msgstr ""
#: cps/templates/stats.html:37
msgid "Books in this Library" msgid "Books in this Library"
msgstr "Livres dans la bibiothèque" msgstr "Livres dans la bibiothèque"
#: cps/templates/stats.html:5 #: cps/templates/stats.html:41
msgid "Authors in this Library" msgid "Authors in this Library"
msgstr "Auteurs dans la bibliothèque" msgstr "Auteurs dans la bibliothèque"
@ -574,51 +635,51 @@ msgstr "Montrer les livres dans la langue"
msgid "Show all" msgid "Show all"
msgstr "Montrer tout" msgstr "Montrer tout"
#: cps/templates/user_edit.html:46 #: cps/templates/user_edit.html:45
msgid "Show random books" msgid "Show random books"
msgstr "Montrer des livres au hasard" msgstr "Montrer des livres au hasard"
#: cps/templates/user_edit.html:50 #: cps/templates/user_edit.html:49
msgid "Show hot books" msgid "Show hot books"
msgstr "Montrer les livres populaires" msgstr "Montrer les livres populaires"
#: cps/templates/user_edit.html:54 #: cps/templates/user_edit.html:53
msgid "Show language selection" msgid "Show language selection"
msgstr "Montrer la sélection de la langue" msgstr "Montrer la sélection de la langue"
#: cps/templates/user_edit.html:58 #: cps/templates/user_edit.html:57
msgid "Show series selection" msgid "Show series selection"
msgstr "Montrer la sélection des séries" msgstr "Montrer la sélection des séries"
#: cps/templates/user_edit.html:62 #: cps/templates/user_edit.html:61
msgid "Show category selection" msgid "Show category selection"
msgstr "Montrer la sélection des catégories" msgstr "Montrer la sélection des catégories"
#: cps/templates/user_edit.html:67 #: cps/templates/user_edit.html:68
msgid "Admin user" msgid "Admin user"
msgstr "Utilisateur admin" msgstr "Utilisateur admin"
#: cps/templates/user_edit.html:71 #: cps/templates/user_edit.html:72
msgid "Allow Downloads" msgid "Allow Downloads"
msgstr "Permettre les téléchargements" msgstr "Permettre les téléchargements"
#: cps/templates/user_edit.html:75 #: cps/templates/user_edit.html:76
msgid "Allow Uploads" msgid "Allow Uploads"
msgstr "Permettre les téléversements" msgstr "Permettre les téléversements"
#: cps/templates/user_edit.html:79 #: cps/templates/user_edit.html:80
msgid "Allow Edit" msgid "Allow Edit"
msgstr "Permettre l'édition" msgstr "Permettre l'édition"
#: cps/templates/user_edit.html:83 #: cps/templates/user_edit.html:84
msgid "Allow Changing Password" msgid "Allow Changing Password"
msgstr "Permettre le changement de mot de passe" msgstr "Permettre le changement de mot de passe"
#: cps/templates/user_edit.html:89 #: cps/templates/user_edit.html:90
msgid "Delete this user" msgid "Delete this user"
msgstr "Supprimer cet utilisateur" msgstr "Supprimer cet utilisateur"
#: cps/templates/user_edit.html:100 #: cps/templates/user_edit.html:101
msgid "Recent Downloads" msgid "Recent Downloads"
msgstr "Téléchargements récents" msgstr "Téléchargements récents"
@ -668,3 +729,4 @@ msgstr "Changer les paramètre smtp"
msgid "Latin" msgid "Latin"
msgstr "Latin" msgstr "Latin"

@ -21,6 +21,7 @@ ROLE_EDIT = 8
ROLE_PASSWD = 16 ROLE_PASSWD = 16
DEFAULT_PASS = "admin123" DEFAULT_PASS = "admin123"
class User(Base): class User(Base):
__tablename__ = 'user' __tablename__ = 'user'
@ -43,26 +44,31 @@ class User(Base):
def is_authenticated(self): def is_authenticated(self):
return True return True
def role_admin(self): def role_admin(self):
if self.role is not None: if self.role is not None:
return True if self.role & ROLE_ADMIN == ROLE_ADMIN else False return True if self.role & ROLE_ADMIN == ROLE_ADMIN else False
else: else:
return False return False
def role_download(self): def role_download(self):
if self.role is not None: if self.role is not None:
return True if self.role & ROLE_DOWNLOAD == ROLE_DOWNLOAD else False return True if self.role & ROLE_DOWNLOAD == ROLE_DOWNLOAD else False
else: else:
return False return False
def role_upload(self): def role_upload(self):
if self.role is not None: if self.role is not None:
return True if self.role & ROLE_UPLOAD == ROLE_UPLOAD else False return True if self.role & ROLE_UPLOAD == ROLE_UPLOAD else False
else: else:
return False return False
def role_edit(self): def role_edit(self):
if self.role is not None: if self.role is not None:
return True if self.role & ROLE_EDIT == ROLE_EDIT else False return True if self.role & ROLE_EDIT == ROLE_EDIT else False
else: else:
return False return False
def role_passwd(self): def role_passwd(self):
if self.role is not None: if self.role is not None:
return True if self.role & ROLE_PASSWD == ROLE_PASSWD else False return True if self.role & ROLE_PASSWD == ROLE_PASSWD else False
@ -96,9 +102,9 @@ class User(Base):
def show_category(self): def show_category(self):
return self.category_books return self.category_books
def __repr__(self): def __repr__(self):
return '<User %r>' % (self.nickname) return '<User %r>' % self.nickname
class Shelf(Base): class Shelf(Base):
__tablename__ = 'shelf' __tablename__ = 'shelf'
@ -109,7 +115,7 @@ class Shelf(Base):
user_id = Column(Integer, ForeignKey('user.id')) user_id = Column(Integer, ForeignKey('user.id'))
def __repr__(self): def __repr__(self):
return '<Shelf %r>' % (self.name) return '<Shelf %r>' % self.name
class Whislist(Base): class Whislist(Base):
@ -124,7 +130,7 @@ class Whislist(Base):
pass pass
def __repr__(self): def __repr__(self):
return '<Whislist %r>' % (self.name) return '<Whislist %r>' % self.name
class BookShelf(Base): class BookShelf(Base):
@ -135,7 +141,7 @@ class BookShelf(Base):
shelf = Column(Integer, ForeignKey('shelf.id')) shelf = Column(Integer, ForeignKey('shelf.id'))
def __repr__(self): def __repr__(self):
return '<Book %r>' % (self.id) return '<Book %r>' % self.id
class Downloads(Base): class Downloads(Base):
@ -146,7 +152,8 @@ class Downloads(Base):
user_id = Column(Integer, ForeignKey('user.id')) user_id = Column(Integer, ForeignKey('user.id'))
def __repr__(self): def __repr__(self):
return '<Download %r' % (self.book_id) return '<Download %r' % self.book_id
class Whish(Base): class Whish(Base):
__tablename__ = 'whish' __tablename__ = 'whish'
@ -157,7 +164,8 @@ class Whish(Base):
wishlist = Column(Integer, ForeignKey('wishlist.id')) wishlist = Column(Integer, ForeignKey('wishlist.id'))
def __repr__(self): def __repr__(self):
return '<Whish %r>' % (self.title) return '<Whish %r>' % self.title
class Settings(Base): class Settings(Base):
__tablename__ = 'settings' __tablename__ = 'settings'
@ -174,6 +182,7 @@ class Settings(Base):
#return '<Smtp %r>' % (self.mail_server) #return '<Smtp %r>' % (self.mail_server)
pass pass
def migrate_Database(): def migrate_Database():
try: try:
session.query(exists().where(User.random_books)).scalar() session.query(exists().where(User.random_books)).scalar()
@ -208,6 +217,7 @@ def create_default_config():
session.add(settings) session.add(settings)
session.commit() session.commit()
def get_mail_settings(): def get_mail_settings():
settings = session.query(Settings).first() settings = session.query(Settings).first()
@ -225,6 +235,7 @@ def get_mail_settings():
return data return data
def create_admin_user(): def create_admin_user():
user = User() user = User()
user.nickname = "admin" user.nickname = "admin"
@ -251,4 +262,3 @@ if not os.path.exists(dbpath):
pass pass
else: else:
migrate_Database() migrate_Database()

@ -9,6 +9,8 @@ BookMeta = namedtuple('BookMeta', 'file_path, extension, title, author, cover, d
""" """
:rtype: BookMeta :rtype: BookMeta
""" """
def upload(file): def upload(file):
tmp_dir = os.path.join(gettempdir(), 'calibre_web') tmp_dir = os.path.join(gettempdir(), 'calibre_web')
@ -23,7 +25,3 @@ def upload(file):
file.save(tmp_file_path) file.save(tmp_file_path)
meta = book_formats.process(tmp_file_path, filename_root, file_extension) meta = book_formats.process(tmp_file_path, filename_root, file_extension)
return meta return meta

File diff suppressed because it is too large Load Diff

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PROJECT VERSION\n" "Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2016-11-12 09:44+0100\n" "POT-Creation-Date: 2016-12-23 08:39+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -17,244 +17,266 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.3.4\n" "Generated-By: Babel 2.3.4\n"
#: cps/helper.py:80 cps/templates/detail.html:113 #: cps/book_formats.py:108 cps/book_formats.py:112 cps/web.py:923
msgid "Send to Kindle" msgid "not installed"
msgstr ""
#: cps/helper.py:93
msgid "Calibre-web test email"
msgstr "" msgstr ""
#: cps/helper.py:81 #: cps/helper.py:94 cps/helper.py:147
msgid "This email has been sent via calibre web." msgid "This email has been sent via calibre web."
msgstr "" msgstr ""
#: cps/helper.py:103 cps/helper.py:118 #: cps/helper.py:128 cps/helper.py:216
msgid "Could not find any formats suitable for sending by email" #, python-format
msgid "Failed to send mail: %s"
msgstr "" msgstr ""
#: cps/helper.py:112 #: cps/helper.py:146 cps/templates/detail.html:113
msgid "Could not convert epub to mobi" msgid "Send to Kindle"
msgstr "" msgstr ""
#: cps/helper.py:142 #: cps/helper.py:169 cps/helper.py:184
#, python-format msgid "Could not find any formats suitable for sending by email"
msgid "Failed to send mail: %s"
msgstr "" msgstr ""
#: cps/helper.py:162 #: cps/helper.py:178
msgid "Could not convert epub to mobi"
msgstr ""
#: cps/helper.py:236
msgid "The requested file could not be read. Maybe wrong permissions?" msgid "The requested file could not be read. Maybe wrong permissions?"
msgstr "" msgstr ""
#: cps/web.py:639 #: cps/web.py:717
msgid "Latest Books" msgid "Latest Books"
msgstr "" msgstr ""
#: cps/web.py:661 #: cps/web.py:742
msgid "Hot Books (most downloaded)" msgid "Hot Books (most downloaded)"
msgstr "" msgstr ""
#: cps/templates/index.xml:41 cps/web.py:668 #: cps/templates/index.xml:41 cps/web.py:750
msgid "Random Books" msgid "Random Books"
msgstr "" msgstr ""
#: cps/web.py:679 #: cps/web.py:763
msgid "Author list" msgid "Author list"
msgstr "" msgstr ""
#: cps/web.py:695 #: cps/web.py:780
#, python-format #, python-format
msgid "Author: %(nam)s" msgid "Author: %(nam)s"
msgstr "" msgstr ""
#: cps/templates/index.xml:65 cps/web.py:706 #: cps/templates/index.xml:65 cps/web.py:793
msgid "Series list" msgid "Series list"
msgstr "" msgstr ""
#: cps/web.py:714 #: cps/web.py:804
#, python-format #, python-format
msgid "Series: %(serie)s" msgid "Series: %(serie)s"
msgstr "" msgstr ""
#: cps/web.py:716 cps/web.py:796 cps/web.py:914 cps/web.py:1524 #: cps/web.py:806 cps/web.py:902 cps/web.py:1061 cps/web.py:1729
msgid "Error opening eBook. File does not exist or file is not accessible:" msgid "Error opening eBook. File does not exist or file is not accessible:"
msgstr "" msgstr ""
#: cps/web.py:742 #: cps/web.py:837
msgid "Available languages" msgid "Available languages"
msgstr "" msgstr ""
#: cps/web.py:754 #: cps/web.py:852
#, python-format #, python-format
msgid "Language: %(name)s" msgid "Language: %(name)s"
msgstr "" msgstr ""
#: cps/templates/index.xml:57 cps/web.py:765 #: cps/templates/index.xml:57 cps/web.py:865
msgid "Category list" msgid "Category list"
msgstr "" msgstr ""
#: cps/web.py:772 #: cps/web.py:875
#, python-format #, python-format
msgid "Category: %(name)s" msgid "Category: %(name)s"
msgstr "" msgstr ""
#: cps/web.py:810 #: cps/web.py:931
msgid "Statistics" msgid "Statistics"
msgstr "" msgstr ""
#: cps/web.py:898 cps/web.py:905 cps/web.py:912 #: cps/web.py:939
msgid "Server restarts"
msgstr ""
#: cps/web.py:1037 cps/web.py:1044 cps/web.py:1051 cps/web.py:1058
msgid "Read a Book" msgid "Read a Book"
msgstr "" msgstr ""
#: cps/web.py:951 cps/web.py:1179 #: cps/web.py:1100 cps/web.py:1365
msgid "Please fill out all fields!" msgid "Please fill out all fields!"
msgstr "" msgstr ""
#: cps/web.py:967 #: cps/web.py:1116
msgid "An unknown error occured. Please try again later." msgid "An unknown error occured. Please try again later."
msgstr "" msgstr ""
#: cps/web.py:972 #: cps/web.py:1121
msgid "This username or email address is already in use." msgid "This username or email address is already in use."
msgstr "" msgstr ""
#: cps/web.py:975 #: cps/web.py:1124
msgid "register" msgid "register"
msgstr "" msgstr ""
#: cps/web.py:990 #: cps/web.py:1140
#, python-format #, python-format
msgid "you are now logged in as: '%(nickname)s'" msgid "you are now logged in as: '%(nickname)s'"
msgstr "" msgstr ""
#: cps/web.py:993 #: cps/web.py:1143
msgid "Wrong Username or Password" msgid "Wrong Username or Password"
msgstr "" msgstr ""
#: cps/web.py:995 #: cps/web.py:1145
msgid "login" msgid "login"
msgstr "" msgstr ""
#: cps/web.py:1011 #: cps/web.py:1162
msgid "Please configure the SMTP mail settings first..." msgid "Please configure the SMTP mail settings first..."
msgstr "" msgstr ""
#: cps/web.py:1015 #: cps/web.py:1166
#, python-format #, python-format
msgid "Book successfully send to %(kindlemail)s" msgid "Book successfully send to %(kindlemail)s"
msgstr "" msgstr ""
#: cps/web.py:1018 #: cps/web.py:1170
#, python-format #, python-format
msgid "There was an error sending this book: %(res)s" msgid "There was an error sending this book: %(res)s"
msgstr "" msgstr ""
#: cps/web.py:1020 #: cps/web.py:1172
msgid "Please configure your kindle email address first..." msgid "Please configure your kindle email address first..."
msgstr "" msgstr ""
#: cps/web.py:1035 #: cps/web.py:1188
#, python-format #, python-format
msgid "Book has been added to shelf: %(sname)s" msgid "Book has been added to shelf: %(sname)s"
msgstr "" msgstr ""
#: cps/web.py:1054 #: cps/web.py:1209
#, python-format #, python-format
msgid "Book has been removed from shelf: %(sname)s" msgid "Book has been removed from shelf: %(sname)s"
msgstr "" msgstr ""
#: cps/web.py:1070 #: cps/web.py:1226
#, python-format #, python-format
msgid "A shelf with the name '%(title)s' already exists." msgid "A shelf with the name '%(title)s' already exists."
msgstr "" msgstr ""
#: cps/web.py:1075 #: cps/web.py:1231
#, python-format #, python-format
msgid "Shelf %(title)s created" msgid "Shelf %(title)s created"
msgstr "" msgstr ""
#: cps/web.py:1077 #: cps/web.py:1233
msgid "There was an error" msgid "There was an error"
msgstr "" msgstr ""
#: cps/web.py:1078 cps/web.py:1080 #: cps/web.py:1234 cps/web.py:1236
msgid "create a shelf" msgid "create a shelf"
msgstr "" msgstr ""
#: cps/web.py:1096 #: cps/web.py:1256
#, python-format #, python-format
msgid "successfully deleted shelf %(name)s" msgid "successfully deleted shelf %(name)s"
msgstr "" msgstr ""
#: cps/web.py:1113 #: cps/web.py:1277
#, python-format #, python-format
msgid "Shelf: '%(name)s'" msgid "Shelf: '%(name)s'"
msgstr "" msgstr ""
#: cps/web.py:1150 #: cps/web.py:1332
msgid "Found an existing account for this email address." msgid "Found an existing account for this email address."
msgstr "" msgstr ""
#: cps/web.py:1151 cps/web.py:1153 #: cps/web.py:1334 cps/web.py:1337
#, python-format #, python-format
msgid "%(name)s's profile" msgid "%(name)s's profile"
msgstr "" msgstr ""
#: cps/web.py:1152 #: cps/web.py:1335
msgid "Profile updated" msgid "Profile updated"
msgstr "" msgstr ""
#: cps/web.py:1161 #: cps/web.py:1346
msgid "User list" msgid "User list"
msgstr "" msgstr ""
#: cps/templates/user_list.html:32 cps/web.py:1180 #: cps/templates/user_list.html:32 cps/web.py:1366
msgid "Add new user" msgid "Add new user"
msgstr "" msgstr ""
#: cps/web.py:1213 #: cps/web.py:1399
#, python-format #, python-format
msgid "User '%(user)s' created" msgid "User '%(user)s' created"
msgstr "" msgstr ""
#: cps/web.py:1217 #: cps/web.py:1403
msgid "Found an existing account for this email address or nickname." msgid "Found an existing account for this email address or nickname."
msgstr "" msgstr ""
#: cps/web.py:1238 #: cps/web.py:1426 cps/web.py:1437
msgid "Mail settings updated" msgid "Mail settings updated"
msgstr "" msgstr ""
#: cps/web.py:1241 #: cps/web.py:1432
#, python-format
msgid "Test E-Mail successfully send to %(kindlemail)s"
msgstr ""
#: cps/web.py:1435
#, python-format
msgid "There was an error sending the Test E-Mail: %(res)s"
msgstr ""
#: cps/web.py:1438
msgid "Edit mail settings" msgid "Edit mail settings"
msgstr "" msgstr ""
#: cps/web.py:1263 #: cps/web.py:1461
#, python-format #, python-format
msgid "User '%(nick)s' deleted" msgid "User '%(nick)s' deleted"
msgstr "" msgstr ""
#: cps/web.py:1318 #: cps/web.py:1516
#, python-format #, python-format
msgid "User '%(nick)s' updated" msgid "User '%(nick)s' updated"
msgstr "" msgstr ""
#: cps/web.py:1321 #: cps/web.py:1519
msgid "An unknown error occured." msgid "An unknown error occured."
msgstr "" msgstr ""
#: cps/web.py:1322 #: cps/web.py:1521
#, python-format #, python-format
msgid "Edit User %(nick)s" msgid "Edit User %(nick)s"
msgstr "" msgstr ""
#: cps/web.py:1556 #: cps/web.py:1759
#, python-format #, python-format
msgid "Failed to create path %s (Permission denied)." msgid "Failed to create path %s (Permission denied)."
msgstr "" msgstr ""
#: cps/web.py:1561 #: cps/web.py:1764
#, python-format #, python-format
msgid "Failed to store file %s (Permission denied)." msgid "Failed to store file %s (Permission denied)."
msgstr "" msgstr ""
#: cps/web.py:1566 #: cps/web.py:1769
#, python-format #, python-format
msgid "Failed to delete file %s (Permission denied)." msgid "Failed to delete file %s (Permission denied)."
msgstr "" msgstr ""
@ -339,14 +361,14 @@ msgstr ""
msgid "view book after edit" msgid "view book after edit"
msgstr "" msgstr ""
#: cps/templates/edit_book.html:105 cps/templates/email_edit.html:30 #: cps/templates/edit_book.html:105 cps/templates/login.html:19
#: cps/templates/login.html:19 cps/templates/search_form.html:33 #: cps/templates/search_form.html:33 cps/templates/shelf_edit.html:15
#: cps/templates/shelf_edit.html:15 cps/templates/user_edit.html:93 #: cps/templates/user_edit.html:94
msgid "Submit" msgid "Submit"
msgstr "" msgstr ""
#: cps/templates/edit_book.html:106 cps/templates/email_edit.html:31 #: cps/templates/edit_book.html:106 cps/templates/email_edit.html:32
#: cps/templates/user_edit.html:95 #: cps/templates/user_edit.html:96
msgid "Back" msgid "Back"
msgstr "" msgstr ""
@ -374,6 +396,14 @@ msgstr ""
msgid "From e-mail" msgid "From e-mail"
msgstr "" msgstr ""
#: cps/templates/email_edit.html:30
msgid "Save settings"
msgstr ""
#: cps/templates/email_edit.html:31
msgid "Save settings and send Test E-Mail"
msgstr ""
#: cps/templates/feed.xml:14 #: cps/templates/feed.xml:14
msgid "Next" msgid "Next"
msgstr "" msgstr ""
@ -504,6 +534,14 @@ msgstr ""
msgid "Remember me" msgid "Remember me"
msgstr "" msgstr ""
#: cps/templates/read.html:136
msgid "Reflow text when sidebars are open."
msgstr ""
#: cps/templates/readpdf.html:29
msgid "PDF.js viewer"
msgstr ""
#: cps/templates/register.html:4 #: cps/templates/register.html:4
msgid "Register a new account" msgid "Register a new account"
msgstr "" msgstr ""
@ -544,6 +582,10 @@ msgstr ""
msgid "Delete this Shelf" msgid "Delete this Shelf"
msgstr "" msgstr ""
#: cps/templates/shelf.html:7
msgid "Edit Shelf name"
msgstr ""
#: cps/templates/shelf_edit.html:7 #: cps/templates/shelf_edit.html:7
msgid "Title" msgid "Title"
msgstr "" msgstr ""
@ -552,11 +594,27 @@ msgstr ""
msgid "should the shelf be public?" msgid "should the shelf be public?"
msgstr "" msgstr ""
#: cps/templates/stats.html:4 #: cps/templates/stats.html:3
msgid "Linked libraries"
msgstr ""
#: cps/templates/stats.html:8
msgid "Program library"
msgstr ""
#: cps/templates/stats.html:9
msgid "Installed Version"
msgstr ""
#: cps/templates/stats.html:32
msgid "Calibre library statistics"
msgstr ""
#: cps/templates/stats.html:37
msgid "Books in this Library" msgid "Books in this Library"
msgstr "" msgstr ""
#: cps/templates/stats.html:5 #: cps/templates/stats.html:41
msgid "Authors in this Library" msgid "Authors in this Library"
msgstr "" msgstr ""
@ -572,51 +630,51 @@ msgstr ""
msgid "Show all" msgid "Show all"
msgstr "" msgstr ""
#: cps/templates/user_edit.html:46 #: cps/templates/user_edit.html:45
msgid "Show random books" msgid "Show random books"
msgstr "" msgstr ""
#: cps/templates/user_edit.html:50 #: cps/templates/user_edit.html:49
msgid "Show hot books" msgid "Show hot books"
msgstr "" msgstr ""
#: cps/templates/user_edit.html:54 #: cps/templates/user_edit.html:53
msgid "Show language selection" msgid "Show language selection"
msgstr "" msgstr ""
#: cps/templates/user_edit.html:58 #: cps/templates/user_edit.html:57
msgid "Show series selection" msgid "Show series selection"
msgstr "" msgstr ""
#: cps/templates/user_edit.html:62 #: cps/templates/user_edit.html:61
msgid "Show category selection" msgid "Show category selection"
msgstr "" msgstr ""
#: cps/templates/user_edit.html:67 #: cps/templates/user_edit.html:68
msgid "Admin user" msgid "Admin user"
msgstr "" msgstr ""
#: cps/templates/user_edit.html:71 #: cps/templates/user_edit.html:72
msgid "Allow Downloads" msgid "Allow Downloads"
msgstr "" msgstr ""
#: cps/templates/user_edit.html:75 #: cps/templates/user_edit.html:76
msgid "Allow Uploads" msgid "Allow Uploads"
msgstr "" msgstr ""
#: cps/templates/user_edit.html:79 #: cps/templates/user_edit.html:80
msgid "Allow Edit" msgid "Allow Edit"
msgstr "" msgstr ""
#: cps/templates/user_edit.html:83 #: cps/templates/user_edit.html:84
msgid "Allow Changing Password" msgid "Allow Changing Password"
msgstr "" msgstr ""
#: cps/templates/user_edit.html:89 #: cps/templates/user_edit.html:90
msgid "Delete this user" msgid "Delete this user"
msgstr "" msgstr ""
#: cps/templates/user_edit.html:100 #: cps/templates/user_edit.html:101
msgid "Recent Downloads" msgid "Recent Downloads"
msgstr "" msgstr ""
@ -664,5 +722,3 @@ msgstr ""
msgid "Change SMTP settings" msgid "Change SMTP settings"
msgstr "" msgstr ""
msgid "Latin"
msgstr ""

Loading…
Cancel
Save