Commit ba44a158 authored by OzzieIsaacs's avatar OzzieIsaacs

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
parent c582ccf7
......@@ -17,6 +17,7 @@ eggs/
*.db
*.log
config.ini
cps/static/[0-9]*
.idea/
*.bak
......
......@@ -2,10 +2,14 @@
import os
import sys
from threading import Thread
from multiprocessing import Queue
import time
base_path = os.path.dirname(os.path.abspath(__file__))
# Insert local directories into path
sys.path.insert(0,os.path.join(base_path, 'vendor'))
sys.path.insert(0, os.path.join(base_path, 'vendor'))
from cps import web
from cps import config
......@@ -15,11 +19,47 @@ from tornado.ioloop import IOLoop
global title_sort
def title_sort(title):
return title
if config.DEVELOPMENT:
web.app.run(host="0.0.0.0",port=config.PORT, debug=True)
else:
http_server = HTTPServer(WSGIContainer(web.app))
http_server.listen(config.PORT)
IOLoop.instance().start()
\ No newline at end of file
def start_calibreweb(messagequeue):
web.global_queue = messagequeue
if config.DEVELOPMENT:
web.app.run(host="0.0.0.0", port=config.PORT, debug=True)
else:
http_server = HTTPServer(WSGIContainer(web.app))
http_server.listen(config.PORT)
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()
import logging
import uploader
import os
from flask_babel import gettext as _
__author__ = 'lemmsh'
import logging
logger = logging.getLogger("book_formats")
import uploader
import os
try:
from wand.image import Image
from wand import version as ImageVersion
use_generic_pdf_cover = False
except ImportError, e:
logger.warning('cannot import Image, generating pdf covers for pdf uploads will not work: %s', e)
use_generic_pdf_cover = True
try:
from PyPDF2 import PdfFileReader
from PyPDF2 import __version__ as PyPdfVersion
use_pdf_meta = True
except ImportError, 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:
if ".PDF" == original_file_extension.upper():
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)
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)
except Exception, e:
logger.warning('cannot parse metadata, using default: %s', e)
......@@ -47,29 +51,28 @@ def process(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):
return uploader.BookMeta(
file_path = tmp_file_path,
extension = original_file_extension,
title = original_file_name,
author = "Unknown",
cover = None,
description = "",
tags = "",
series = "",
file_path=tmp_file_path,
extension=original_file_extension,
title=original_file_name,
author="Unknown",
cover=None,
description="",
tags="",
series="",
series_id="")
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'))
doc_info = pdf.getDocumentInfo()
else:
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"
title = doc_info.title if doc_info.title is not None else original_file_name
subject = doc_info.subject
......@@ -78,16 +81,17 @@ def pdf_meta(tmp_file_path, original_file_name, original_file_extension):
title = original_file_name
subject = ""
return uploader.BookMeta(
file_path = tmp_file_path,
extension = original_file_extension,
title = title,
author = author,
cover = pdf_preview(tmp_file_path, original_file_name),
description = subject,
tags = "",
series = "",
file_path=tmp_file_path,
extension=original_file_extension,
title=title,
author=author,
cover=pdf_preview(tmp_file_path, original_file_name),
description=subject,
tags="",
series="",
series_id="")
def pdf_preview(tmp_file_path, tmp_dir):
if use_generic_pdf_cover:
return None
......@@ -97,3 +101,14 @@ def pdf_preview(tmp_file_path, tmp_dir):
img.compression_quality = 88
img.save(filename=os.path.join(tmp_dir, 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}
\ No newline at end of file
......@@ -5,9 +5,10 @@ import os
import sys
from configobj import ConfigObj
CONFIG_FILE= os.path.join(os.path.normpath(os.path.dirname(os.path.realpath(__file__))+os.sep+".."+os.sep), "config.ini")
CONFIG_FILE = os.path.join(os.path.normpath(os.path.dirname(os.path.realpath(__file__))+os.sep+".."+os.sep), "config.ini")
CFG = ConfigObj(CONFIG_FILE)
CFG.encoding='UTF-8'
CFG.encoding = 'UTF-8'
def CheckSection(sec):
""" Check if INI section exists, if not create it """
......@@ -18,6 +19,7 @@ def CheckSection(sec):
CFG[sec] = {}
return False
def check_setting_str(config, cfg_name, item_name, def_val, log=True):
try:
my_val = config[cfg_name][item_name].decode('UTF-8')
......@@ -62,24 +64,16 @@ PUBLIC_REG = bool(check_setting_int(CFG, 'Advanced', 'PUBLIC_REG', 0))
UPLOADING = bool(check_setting_int(CFG, 'Advanced', 'UPLOADING', 0))
ANON_BROWSE = bool(check_setting_int(CFG, 'Advanced', 'ANON_BROWSE', 0))
SYS_ENCODING="UTF-8"
SYS_ENCODING = "UTF-8"
if DB_ROOT == "":
print "Calibre database directory (DB_ROOT) is not configured"
sys.exit(1)
configval={}
configval["DB_ROOT"] = DB_ROOT
configval["APP_DB_ROOT"] = APP_DB_ROOT
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
configval = {"DB_ROOT": DB_ROOT, "APP_DB_ROOT": APP_DB_ROOT, "MAIN_DIR": MAIN_DIR, "LOG_DIR": LOG_DIR, "PORT": PORT,
"NEWEST_BOOKS": NEWEST_BOOKS, "DEVELOPMENT": DEVELOPMENT, "TITLE_REGEX": TITLE_REGEX,
"PUBLIC_REG": PUBLIC_REG, "UPLOADING": UPLOADING, "ANON_BROWSE": ANON_BROWSE}
def save_config(configval):
new_config = ConfigObj(encoding='UTF-8')
......
......@@ -9,8 +9,10 @@ import config
import re
import ast
#calibre sort stuff
# calibre sort stuff
title_pat = re.compile(config.TITLE_REGEX, re.IGNORECASE)
def title_sort(title):
match = title_pat.search(title)
if match:
......@@ -52,7 +54,7 @@ books_languages_link = Table('books_languages_link', Base.metadata,
cc = conn.execute("SELECT id, datatype FROM custom_columns")
cc_ids = []
cc_exceptions = [ 'datetime', 'int', 'comments', 'float', 'composite','series' ]
cc_exceptions = ['datetime', 'int', 'comments', 'float', 'composite', 'series']
books_custom_column_links = {}
cc_classes = {}
for row in cc:
......@@ -61,18 +63,19 @@ for row in cc:
Column('book', Integer, ForeignKey('books.id'), primary_key=True),
Column('value', Integer, ForeignKey('custom_column_' + str(row.id) + '.id'), primary_key=True)
)
cc_ids.append([row.id,row.datatype])
cc_ids.append([row.id, row.datatype])
if row.datatype == 'bool':
ccdict = {'__tablename__': 'custom_column_' + str(row.id),
'id': Column(Integer, primary_key=True),
'book': Column(Integer,ForeignKey('books.id')),
'book': Column(Integer, ForeignKey('books.id')),
'value': Column(Boolean)}
else:
ccdict={'__tablename__':'custom_column_' + str(row.id),
'id':Column(Integer, primary_key=True),
'value':Column(String)}
ccdict = {'__tablename__': 'custom_column_' + str(row.id),
'id': Column(Integer, primary_key=True),
'value': Column(String)}
cc_classes[row.id] = type('Custom_Column_' + str(row.id), (Base,), ccdict)
class Comments(Base):
__tablename__ = 'comments'
......@@ -100,6 +103,7 @@ class Tags(Base):
def __repr__(self):
return u"<Tags('{0})>".format(self.name)
class Authors(Base):
__tablename__ = 'authors'
......@@ -116,6 +120,7 @@ class Authors(Base):
def __repr__(self):
return u"<Authors('{0},{1}{2}')>".format(self.name, self.sort, self.link)
class Series(Base):
__tablename__ = 'series'
......@@ -130,30 +135,33 @@ class Series(Base):
def __repr__(self):
return u"<Series('{0},{1}')>".format(self.name, self.sort)
class Ratings(Base):
__tablename__ = 'ratings'
id = Column(Integer, primary_key=True)
rating = Column(Integer)
def __init__(self,rating):
def __init__(self, rating):
self.rating = rating
def __repr__(self):
return u"<Ratings('{0}')>".format(self.rating)
class Languages(Base):
__tablename__ = 'languages'
id = Column(Integer, primary_key=True)
lang_code = Column(String)
def __init__(self,lang_code):
def __init__(self, lang_code):
self.lang_code = lang_code
def __repr__(self):
return u"<Languages('{0}')>".format(self.lang_code)
class Data(Base):
__tablename__ = 'data'
......@@ -172,6 +180,7 @@ class Data(Base):
def __repr__(self):
return u"<Data('{0},{1}{2}{3}')>".format(self.book, self.format, self.uncompressed_size, self.name)
class Books(Base):
__tablename__ = 'books'
......@@ -207,17 +216,24 @@ class Books(Base):
self.has_cover = has_cover
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:
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:
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):
__tablename__ = 'custom_columns'
id = Column(Integer,primary_key=True)
id = Column(Integer, primary_key=True)
label = Column(String)
name = Column(String)
datatype = Column(String)
......@@ -231,9 +247,7 @@ class Custom_Columns(Base):
display_dict = ast.literal_eval(self.display)
return display_dict
#Base.metadata.create_all(engine)
# Base.metadata.create_all(engine)
Session = sessionmaker()
Session.configure(bind=engine)
session = Session()
......@@ -3,8 +3,9 @@ from lxml import etree
import os
import uploader
def extractCover(zip, coverFile, tmp_file_name):
if (coverFile is None):
if coverFile is None:
return None
else:
cf = zip.read("OPS/" + coverFile)
......@@ -16,35 +17,34 @@ def extractCover(zip, coverFile, tmp_file_name):
return tmp_cover_name
def get_epub_info(tmp_file_path, original_file_name, original_file_extension):
ns = {
'n':'urn:oasis:names:tc:opendocument:xmlns:container',
'pkg':'http://www.idpf.org/2007/opf',
'dc':'http://purl.org/dc/elements/1.1/'
'n': 'urn:oasis:names:tc:opendocument:xmlns:container',
'pkg': 'http://www.idpf.org/2007/opf',
'dc': 'http://purl.org/dc/elements/1.1/'
}
zip = zipfile.ZipFile(tmp_file_path)
txt = zip.read('META-INF/container.xml')
tree = etree.fromstring(txt)
cfname = tree.xpath('n:rootfiles/n:rootfile/@full-path',namespaces=ns)[0]
cfname = tree.xpath('n:rootfiles/n:rootfile/@full-path', namespaces=ns)[0]
cf = zip.read(cfname)
tree = etree.fromstring(cf)
p = tree.xpath('/pkg:package/pkg:metadata',namespaces=ns)[0]
p = tree.xpath('/pkg:package/pkg:metadata', namespaces=ns)[0]
epub_metadata = {}
for s in ['title', 'description', 'creator']:
tmp = p.xpath('dc:%s/text()'%(s),namespaces=ns)
if (len(tmp) > 0):
epub_metadata[s] = p.xpath('dc:%s/text()'%(s),namespaces=ns)[0]
tmp = p.xpath('dc:%s/text()' % s, namespaces=ns)
if len(tmp) > 0:
epub_metadata[s] = p.xpath('dc:%s/text()' % s, namespaces=ns)[0]
else:
epub_metadata[s] = "Unknown"
coversection = tree.xpath("/pkg:package/pkg:manifest/pkg:item[@id='cover']/@href",namespaces=ns)
if (len(coversection) > 0):
coversection = tree.xpath("/pkg:package/pkg:manifest/pkg:item[@id='cover']/@href", namespaces=ns)
if len(coversection) > 0:
coverfile = extractCover(zip, coversection[0], tmp_file_path)
else:
coverfile = None
......@@ -53,15 +53,13 @@ def get_epub_info(tmp_file_path, original_file_name, original_file_extension):
else:
title = epub_metadata['title']
return uploader.BookMeta(
file_path = tmp_file_path,
extension = original_file_extension,
title = title,
author = epub_metadata['creator'],
cover = coverfile,
description = epub_metadata['description'],
tags = "",
series = "",
file_path=tmp_file_path,
extension=original_file_extension,
title=title,
author=epub_metadata['creator'],
cover=coverfile,
description=epub_metadata['description'],
tags="",
series="",
series_id="")
......@@ -7,29 +7,31 @@ import uploader
def get_fb2_info(tmp_file_path, original_file_name, original_file_extension):
ns = {
'fb':'http://www.gribuser.ru/xml/fictionbook/2.0',
'l':'http://www.w3.org/1999/xlink',
'fb': 'http://www.gribuser.ru/xml/fictionbook/2.0',
'l': 'http://www.w3.org/1999/xlink',
}
fb2_file = open(tmp_file_path)
tree = etree.fromstring(fb2_file.read())
authors = tree.xpath('/fb:FictionBook/fb:description/fb:title-info/fb:author', namespaces=ns)
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))
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(
file_path = tmp_file_path,
extension = original_file_extension,
title = title,
author = author,
cover = None,
description = description,
tags = "",
series = "",
file_path=tmp_file_path,
extension=original_file_extension,
title=title,
author=author,
cover=None,
description=description,
tags="",
series="",
series_id="")
......@@ -4,8 +4,10 @@
import db, ub
import config
from flask import current_app as app
import logging
import smtplib
import tempfile
import socket
import sys
import os
......@@ -21,16 +23,19 @@ from email.generator import Generator
from flask_babel import gettext as _
import subprocess
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:
new_download = ub.Downloads(user_id=user_id, book_id=book_id)
ub.session.add(new_download)
ub.session.commit()
def make_mobi(book_id):
if sys.platform =="win32":
if sys.platform == "win32":
kindlegen = os.path.join(config.MAIN_DIR, "vendor", u"kindlegen.exe")
else:
kindlegen = os.path.join(config.MAIN_DIR, "vendor", u"kindlegen")
......@@ -45,9 +50,17 @@ def make_mobi(book_id):
file_path = os.path.join(config.DB_ROOT, book.path, data.name)
if os.path.exists(file_path + u".epub"):
p = subprocess.Popen((kindlegen + " \"" + file_path + u".epub\" ").encode(sys.getfilesystemencoding()), shell=True, stdout=subprocess.PIPE,
stderr=subprocess.PIPE, stdin=subprocess.PIPE)
check = p.wait()
p = subprocess.Popen((kindlegen + " \"" + file_path + u".epub\" ").encode(sys.getfilesystemencoding()),
shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
# 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:
book.data.append(db.Data(
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)
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):
'''Send email with attachments'''
"""Send email with attachments"""
is_mobi = False
is_azw = False
is_azw3 = False
......@@ -84,7 +156,7 @@ def send_mail(book_id, kindle_mail):
use_ssl = settings.get('mail_use_ssl', 0)
# attach files
#msg.attach(self.get_attachment(file_path))
# msg.attach(self.get_attachment(file_path))
book = db.session.query(db.Books).filter(db.Books.id == book_id).first()
data = db.session.query(db.Data).filter(db.Data.book == book.id)
......@@ -125,8 +197,13 @@ def send_mail(book_id, kindle_mail):
# send email
try:
mailserver = smtplib.SMTP(settings["mail_server"],settings["mail_port"])
mailserver.set_debuglevel(0)
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()
......@@ -137,6 +214,9 @@ def send_mail(book_id, kindle_mail):
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))
......@@ -145,7 +225,7 @@ def send_mail(book_id, kindle_mail):
def get_attachment(file_path):
'''Get file as MIMEBase message'''
"""Get file as MIMEBase message"""
try:
file_ = open(file_path, 'rb')
......@@ -163,6 +243,7 @@ def get_attachment(file_path):
'permissions?'))
return None
def get_valid_filename(value, replace_whitespace=True):
"""
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")
return value
def get_normalized_author(value):
"""
Normalizes sorted author name
......@@ -187,13 +269,14 @@ def get_normalized_author(value):
value = " ".join(value.split(", ")[::-1])
return value
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()
path = os.path.join(config.DB_ROOT, book.path)
authordir = book.path.split(os.sep)[0]
new_authordir=get_valid_filename(book.authors[0].name, False)
new_authordir = get_valid_filename(book.authors[0].name, False)
titledir = book.path.split(os.sep)[1]
new_titledir = get_valid_filename(book.title, False) + " (" + str(book_id) + ")"
......@@ -208,4 +291,3 @@ def update_dir_stucture(book_id):
os.renames(path, new_author_path)
book.path = new_authordir + os.sep + book.path.split(os.sep)[1]
db.session.commit()
......@@ -27,7 +27,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>
<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>
</form>
......
{% extends "layout.html" %}
{% block body %}
<div class="discover">
<h2>{{bookcounter}} {{_('Books in this Library')}}</h2>
<h2>{{authorcounter}} {{_('Authors in this Library')}}</h2>
</div>
<h3>{{_('Linked libraries')}}</h3>
<table class="table">
<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 %}
......@@ -40,7 +40,6 @@
{% endfor %}
</select>
</div>
{% if g.user and g.user.role_admin() and not profile %}
<div class="form-group">
<input type="checkbox" name="show_random" {% if content.random_books %}checked{% endif %}>
<label for="show_random">{{_('Show random books')}}</label>
......@@ -62,6 +61,8 @@
<label for="show_category">{{_('Show category selection')}}</label>
</div>
{% if g.user and g.user.role_admin() and not profile %}
<div class="form-group">
<input type="checkbox" name="admin_role" id="admin_role" {% if content.role_admin() %}checked{% endif %}>
<label for="admin_role">{{_('Admin user')}}</label>
......
......@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\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"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: de\n"
......@@ -18,250 +18,272 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.3.4\n"
#: cps/helper.py:80 cps/templates/detail.html:113
msgid "Send to Kindle"
msgstr "An Kindle senden"
#: cps/book_formats.py:108 cps/book_formats.py:112 cps/web.py:923
msgid "not installed"
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."
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"
msgstr ""
"Konnte keine Formate finden welche für das versenden per E-Mail geeignet "
"sind"
#: cps/helper.py:112
#: cps/helper.py:178
msgid "Could not convert epub to mobi"
msgstr "Konnte .epub nicht nach .mobi convertieren"
#: cps/helper.py:142
#, python-format
msgid "Failed to send mail: %s"
msgstr "E-Mail: %s konnte nicht gesendet werden"
msgstr "Konnte .epub nicht nach .mobi konvertieren"
#: cps/helper.py:162
#: cps/helper.py:236
msgid "The requested file could not be read. Maybe wrong permissions?"
msgstr "Die angeforderte Datei konnte nicht gelesen werden. Falsche Dateirechte?"
#: cps/web.py:639
#: cps/web.py:717
msgid "Latest Books"
msgstr "Letzte Bücher"
#: cps/web.py:661
#: cps/web.py:742
msgid "Hot Books (most downloaded)"
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"
msgstr "Zufällige Bücher"
#: cps/web.py:679
#: cps/web.py:763
msgid "Author list"
msgstr "Autorenliste"
#: cps/web.py:695
#: cps/web.py:780
#, python-format
msgid "Author: %(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"
msgstr "Liste Serien"
#: cps/web.py:714
#: cps/web.py:804
#, python-format
msgid "Series: %(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:"
msgstr ""
"Buch öffnen fehlgeschlagen. Datei existiert nicht, oder ist nicht "
"zugänglich."
#: cps/web.py:742
#: cps/web.py:837
msgid "Available languages"
msgstr "Verfügbare Sprachen"
#: cps/web.py:754
#: cps/web.py:852
#, python-format
msgid "Language: %(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"
msgstr "Kategorieliste"
#: cps/web.py:772
#: cps/web.py:875
#, python-format
msgid "Category: %(name)s"
msgstr "Kategorie: %(name)s"
#: cps/web.py:810
#: cps/web.py:931
msgid "Statistics"
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"
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!"
msgstr "Bitte alle Felder ausfüllen!"
#: cps/web.py:967
#: cps/web.py:1116
msgid "An unknown error occured. Please try again later."
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."
msgstr "Der Benutzername oder die E-Mailadresse ist in bereits in Benutzung."
#: cps/web.py:975
#: cps/web.py:1124
msgid "register"
msgstr "Registieren"
#: cps/web.py:990
#: cps/web.py:1140
#, python-format
msgid "you are now logged in as: '%(nickname)s'"
msgstr "Du bist nun eingeloggt als '%(nickname)s'"
#: cps/web.py:993
#: cps/web.py:1143
msgid "Wrong Username or Password"
msgstr "Flascher Benutzername oder Passwort"
#: cps/web.py:995
#: cps/web.py:1145
msgid "login"
msgstr "Login"
#: cps/web.py:1011
#: cps/web.py:1162
msgid "Please configure the SMTP mail settings first..."
msgstr "Bitte zuerst die SMTP Mail Einstellung konfigurieren ..."
#: cps/web.py:1015
#: cps/web.py:1166
#, python-format
msgid "Book successfully send to %(kindlemail)s"
msgstr "Buch erfolgreich versandt an %(kindlemail)s"
#: cps/web.py:1018
#: cps/web.py:1170
#, python-format
msgid "There was an error sending this book: %(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..."
msgstr "Bitte die Kindle E-Mail Adresse zuuerst konfigurieren..."
#: cps/web.py:1035
#: cps/web.py:1188
#, python-format
msgid "Book has been added to shelf: %(sname)s"
msgstr "Das Buch wurde dem Bücherregal: %(sname)s hinzugefügt"
#: cps/web.py:1054
#: cps/web.py:1209
#, python-format
msgid "Book has been removed from shelf: %(sname)s"
msgstr "Das Buch wurde aus dem Bücherregal: %(sname)s entfernt"
#: cps/web.py:1070
#: cps/web.py:1226
#, python-format
msgid "A shelf with the name '%(title)s' already exists."
msgstr "Es existiert bereits ein Bücheregal mit dem Titel '%(title)s'"
#: cps/web.py:1075
#: cps/web.py:1231
#, python-format
msgid "Shelf %(title)s created"
msgstr "Bücherregal %(title)s erzeugt"
#: cps/web.py:1077
#: cps/web.py:1233
msgid "There was an error"
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"
msgstr "Bücherregal erzeugen"
#: cps/web.py:1096
#: cps/web.py:1256
#, python-format
msgid "successfully deleted shelf %(name)s"
msgstr "Bücherregal %(name)s erfolgreich gelöscht"
#: cps/web.py:1113
#: cps/web.py:1277
#, python-format
msgid "Shelf: '%(name)s'"
msgstr "Bücherregal: '%(name)s'"
#: cps/web.py:1150
#: cps/web.py:1332
msgid "Found an existing account for this email address."
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
msgid "%(name)s's profile"
msgstr "%(name)s's Profil"
#: cps/web.py:1152
#: cps/web.py:1335
msgid "Profile updated"
msgstr "Profil aktualisiert"
#: cps/web.py:1161
#: cps/web.py:1346
msgid "User list"
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"
msgstr "Neuen Benutzer hinzufügen"
#: cps/web.py:1213
#: cps/web.py:1399
#, python-format
msgid "User '%(user)s' created"
msgstr "Benutzer '%(user)s' angelegt"
#: cps/web.py:1217
#: cps/web.py:1403
msgid "Found an existing account for this email address or nickname."
msgstr ""
"Es existiert ein Benutzerkonto für diese Emailadresse oder den "
"Benutzernamen"
#: cps/web.py:1238
#: cps/web.py:1426 cps/web.py:1437
msgid "Mail settings updated"
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"
msgstr "E-Mail Einstellungen editieren"
#: cps/web.py:1263
#: cps/web.py:1461
#, python-format
msgid "User '%(nick)s' deleted"
msgstr "Benutzer '%(nick)s' gelöscht"
#: cps/web.py:1318
#: cps/web.py:1516
#, python-format
msgid "User '%(nick)s' updated"
msgstr "Benutzer '%(nick)s' aktualisiert"
#: cps/web.py:1321
#: cps/web.py:1519
msgid "An unknown error occured."
msgstr "Es ist ein unbekanter Fehler aufgetreten"
#: cps/web.py:1322
#: cps/web.py:1521
#, python-format
msgid "Edit User %(nick)s"
msgstr "Benutzer %(nick)s bearbeiten"
#: cps/web.py:1556
#: cps/web.py:1759
#, python-format
msgid "Failed to create path %s (Permission denied)."
msgstr "Fehler beim Erzeugen des Pfads %s (Zugriff verweigert)"
#: cps/web.py:1561
#: cps/web.py:1764
#, python-format
msgid "Failed to store file %s (Permission denied)."
msgstr "Fehler beim speichern der Datei %s (Zugriff verweigert)"
#: cps/web.py:1566
#: cps/web.py:1769
#, python-format
msgid "Failed to delete file %s (Permission denied)."
msgstr "Fehler beim Löschen von Datei %s (Zugriff verweigert)"
......@@ -346,14 +368,14 @@ msgstr "Nein"
msgid "view book after edit"
msgstr "Buch nach Bearbeitung ansehen"
#: cps/templates/edit_book.html:105 cps/templates/email_edit.html:30
#: cps/templates/login.html:19 cps/templates/search_form.html:33
#: cps/templates/shelf_edit.html:15 cps/templates/user_edit.html:93
#: cps/templates/edit_book.html:105 cps/templates/login.html:19
#: cps/templates/search_form.html:33 cps/templates/shelf_edit.html:15
#: cps/templates/user_edit.html:94
msgid "Submit"
msgstr "Abschicken"
#: cps/templates/edit_book.html:106 cps/templates/email_edit.html:31
#: cps/templates/user_edit.html:95
#: cps/templates/edit_book.html:106 cps/templates/email_edit.html:32
#: cps/templates/user_edit.html:96
msgid "Back"
msgstr "Zurück"
......@@ -381,6 +403,14 @@ msgstr "SMTP Passwort"
msgid "From e-mail"
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
msgid "Next"
msgstr "Nächste"
......@@ -511,6 +541,14 @@ msgstr "Passwort"
msgid "Remember me"
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
msgid "Register a new account"
msgstr "Neues Benutzerkonto erzeugen"
......@@ -551,6 +589,10 @@ msgstr "Tags ausschließen"
msgid "Delete this Shelf"
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
msgid "Title"
msgstr "Titel"
......@@ -559,11 +601,27 @@ msgstr "Titel"
msgid "should the shelf be public?"
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"
msgstr "Bücher in dieser Bibliothek"
#: cps/templates/stats.html:5
#: cps/templates/stats.html:41
msgid "Authors in this Library"
msgstr "Autoren in dieser Bibliothek"
......@@ -579,51 +637,51 @@ msgstr "Zeige nur Bücher mit dieser Sprache"
msgid "Show all"
msgstr "Zeige alle"
#: cps/templates/user_edit.html:46
#: cps/templates/user_edit.html:45
msgid "Show random books"
msgstr "Zeige Zufällige Bücher"
#: cps/templates/user_edit.html:50
#: cps/templates/user_edit.html:49
msgid "Show hot books"
msgstr "Zeige Auswahl Beliebte Bücher"
#: cps/templates/user_edit.html:54
#: cps/templates/user_edit.html:53
msgid "Show language selection"
msgstr "Zeige Sprachauswahl"
#: cps/templates/user_edit.html:58
#: cps/templates/user_edit.html:57
msgid "Show series selection"
msgstr "Zeige Auswahl Serien"
#: cps/templates/user_edit.html:62
#: cps/templates/user_edit.html:61
msgid "Show category selection"
msgstr "Zeige Kategorie Auswahl"
#: cps/templates/user_edit.html:67
#: cps/templates/user_edit.html:68
msgid "Admin user"
msgstr "Admin Benutzer"
#: cps/templates/user_edit.html:71
#: cps/templates/user_edit.html:72
msgid "Allow Downloads"
msgstr "Downloads erlauben"
#: cps/templates/user_edit.html:75
#: cps/templates/user_edit.html:76
msgid "Allow Uploads"
msgstr "Uploads erlauben"
#: cps/templates/user_edit.html:79
#: cps/templates/user_edit.html:80
msgid "Allow Edit"
msgstr "Bearbeiten erlauben"
#: cps/templates/user_edit.html:83
#: cps/templates/user_edit.html:84
msgid "Allow Changing Password"
msgstr "Passwort ändern erlauben"
#: cps/templates/user_edit.html:89
#: cps/templates/user_edit.html:90
msgid "Delete this user"
msgstr "Benutzer löschen"
#: cps/templates/user_edit.html:100
#: cps/templates/user_edit.html:101
msgid "Recent Downloads"
msgstr "Letzte Downloads"
......@@ -674,6 +732,3 @@ msgstr "SMTP Einstellungen ändern"
msgid "Latin"
msgstr "Latein"
#~ msgid "Version"
#~ msgstr "Version"
# Translations template for PROJECT.
# French translations for PROJECT.
# Copyright (C) 2016 ORGANIZATION
# This file is distributed under the same license as the PROJECT project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2016.
......@@ -7,256 +7,281 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\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"
"Last-Translator: Nicolas Roudninski <nicoroud@gmail.com>\n"
"Language: fr\n"
"Language-Team: \n"
"Plural-Forms: nplurals=2; plural=(n > 1)\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"
"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
msgid "Send to Kindle"
msgstr "Envoyer ver Kindle"
#: cps/book_formats.py:108 cps/book_formats.py:112 cps/web.py:923
msgid "not installed"
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."
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"
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"
msgstr "Impossible de convertir epub vers mobi"
#: cps/helper.py:142
#, python-format
msgid "Failed to send mail: %s"
msgstr "Impossible d'envoyer le courriel : %s"
#: cps/helper.py:162
#: cps/helper.py:236
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"
msgstr "Derniers livres"
#: cps/web.py:661
#: cps/web.py:742
msgid "Hot Books (most downloaded)"
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"
msgstr "Livres au hasard"
#: cps/web.py:679
#: cps/web.py:763
msgid "Author list"
msgstr "Liste des auteurs"
#: cps/web.py:695
#: cps/web.py:780
#, python-format
msgid "Author: %(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"
msgstr "Liste des séries"
#: cps/web.py:714
#: cps/web.py:804
#, python-format
msgid "Series: %(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:"
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"
msgstr "Langues disponibles"
#: cps/web.py:754
#: cps/web.py:852
#, python-format
msgid "Language: %(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"
msgstr "Liste des catégories"
#: cps/web.py:772
#: cps/web.py:875
#, python-format
msgid "Category: %(name)s"
msgstr "Catégorie : %(name)s"
#: cps/web.py:810
#: cps/web.py:931
msgid "Statistics"
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"
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!"
msgstr "SVP, complétez tous les champs !"
#: cps/web.py:967
#: cps/web.py:1116
msgid "An unknown error occured. Please try again later."
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."
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"
msgstr "S'enregistrer"
#: cps/web.py:990
#: cps/web.py:1140
#, python-format
msgid "you are now logged in as: '%(nickname)s'"
msgstr "Vous êtes maintenant connecté sous : '%(nickname)s'"
#: cps/web.py:993
#: cps/web.py:1143
msgid "Wrong Username or Password"
msgstr "Mauvais nom d'utilisateur ou mot de passe"
#: cps/web.py:995
#: cps/web.py:1145
msgid "login"
msgstr "Connexion"
#: cps/web.py:1011
#: cps/web.py:1162
msgid "Please configure the SMTP mail settings first..."
msgstr "Veillez configurer les paramètres smtp d'abord..."
#: cps/web.py:1015
#: cps/web.py:1166
#, python-format
msgid "Book successfully send to %(kindlemail)s"
msgstr "Livres envoyés à %(kindlemail)s avec succès"
#: cps/web.py:1018
#: cps/web.py:1170
#, python-format
msgid "There was an error sending this book: %(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..."
msgstr "Veuillez configurer votre adresse kindle d'abord..."
#: cps/web.py:1035
#: cps/web.py:1188
#, python-format
msgid "Book has been added to shelf: %(sname)s"
msgstr "Le livre a bien été ajouté à l'étagère : %(sname)s"
#: cps/web.py:1054
#: cps/web.py:1209
#, python-format
msgid "Book has been removed from shelf: %(sname)s"
msgstr "Le livre a été supprimé de l'étagère %(sname)s"
#: cps/web.py:1070
#: cps/web.py:1226
#, python-format
msgid "A shelf with the name '%(title)s' already exists."
msgstr "Une étagère de ce nom '%(title)s' existe déjà."
#: cps/web.py:1075
#: cps/web.py:1231
#, python-format
msgid "Shelf %(title)s created"
msgstr "Étagère %(title)s créée"
#: cps/web.py:1077
#: cps/web.py:1233
msgid "There was an error"
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"
msgstr "Créer une étagère"
#: cps/web.py:1096
#: cps/web.py:1256
#, python-format
msgid "successfully deleted shelf %(name)s"
msgstr "L'étagère %(name)s a été supprimé avec succès"
#: cps/web.py:1113
#: cps/web.py:1277
#, python-format
msgid "Shelf: '%(name)s'"
msgstr "Étagère : '%(name)s'"
#: cps/web.py:1150
#: cps/web.py:1332
msgid "Found an existing account for this email address."
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
msgid "%(name)s's profile"
msgstr "Profil de %(name)s"
#: cps/web.py:1152
#: cps/web.py:1335
msgid "Profile updated"
msgstr "Profil mis à jour"
#: cps/web.py:1161
#: cps/web.py:1346
msgid "User list"
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"
msgstr "Ajouter un nouvel utilisateur"
#: cps/web.py:1213
#: cps/web.py:1399
#, python-format
msgid "User '%(user)s' created"
msgstr "Utilisateur '%(user)s' créé"
#: cps/web.py:1217
#: cps/web.py:1403
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à."
#: cps/web.py:1238
#: cps/web.py:1426 cps/web.py:1437
msgid "Mail settings updated"
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"
msgstr "Éditer les paramètres de courriel"
#: cps/web.py:1263
#: cps/web.py:1461
#, python-format
msgid "User '%(nick)s' deleted"
msgstr "Utilisateur '%(nick)s' supprimé"
#: cps/web.py:1318
#: cps/web.py:1516
#, python-format
msgid "User '%(nick)s' updated"
msgstr "Utilisateur '%(nick)s' mis à jour"
#: cps/web.py:1321
#: cps/web.py:1519
msgid "An unknown error occured."
msgstr "Oups ! Une erreur inconnue a eu lieu."
#: cps/web.py:1322
#: cps/web.py:1521
#, python-format
msgid "Edit User %(nick)s"
msgstr "Éditer l'utilisateur %(nick)s"
#: cps/web.py:1556
#: cps/web.py:1759
#, python-format
msgid "Failed to create path %s (Permission denied)."
msgstr "Impossible de créer le chemin %s (permission refusée)"
#: cps/web.py:1561
#: cps/web.py:1764
#, python-format
msgid "Failed to store file %s (Permission denied)."
msgstr "Impossible d'enregistrer le fichier %s (permission refusée)"
#: cps/web.py:1566
#: cps/web.py:1769
#, python-format
msgid "Failed to delete file %s (Permission denied)."
msgstr "Impossible de supprimer le fichier %s (permission refusée)"
......@@ -341,14 +366,14 @@ msgstr "Non"
msgid "view book after edit"
msgstr "Voir le livre après l'édition"
#: cps/templates/edit_book.html:105 cps/templates/email_edit.html:30
#: cps/templates/login.html:19 cps/templates/search_form.html:33
#: cps/templates/shelf_edit.html:15 cps/templates/user_edit.html:93
#: cps/templates/edit_book.html:105 cps/templates/login.html:19
#: cps/templates/search_form.html:33 cps/templates/shelf_edit.html:15
#: cps/templates/user_edit.html:94
msgid "Submit"
msgstr "Soumettre"
#: cps/templates/edit_book.html:106 cps/templates/email_edit.html:31
#: cps/templates/user_edit.html:95
#: cps/templates/edit_book.html:106 cps/templates/email_edit.html:32
#: cps/templates/user_edit.html:96
msgid "Back"
msgstr "Retour"
......@@ -376,6 +401,14 @@ msgstr "Mot de passe smtp"
msgid "From e-mail"
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
msgid "Next"
msgstr "Suivant"
......@@ -506,6 +539,14 @@ msgstr "Mot de passe"
msgid "Remember me"
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
msgid "Register a new account"
msgstr "Enregistrer un nouveau compte"
......@@ -546,6 +587,10 @@ msgstr "Exclure des étiquettes"
msgid "Delete this Shelf"
msgstr "Effacer cette étagère"
#: cps/templates/shelf.html:7
msgid "Edit Shelf name"
msgstr ""
#: cps/templates/shelf_edit.html:7
msgid "Title"
msgstr "Titre"
......@@ -554,11 +599,27 @@ msgstr "Titre"
msgid "should the shelf be public?"
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"
msgstr "Livres dans la bibiothèque"
#: cps/templates/stats.html:5
#: cps/templates/stats.html:41
msgid "Authors in this Library"
msgstr "Auteurs dans la bibliothèque"
......@@ -574,51 +635,51 @@ msgstr "Montrer les livres dans la langue"
msgid "Show all"
msgstr "Montrer tout"
#: cps/templates/user_edit.html:46
#: cps/templates/user_edit.html:45
msgid "Show random books"
msgstr "Montrer des livres au hasard"
#: cps/templates/user_edit.html:50
#: cps/templates/user_edit.html:49
msgid "Show hot books"
msgstr "Montrer les livres populaires"
#: cps/templates/user_edit.html:54
#: cps/templates/user_edit.html:53
msgid "Show language selection"
msgstr "Montrer la sélection de la langue"
#: cps/templates/user_edit.html:58
#: cps/templates/user_edit.html:57
msgid "Show series selection"
msgstr "Montrer la sélection des séries"
#: cps/templates/user_edit.html:62
#: cps/templates/user_edit.html:61
msgid "Show category selection"
msgstr "Montrer la sélection des catégories"
#: cps/templates/user_edit.html:67
#: cps/templates/user_edit.html:68
msgid "Admin user"
msgstr "Utilisateur admin"
#: cps/templates/user_edit.html:71
#: cps/templates/user_edit.html:72
msgid "Allow Downloads"
msgstr "Permettre les téléchargements"
#: cps/templates/user_edit.html:75
#: cps/templates/user_edit.html:76
msgid "Allow Uploads"
msgstr "Permettre les téléversements"
#: cps/templates/user_edit.html:79
#: cps/templates/user_edit.html:80
msgid "Allow Edit"
msgstr "Permettre l'édition"
#: cps/templates/user_edit.html:83
#: cps/templates/user_edit.html:84
msgid "Allow Changing Password"
msgstr "Permettre le changement de mot de passe"
#: cps/templates/user_edit.html:89
#: cps/templates/user_edit.html:90
msgid "Delete this user"
msgstr "Supprimer cet utilisateur"
#: cps/templates/user_edit.html:100
#: cps/templates/user_edit.html:101
msgid "Recent Downloads"
msgstr "Téléchargements récents"
......@@ -668,3 +729,4 @@ msgstr "Changer les paramètre smtp"
msgid "Latin"
msgstr "Latin"
......@@ -21,18 +21,19 @@ ROLE_EDIT = 8
ROLE_PASSWD = 16
DEFAULT_PASS = "admin123"
class User(Base):
__tablename__ = 'user'
id = Column(Integer, primary_key = True)
nickname = Column(String(64), unique = True)
email = Column(String(120), unique = True, default = "")
role = Column(SmallInteger, default = ROLE_USER)
id = Column(Integer, primary_key=True)
nickname = Column(String(64), unique=True)
email = Column(String(120), unique=True, default="")
role = Column(SmallInteger, default=ROLE_USER)
password = Column(String)
kindle_mail = Column(String(120), default="")
shelf = relationship('Shelf', backref = 'user', lazy = 'dynamic')
whislist = relationship('Whislist', backref = 'user', lazy = 'dynamic')
downloads = relationship('Downloads', backref= 'user', lazy = 'dynamic')
shelf = relationship('Shelf', backref='user', lazy='dynamic')
whislist = relationship('Whislist', backref='user', lazy='dynamic')
downloads = relationship('Downloads', backref='user', lazy='dynamic')
locale = Column(String(2), default="en")
random_books = Column(Integer, default=1)
language_books = Column(Integer, default=1)
......@@ -43,26 +44,31 @@ class User(Base):
def is_authenticated(self):
return True
def role_admin(self):
if self.role is not None:
return True if self.role & ROLE_ADMIN == ROLE_ADMIN else False
else:
return False
def role_download(self):
if self.role is not None:
return True if self.role & ROLE_DOWNLOAD == ROLE_DOWNLOAD else False
else:
return False
def role_upload(self):
if self.role is not None:
return True if self.role & ROLE_UPLOAD == ROLE_UPLOAD else False
else:
return False
def role_edit(self):
if self.role is not None:
return True if self.role & ROLE_EDIT == ROLE_EDIT else False
else:
return False
def role_passwd(self):
if self.role is not None:
return True if self.role & ROLE_PASSWD == ROLE_PASSWD else False
......@@ -96,20 +102,20 @@ class User(Base):
def show_category(self):
return self.category_books
def __repr__(self):
return '<User %r>' % (self.nickname)
return '<User %r>' % self.nickname
class Shelf(Base):
__tablename__ = 'shelf'
id = Column(Integer, primary_key = True)
id = Column(Integer, primary_key=True)
name = Column(String)
is_public = Column(Integer, default=0)
user_id = Column(Integer, ForeignKey('user.id'))
def __repr__(self):
return '<Shelf %r>' % (self.name)
return '<Shelf %r>' % self.name
class Whislist(Base):
......@@ -124,7 +130,7 @@ class Whislist(Base):
pass
def __repr__(self):
return '<Whislist %r>' % (self.name)
return '<Whislist %r>' % self.name
class BookShelf(Base):
......@@ -135,7 +141,7 @@ class BookShelf(Base):
shelf = Column(Integer, ForeignKey('shelf.id'))
def __repr__(self):
return '<Book %r>' % (self.id)
return '<Book %r>' % self.id
class Downloads(Base):
......@@ -146,7 +152,8 @@ class Downloads(Base):
user_id = Column(Integer, ForeignKey('user.id'))
def __repr__(self):
return '<Download %r' % (self.book_id)
return '<Download %r' % self.book_id
class Whish(Base):
__tablename__ = 'whish'
......@@ -157,7 +164,8 @@ class Whish(Base):
wishlist = Column(Integer, ForeignKey('wishlist.id'))
def __repr__(self):
return '<Whish %r>' % (self.title)
return '<Whish %r>' % self.title
class Settings(Base):
__tablename__ = 'settings'
......@@ -174,12 +182,13 @@ class Settings(Base):
#return '<Smtp %r>' % (self.mail_server)
pass
def migrate_Database():
try:
session.query(exists().where(User.random_books)).scalar()
session.commit()
except exc.OperationalError: # Database is not compatible, some rows are missing
conn=engine.connect()
except exc.OperationalError: # Database is not compatible, some rows are missing
conn = engine.connect()
conn.execute("ALTER TABLE user ADD column random_books INTEGER DEFAULT 1")
conn.execute("ALTER TABLE user ADD column locale String(2) DEFAULT 'en'")
conn.execute("ALTER TABLE user ADD column default_language String(3) DEFAULT 'all'")
......@@ -208,23 +217,25 @@ def create_default_config():
session.add(settings)
session.commit()
def get_mail_settings():
settings = session.query(Settings).first()
if not settings:
return {}
return {}
data = {
'mail_server': settings.mail_server,
'mail_port': settings.mail_port,
'mail_use_ssl': settings.mail_use_ssl,
'mail_login': settings.mail_login,
'mail_password': settings.mail_password,
'mail_from': settings.mail_from
'mail_server': settings.mail_server,
'mail_port': settings.mail_port,
'mail_use_ssl': settings.mail_use_ssl,
'mail_login': settings.mail_login,
'mail_password': settings.mail_password,
'mail_from': settings.mail_from
}
return data
def create_admin_user():
user = User()
user.nickname = "admin"
......@@ -251,4 +262,3 @@ if not os.path.exists(dbpath):
pass
else:
migrate_Database()
......@@ -9,6 +9,8 @@ BookMeta = namedtuple('BookMeta', 'file_path, extension, title, author, cover, d
"""
:rtype: BookMeta
"""
def upload(file):
tmp_dir = os.path.join(gettempdir(), 'calibre_web')
......@@ -23,7 +25,3 @@ def upload(file):
file.save(tmp_file_path)
meta = book_formats.process(tmp_file_path, filename_root, file_extension)
return meta
......@@ -5,8 +5,8 @@ import mimetypes
import logging
from logging.handlers import RotatingFileHandler
import textwrap
mimetypes.add_type('application/xhtml+xml','.xhtml')
from flask import Flask, render_template, session, request, Response, redirect, url_for, send_from_directory, make_response, g, flash, abort
from flask import Flask, render_template, session, request, Response, redirect, url_for, send_from_directory, \
make_response, g, flash, abort
import db, config, ub, helper
import os
import errno
......@@ -31,10 +31,15 @@ import datetime
from iso639 import languages as isoLanguages
from uuid import uuid4
import os.path
import sys
import subprocess
import shutil
import re
from shutil import move
try:
from wand.image import Image
use_generic_pdf_cover = False
except ImportError, e:
use_generic_pdf_cover = True
......@@ -42,8 +47,11 @@ except ImportError, e:
from shutil import copyfile
from cgi import escape
mimetypes.add_type('application/xhtml+xml', '.xhtml')
class ReverseProxied(object):
'''Wrap the application in this middleware and configure the
"""Wrap the application in this middleware and configure the
front-end server to add these headers, to let you quietly bind
this to a URL other than / and to an HTTP scheme that is
different than what is used locally.
......@@ -58,7 +66,8 @@ class ReverseProxied(object):
proxy_set_header X-Scheme $scheme;
proxy_set_header X-Script-Name /myprefix;
}
'''
"""
def __init__(self, app):
self.app = app
......@@ -78,49 +87,69 @@ class ReverseProxied(object):
environ['HTTP_HOST'] = server
return self.app(environ, start_response)
app = (Flask(__name__))
app.wsgi_app = ReverseProxied(app.wsgi_app)
formatter = logging.Formatter(
"[%(asctime)s] {%(pathname)s:%(lineno)d} %(levelname)s - %(message)s")
file_handler = RotatingFileHandler(os.path.join(config.LOG_DIR, "calibre-web.log"), maxBytes=10000, backupCount=1)
file_handler.setLevel(logging.INFO)
file_handler = RotatingFileHandler(os.path.join(config.LOG_DIR, "calibre-web.log"), maxBytes=50000, backupCount=1)
file_handler.setFormatter(formatter)
app.logger.addHandler(file_handler)
if config.DEVELOPMENT:
app.logger.setLevel(logging.DEBUG)
else:
app.logger.setLevel(logging.INFO)
app.logger.info('Starting Calibre Web...')
logging.getLogger("book_formats").addHandler(file_handler)
logging.getLogger("book_formats").setLevel(logging.INFO)
Principal(app)
babel = Babel(app)
import uploader
global global_queue
global_queue = None
class Anonymous(AnonymousUserMixin):
def __init__(self):
self.nickname = 'Guest'
self.role = -1
def role_admin(self):
return False
def role_download(self):
return False
def role_upload(self):
return False
def role_edit(self):
return False
def filter_language(self):
return 'all'
def show_random_books(self):
return True
def show_hot_books(self):
return True
def show_series(self):
return True
def show_category(self):
return True
def show_language(self):
return True
def is_anonymous(self):
return config.ANON_BROWSE
......@@ -138,21 +167,24 @@ LANGUAGES = {
'fr': 'Français'
}
@babel.localeselector
def get_locale():
# if a user is logged in, use the locale from the user settings
user = getattr(g, 'user', None)
if user is not None and hasattr(user, "locale"):
return user.locale
return user.locale
preferred = [x.replace('-', '_') for x in request.accept_languages.values()]
return negotiate_locale(preferred, LANGUAGES.keys())
@babel.timezoneselector
def get_timezone():
user = getattr(g, 'user', None)
if user is not None:
return user.timezone
@lm.user_loader
def load_user(id):
return ub.session.query(ub.User).filter(ub.User.id == int(id)).first()
......@@ -173,6 +205,7 @@ def load_user_from_header(header_val):
return user
return
def check_auth(username, password):
user = ub.session.query(ub.User).filter(ub.User.nickname == username).first()
if user and check_password_hash(user.password, password):
......@@ -180,11 +213,13 @@ def check_auth(username, password):
else:
return False
def authenticate():
return Response(
'Could not verify your access level for that URL.\n'
'You have to login with proper credentials', 401,
{'WWW-Authenticate': 'Basic realm="Login Required"'})
'Could not verify your access level for that URL.\n'
'You have to login with proper credentials', 401,
{'WWW-Authenticate': 'Basic realm="Login Required"'})
def requires_basic_auth_if_no_ano(f):
@wraps(f)
......@@ -194,11 +229,12 @@ def requires_basic_auth_if_no_ano(f):
if not auth or not check_auth(auth.username, auth.password):
return authenticate()
return f(*args, **kwargs)
return decorated
#simple pagination for the feed
class Pagination(object):
# simple pagination for the feed
class Pagination(object):
def __init__(self, page, per_page, total_count):
self.page = page
self.per_page = per_page
......@@ -221,88 +257,104 @@ class Pagination(object):
last = 0
for num in xrange(1, self.pages + 1):
if num <= left_edge or \
(num > self.page - left_current - 1 and \
num < self.page + right_current) or \
num > self.pages - right_edge:
(num > self.page - left_current - 1 and \
num < self.page + right_current) or \
num > self.pages - right_edge:
if last + 1 != num:
yield None
yield num
last = num
##pagination links in jinja
# pagination links in jinja
def url_for_other_page(page):
args = request.view_args.copy()
args['page'] = page
return url_for(request.endpoint, **args)
app.jinja_env.globals['url_for_other_page'] = url_for_other_page
def login_required_if_no_ano(func):
if config.ANON_BROWSE == 1:
return func
return login_required(func)
## custom jinja filters
# custom jinja filters
@app.template_filter('shortentitle')
def shortentitle_filter(s):
if len(s) > 60:
s = s.split(':', 1)[0]
if len(s) > 60:
s = textwrap.wrap(s, 60, break_long_words=False)[0]+' [...]'
s = textwrap.wrap(s, 60, break_long_words=False)[0] + ' [...]'
return s
def admin_required(f):
"""
Checks if current_user.role == 1
"""
@wraps(f)
def inner(*args, **kwargs):
if current_user.role_admin():
return f(*args, **kwargs)
abort(403)
return inner
def download_required(f):
@wraps(f)
def inner(*args, **kwargs):
if current_user.role_download() or current_user.role_admin():
return f(*args, **kwargs)
abort(403)
return inner
def upload_required(f):
@wraps(f)
def inner(*args, **kwargs):
if current_user.role_upload() or current_user.role_admin():
return f(*args, **kwargs)
abort(403)
return inner
def edit_required(f):
@wraps(f)
def inner(*args, **kwargs):
if current_user.role_edit() or current_user.role_admin():
return f(*args, **kwargs)
abort(403)
return inner
# Fill indexpage with all requested data from database
def fill_indexpage(page,database, db_filter, order) :
def fill_indexpage(page, database, db_filter, order):
if current_user.filter_language() != "all":
filter = db.Books.languages.any(db.Languages.lang_code == current_user.filter_language())
else:
filter= True
filter = True
if current_user.show_random_books():
random = db.session.query(db.Books).filter(filter).order_by(func.random()).limit(config.RANDOM_BOOKS)
else :
else:
random = false
off = int(int(config.NEWEST_BOOKS) * (page-1))
pagination = Pagination(page, config.NEWEST_BOOKS, len(db.session.query(database).filter(db_filter).filter(filter).all()))
entries = db.session.query(database).filter(db_filter).filter(filter).order_by(order).offset(off).limit(config.NEWEST_BOOKS)
off = int(int(config.NEWEST_BOOKS) * (page - 1))
pagination = Pagination(page, config.NEWEST_BOOKS,
len(db.session.query(database).filter(db_filter).filter(filter).all()))
entries = db.session.query(database).filter(db_filter).filter(filter).order_by(order).offset(off).limit(
config.NEWEST_BOOKS)
return entries, random, pagination
def modify_database_object(input_elements, db_book_object, db_object, db_session,type):
def modify_database_object(input_elements, db_book_object, db_object, db_session, type):
input_elements = [x for x in input_elements if x != '']
# we have all input element (authors, series, tags) names now
# 1. search for elements to remove
......@@ -312,7 +364,7 @@ def modify_database_object(input_elements, db_book_object, db_object, db_session
for inp_element in input_elements:
if inp_element == c_elements.name:
found = True
break;
break
# if the element was not found in the new list, add it to remove list
if not found:
del_elements.append(c_elements)
......@@ -323,7 +375,7 @@ def modify_database_object(input_elements, db_book_object, db_object, db_session
for c_elements in db_book_object:
if inp_element == c_elements.name:
found = True
break;
break
if not found:
add_elements.append(inp_element)
# if there are elements to remove, we remove them now
......@@ -335,20 +387,20 @@ def modify_database_object(input_elements, db_book_object, db_object, db_session
# if there are elements to add, we add them now!
if len(add_elements) > 0:
if type == 'languages':
db_filter=db_object.lang_code
db_filter = db_object.lang_code
else:
db_filter=db_object.name
db_filter = db_object.name
for add_element in add_elements:
# check if a element with that name exists
new_element = db_session.query(db_object).filter(db_filter == add_element).first()
# if no element is found add it
if new_element == None:
if type=='author':
if new_element is None:
if type == 'author':
new_element = db_object(add_element, add_element, "")
else:
if type=='series':
if type == 'series':
new_element = db_object(add_element, add_element)
else: # type should be tag, or languages
else: # type should be tag, or languages
new_element = db_object(add_element)
db_session.add(new_element)
new_element = db.session.query(db_object).filter(db_filter == add_element).first()
......@@ -363,6 +415,7 @@ def before_request():
g.allow_registration = config.PUBLIC_REG
g.allow_upload = config.UPLOADING
@app.route("/feed")
@requires_basic_auth_if_no_ano
def feed_index():
......@@ -371,18 +424,20 @@ def feed_index():
else:
filter = True
xml = render_template('index.xml')
response= make_response(xml)
response = make_response(xml)
response.headers["Content-Type"] = "application/xml"
return response
@app.route("/feed/osd")
@requires_basic_auth_if_no_ano
def feed_osd():
xml = render_template('osd.xml')
response= make_response(xml)
response = make_response(xml)
response.headers["Content-Type"] = "application/xml"
return response
@app.route("/feed/search", methods=["GET"])
@requires_basic_auth_if_no_ano
def feed_search():
......@@ -392,15 +447,18 @@ def feed_search():
else:
filter = True
if term:
entries = db.session.query(db.Books).filter(db.or_(db.Books.tags.any(db.Tags.name.like("%"+term+"%")),db.Books.authors.any(db.Authors.name.like("%"+term+"%")),db.Books.title.like("%"+term+"%"))).filter(filter).all()
entries = db.session.query(db.Books).filter(db.or_(db.Books.tags.any(db.Tags.name.like("%" + term + "%")),
db.Books.authors.any(db.Authors.name.like("%" + term + "%")),
db.Books.title.like("%" + term + "%"))).filter(filter).all()
xml = render_template('feed.xml', searchterm=term, entries=entries, Last_Updated=Last_Updated)
else:
xml = render_template('feed.xml', searchterm="")
response= make_response(xml)
response = make_response(xml)
response.headers["Content-Type"] = "application/xml"
return response
@app.route("/feed/new")
@requires_basic_auth_if_no_ano
def feed_new():
......@@ -408,12 +466,14 @@ def feed_new():
if current_user.filter_language() != "all":
filter = db.Books.languages.any(db.Languages.lang_code == current_user.filter_language())
else:
filter= True
filter = True
if not off:
off=0
entries = db.session.query(db.Books).filter(filter).order_by(db.Books.last_modified.desc()).offset(off).limit(config.NEWEST_BOOKS)
xml = render_template('feed.xml', entries=entries, next_url="/feed/new?start_index=%d" % (int(config.NEWEST_BOOKS) + int(off)))
response= make_response(xml)
off = 0
entries = db.session.query(db.Books).filter(filter).order_by(db.Books.timestamp.desc()).offset(off).limit(
config.NEWEST_BOOKS)
xml = render_template('feed.xml', entries=entries,
next_url="/feed/new?start_index=%d" % (int(config.NEWEST_BOOKS) + int(off)))
response = make_response(xml)
response.headers["Content-Type"] = "application/xml"
return response
......@@ -427,13 +487,15 @@ def feed_discover():
else:
filter = True
if not off:
off=0
off = 0
entries = db.session.query(db.Books).filter(filter).order_by(func.random()).offset(off).limit(config.NEWEST_BOOKS)
xml = render_template('feed.xml', entries=entries, next_url="/feed/discover?start_index=%d" % (int(config.NEWEST_BOOKS) + int(off)))
xml = render_template('feed.xml', entries=entries,
next_url="/feed/discover?start_index=%d" % (int(config.NEWEST_BOOKS) + int(off)))
response = make_response(xml)
response.headers["Content-Type"] = "application/xml"
return response
@app.route("/feed/hot")
@requires_basic_auth_if_no_ano
def feed_hot():
......@@ -443,14 +505,17 @@ def feed_hot():
else:
filter = True
if not off:
off=0
entries = db.session.query(db.Books).filter(filter).filter(db.Books.ratings.any(db.Ratings.rating > 9)).offset(off).limit(config.NEWEST_BOOKS)
off = 0
entries = db.session.query(db.Books).filter(filter).filter(db.Books.ratings.any(db.Ratings.rating > 9)).offset(
off).limit(config.NEWEST_BOOKS)
xml = render_template('feed.xml', entries=entries, next_url="/feed/hot?start_index=%d" % (int(config.NEWEST_BOOKS) + int(off)))
response= make_response(xml)
xml = render_template('feed.xml', entries=entries,
next_url="/feed/hot?start_index=%d" % (int(config.NEWEST_BOOKS) + int(off)))
response = make_response(xml)
response.headers["Content-Type"] = "application/xml"
return response
@app.route("/feed/author")
@requires_basic_auth_if_no_ano
def feed_authorindex():
......@@ -460,13 +525,15 @@ def feed_authorindex():
else:
filter = True
if not off:
off=0
off = 0
authors = db.session.query(db.Authors).order_by(db.Authors.sort).offset(off).limit(config.NEWEST_BOOKS)
xml = render_template('feed.xml', authors=authors,next_url="/feed/author?start_index=%d" % (int(config.NEWEST_BOOKS) + int(off)))
response= make_response(xml)
xml = render_template('feed.xml', authors=authors,
next_url="/feed/author?start_index=%d" % (int(config.NEWEST_BOOKS) + int(off)))
response = make_response(xml)
response.headers["Content-Type"] = "application/xml"
return response
@app.route("/feed/author/<name>")
@requires_basic_auth_if_no_ano
def feed_author(name):
......@@ -476,11 +543,12 @@ def feed_author(name):
else:
filter = True
if not off:
off=0
off = 0
entries = db.session.query(db.Books).filter(db.Books.authors.any(db.Authors.name.like("%" + name + "%"))).filter(
filter).offset(off).limit(config.NEWEST_BOOKS)
xml = render_template('feed.xml', entries=entries,next_url="/feed/author?start_index=%d" % (int(config.NEWEST_BOOKS) + int(off)))
response= make_response(xml)
filter).offset(off).limit(config.NEWEST_BOOKS)
xml = render_template('feed.xml', entries=entries,
next_url="/feed/author?start_index=%d" % (int(config.NEWEST_BOOKS) + int(off)))
response = make_response(xml)
response.headers["Content-Type"] = "application/xml"
return response
......@@ -490,10 +558,11 @@ def feed_author(name):
def feed_categoryindex():
off = request.args.get("start_index")
if not off:
off=0
off = 0
entries = db.session.query(db.Tags).order_by(db.Tags.name).offset(off).limit(config.NEWEST_BOOKS)
xml = render_template('feed.xml', categorys=entries, next_url="/feed/category?start_index=%d" % (int(config.NEWEST_BOOKS) + int(off)))
response= make_response(xml)
xml = render_template('feed.xml', categorys=entries,
next_url="/feed/category?start_index=%d" % (int(config.NEWEST_BOOKS) + int(off)))
response = make_response(xml)
response.headers["Content-Type"] = "application/xml"
return response
......@@ -507,23 +576,26 @@ def feed_category(name):
else:
filter = True
if not off:
off=0
off = 0
entries = db.session.query(db.Books).filter(db.Books.tags.any(db.Tags.name.like("%" + name + "%"))).order_by(
db.Books.last_modified.desc()).filter(filter).offset(off).limit(config.NEWEST_BOOKS)
xml = render_template('feed.xml', entries=entries, next_url="/feed/category?start_index=%d" % (int(config.NEWEST_BOOKS) + int(off)))
response= make_response(xml)
db.Books.timestamp.desc()).filter(filter).offset(off).limit(config.NEWEST_BOOKS)
xml = render_template('feed.xml', entries=entries,
next_url="/feed/category?start_index=%d" % (int(config.NEWEST_BOOKS) + int(off)))
response = make_response(xml)
response.headers["Content-Type"] = "application/xml"
return response
@app.route("/feed/series")
@requires_basic_auth_if_no_ano
def feed_seriesindex():
off = request.args.get("start_index")
if not off:
off=0
off = 0
entries = db.session.query(db.Series).order_by(db.Series.name).offset(off).limit(config.NEWEST_BOOKS)
xml = render_template('feed.xml', series=entries, next_url="/feed/series?start_index=%d" % (int(config.NEWEST_BOOKS) + int(off)))
response= make_response(xml)
xml = render_template('feed.xml', series=entries,
next_url="/feed/series?start_index=%d" % (int(config.NEWEST_BOOKS) + int(off)))
response = make_response(xml)
response.headers["Content-Type"] = "application/xml"
return response
......@@ -537,11 +609,12 @@ def feed_series(name):
else:
filter = True
if not off:
off=0
off = 0
entries = db.session.query(db.Books).filter(db.Books.series.any(db.Series.name.like("%" + name + "%"))).order_by(
db.Books.last_modified.desc()).filter(filter).offset(off).limit(config.NEWEST_BOOKS)
xml = render_template('feed.xml', entries=entries, next_url="/feed/series?start_index=%d" % (int(config.NEWEST_BOOKS) + int(off)))
response= make_response(xml)
db.Books.timestamp.desc()).filter(filter).offset(off).limit(config.NEWEST_BOOKS)
xml = render_template('feed.xml', entries=entries,
next_url="/feed/series?start_index=%d" % (int(config.NEWEST_BOOKS) + int(off)))
response = make_response(xml)
response.headers["Content-Type"] = "application/xml"
return response
......@@ -557,31 +630,34 @@ def get_opds_download_link(book_id, format):
author = helper.get_normalized_author(book.author_sort)
file_name = book.title
if len(author) > 0:
file_name = author+'-'+file_name
file_name = author + '-' + file_name
file_name = helper.get_valid_filename(file_name)
response = make_response(send_from_directory(os.path.join(config.DB_ROOT, book.path), data.name + "." +format))
response = make_response(send_from_directory(os.path.join(config.DB_ROOT, book.path), data.name + "." + format))
response.headers["Content-Disposition"] = "attachment; filename=%s.%s" % (data.name, format)
return response
@app.route("/get_authors_json", methods = ['GET', 'POST'])
@app.route("/get_authors_json", methods=['GET', 'POST'])
@login_required_if_no_ano
def get_authors_json():
def get_authors_json():
if request.method == "GET":
query = request.args.get('q')
entries = db.session.execute("select name from authors where name like '%" + query + "%'")
json_dumps = json.dumps([dict(r) for r in entries])
return json_dumps
@app.route("/get_tags_json", methods = ['GET', 'POST'])
@app.route("/get_tags_json", methods=['GET', 'POST'])
@login_required_if_no_ano
def get_tags_json():
def get_tags_json():
if request.method == "GET":
query = request.args.get('q')
entries = db.session.execute("select name from tags where name like '%" + query + "%'")
json_dumps = json.dumps([dict(r) for r in entries])
return json_dumps
@app.route("/get_languages_json", methods = ['GET', 'POST'])
@app.route("/get_languages_json", methods=['GET', 'POST'])
@login_required_if_no_ano
def get_languages_json():
if request.method == "GET":
......@@ -593,24 +669,26 @@ def get_languages_json():
cur_l = LC.parse(lang.lang_code)
lang.name = cur_l.get_language_name(get_locale())
except:
lang.name=_(isoLanguages.get(part3=lang.lang_code).name)
lang.name = _(isoLanguages.get(part3=lang.lang_code).name)
entries = [s for s in languages if query in s.name.lower()]
json_dumps = json.dumps([dict(name=r.name) for r in entries])
return json_dumps
@app.route("/get_series_json", methods = ['GET', 'POST'])
@app.route("/get_series_json", methods=['GET', 'POST'])
@login_required_if_no_ano
def get_series_json():
def get_series_json():
if request.method == "GET":
query = request.args.get('q')
entries = db.session.execute("select name from series where name like '%" + query + "%'")
json_dumps = json.dumps([dict(r) for r in entries])
return json_dumps
@app.route("/get_matching_tags", methods = ['GET', 'POST'])
@app.route("/get_matching_tags", methods=['GET', 'POST'])
@login_required_if_no_ano
def get_matching_tags():
def get_matching_tags():
tag_dict = {'tags': []}
if request.method == "GET":
q = db.session.query(db.Books)
......@@ -618,7 +696,8 @@ def get_matching_tags():
title_input = request.args.get('book_title')
include_tag_inputs = request.args.getlist('include_tag')
exclude_tag_inputs = request.args.getlist('exclude_tag')
q = q.filter(db.Books.authors.any(db.Authors.name.like("%" + author_input + "%")), db.Books.title.like("%"+title_input+"%"))
q = q.filter(db.Books.authors.any(db.Authors.name.like("%" + author_input + "%")),
db.Books.title.like("%" + title_input + "%"))
if len(include_tag_inputs) > 0:
for tag in include_tag_inputs:
q = q.filter(db.Books.tags.any(db.Tags.id == tag))
......@@ -632,12 +711,15 @@ def get_matching_tags():
json_dumps = json.dumps(tag_dict)
return json_dumps
@app.route("/", defaults={'page': 1})
@app.route('/page/<int:page>')
@login_required_if_no_ano
def index(page):
entries, random, pagination =fill_indexpage(page,db.Books,True, db.Books.last_modified.desc())
return render_template('index.html', random=random, entries=entries, pagination=pagination, title=_(u"Latest Books"))
entries, random, pagination = fill_indexpage(page, db.Books, True, db.Books.timestamp.desc())
return render_template('index.html', random=random, entries=entries, pagination=pagination,
title=_(u"Latest Books"))
@app.route("/hot", defaults={'page': 1})
@app.route('/hot/page/<int:page>')
......@@ -649,25 +731,29 @@ def hot_books(page):
filter = True
if current_user.show_random_books():
random = db.session.query(db.Books).filter(filter).order_by(func.random()).limit(config.RANDOM_BOOKS)
else :
else:
random = false
off = int(int(config.NEWEST_BOOKS) * (page - 1))
all_books = ub.session.query(ub.Downloads, ub.func.count(ub.Downloads.book_id)).order_by(ub.func.count(ub.Downloads.book_id).desc()).group_by(ub.Downloads.book_id)
all_books = ub.session.query(ub.Downloads, ub.func.count(ub.Downloads.book_id)).order_by(
ub.func.count(ub.Downloads.book_id).desc()).group_by(ub.Downloads.book_id)
hot_books = all_books.offset(off).limit(config.NEWEST_BOOKS)
entries = list()
for book in hot_books:
entries.append(db.session.query(db.Books).filter(filter).filter(db.Books.id == book.Downloads.book_id).first())
numBooks = entries.__len__()
pagination = Pagination(page, config.NEWEST_BOOKS, numBooks)
return render_template('index.html', random=random, entries=entries, pagination=pagination, title=_(u"Hot Books (most downloaded)"))
return render_template('index.html', random=random, entries=entries, pagination=pagination,
title=_(u"Hot Books (most downloaded)"))
@app.route("/discover", defaults={'page': 1})
@app.route('/discover/page/<int:page>')
@login_required_if_no_ano
def discover(page):
entries, random, pagination = fill_indexpage(page, db.Books, func.randomblob(2),db.Books.last_modified.desc())
entries, random, pagination = fill_indexpage(page, db.Books, func.randomblob(2), db.Books.timestamp.desc())
return render_template('discover.html', entries=entries, pagination=pagination, title=_(u"Random Books"))
@app.route("/author")
@login_required_if_no_ano
def author_list():
......@@ -675,7 +761,8 @@ def author_list():
filter = db.Books.languages.any(db.Languages.lang_code == current_user.filter_language())
else:
filter = True
entries= db.session.query(db.Authors,func.count('books_authors_link.book').label('count')).join(db.books_authors_link).join(db.Books).filter(
entries = db.session.query(db.Authors, func.count('books_authors_link.book').label('count')).join(
db.books_authors_link).join(db.Books).filter(
filter).group_by('books_authors_link.author').order_by(db.Authors.sort).all()
return render_template('list.html', entries=entries, folder='author', title=_(u"Author list"))
......@@ -692,8 +779,10 @@ def author(name):
else:
random = false
entries = db.session.query(db.Books).filter(db.Books.authors.any(db.Authors.name.like("%" + name + "%"))).filter(filter).all()
return render_template('index.html', random=random, entries=entries, title=_(u"Author: %(nam)s",nam=name))
entries = db.session.query(db.Books).filter(db.Books.authors.any(db.Authors.name.like("%" + name + "%"))).filter(
filter).all()
return render_template('index.html', random=random, entries=entries, title=_(u"Author: %(nam)s", nam=name))
@app.route("/series")
@login_required_if_no_ano
......@@ -702,21 +791,26 @@ def series_list():
filter = db.Books.languages.any(db.Languages.lang_code == current_user.filter_language())
else:
filter = True
entries= db.session.query(db.Series,func.count('books_series_link.book').label('count')).join(db.books_series_link).join(db.Books).filter(
entries = db.session.query(db.Series, func.count('books_series_link.book').label('count')).join(
db.books_series_link).join(db.Books).filter(
filter).group_by('books_series_link.series').order_by(db.Series.sort).all()
return render_template('list.html', entries=entries, folder='series', title=_(u"Series list"))
@app.route("/series/<name>/", defaults={'page': 1})
@app.route("/series/<name>/<int:page>'")
@login_required_if_no_ano
def series(name,page):
entries, random, pagination = fill_indexpage(page, db.Books, db.Books.series.any(db.Series.name == name ), db.Books.series_index)
if entries :
return render_template('index.html', random=random, pagination=pagination, entries=entries, title=_(u"Series: %(serie)s",serie=name))
else :
def series(name, page):
entries, random, pagination = fill_indexpage(page, db.Books, db.Books.series.any(db.Series.name == name),
db.Books.series_index)
if entries:
return render_template('index.html', random=random, pagination=pagination, entries=entries,
title=_(u"Series: %(serie)s", serie=name))
else:
flash(_(u"Error opening eBook. File does not exist or file is not accessible:"), category="error")
return redirect('/' or url_for("index", _external=True))
@app.route("/language")
@login_required_if_no_ano
def language_overview():
......@@ -727,32 +821,40 @@ def language_overview():
cur_l = LC.parse(lang.lang_code)
lang.name = cur_l.get_language_name(get_locale())
except:
lang.name=_(isoLanguages.get(part3=lang.lang_code).name)
else :
lang.name = _(isoLanguages.get(part3=lang.lang_code).name)
else:
try:
langfound=1
langfound = 1
cur_l = LC.parse(current_user.filter_language())
except:
langfound=0
languages = db.session.query(db.Languages).filter(db.Languages.lang_code == current_user.filter_language()).all()
langfound = 0
languages = db.session.query(db.Languages).filter(
db.Languages.lang_code == current_user.filter_language()).all()
if langfound:
languages[0].name = cur_l.get_language_name(get_locale())
else :
languages[0].name=_(isoLanguages.get(part3=languages[0].lang_code).name)
lang_counter =db.session.query(db.books_languages_link,func.count('books_languages_link.book').label('bookcount')).group_by('books_languages_link.lang_code').all()
return render_template('languages.html', languages=languages, lang_counter=lang_counter, title=_(u"Available languages"))
else:
languages[0].name = _(isoLanguages.get(part3=languages[0].lang_code).name)
lang_counter = db.session.query(db.books_languages_link,
func.count('books_languages_link.book').label('bookcount')).group_by(
'books_languages_link.lang_code').all()
return render_template('languages.html', languages=languages, lang_counter=lang_counter,
title=_(u"Available languages"))
@app.route("/language/<name>",defaults={'page': 1})
@app.route("/language/<name>", defaults={'page': 1})
@app.route('/language/<name>/page/<int:page>')
@login_required_if_no_ano
def language(name,page):
entries, random, pagination = fill_indexpage(page, db.Books, db.Books.languages.any(db.Languages.lang_code == name ), db.Books.last_modified.desc())
def language(name, page):
entries, random, pagination = fill_indexpage(page, db.Books, db.Books.languages.any(db.Languages.lang_code == name),
db.Books.timestamp.desc())
try:
cur_l = LC.parse(name)
name = cur_l.get_language_name(get_locale())
except:
name = _(isoLanguages.get(part3=name).name)
return render_template('index.html', random=random, entries=entries, pagination=pagination, title=_(u"Language: %(name)s", name=name))
return render_template('index.html', random=random, entries=entries, pagination=pagination,
title=_(u"Language: %(name)s", name=name))
@app.route("/category")
@login_required_if_no_ano
......@@ -761,16 +863,21 @@ def category_list():
filter = db.Books.languages.any(db.Languages.lang_code == current_user.filter_language())
else:
filter = True
entries= db.session.query(db.Tags,func.count('books_tags_link.book').label('count')).join(db.books_tags_link).join(db.Books).filter(
entries = db.session.query(db.Tags, func.count('books_tags_link.book').label('count')).join(
db.books_tags_link).join(db.Books).filter(
filter).group_by('books_tags_link.tag').all()
return render_template('list.html', entries=entries, folder='category', title=_(u"Category list"))
@app.route("/category/<name>",defaults={'page': 1})
@app.route("/category/<name>", defaults={'page': 1})
@app.route('/category/<name>/<int:page>')
@login_required_if_no_ano
def category(name,page):
entries, random, pagination = fill_indexpage(page, db.Books, db.Books.tags.any(db.Tags.name == name ), db.Books.last_modified.desc())
return render_template('index.html', random=random, entries=entries, pagination=pagination, title=_(u"Category: %(name)s",name=name))
def category(name, page):
entries, random, pagination = fill_indexpage(page, db.Books, db.Books.tags.any(db.Tags.name == name),
db.Books.timestamp.desc())
return render_template('index.html', random=random, entries=entries, pagination=pagination,
title=_(u"Category: %(name)s", name=name))
@app.route("/book/<int:id>")
@login_required_if_no_ano
......@@ -780,35 +887,62 @@ def show_book(id):
else:
filter = True
entries = db.session.query(db.Books).filter(db.Books.id == id).filter(filter).first()
if entries :
for index in range(0,len(entries.languages)) :
if entries:
for index in range(0, len(entries.languages)):
try:
entries.languages[index].language_name=LC.parse(entries.languages[index].lang_code).get_language_name(get_locale())
entries.languages[index].language_name = LC.parse(entries.languages[index].lang_code).get_language_name(
get_locale())
except:
entries.languages[index].language_name = _(isoLanguages.get(part3=entries.languages[index].lang_code).name)
entries.languages[index].language_name = _(
isoLanguages.get(part3=entries.languages[index].lang_code).name)
cc = db.session.query(db.Custom_Columns).filter(db.Custom_Columns.datatype.notin_(db.cc_exceptions)).all()
book_in_shelfs = []
shelfs = ub.session.query(ub.BookShelf).filter(ub.BookShelf.book_id == id).all()
for entry in shelfs:
book_in_shelfs.append(entry.shelf)
return render_template('detail.html', entry=entries, cc=cc, title=entries.title, books_shelfs=book_in_shelfs)
else :
return render_template('detail.html', entry=entries, cc=cc, title=entries.title, books_shelfs=book_in_shelfs)
else:
flash(_(u"Error opening eBook. File does not exist or file is not accessible:"), category="error")
return redirect('/' or url_for("index", _external=True))
@app.route("/admin/")
@login_required
def admin():
#return "Admin ONLY!"
# return "Admin ONLY!"
abort(403)
@app.route("/stats")
@login_required
def stats():
counter = len(db.session.query(db.Books).all())
authors = len(db.session.query(db.Authors).all())
return render_template('stats.html', bookcounter=counter, authorcounter=authors, title=_(u"Statistics"))
Versions=uploader.book_formats.get_versions()
if sys.platform == "win32":
kindlegen = os.path.join(config.MAIN_DIR, "vendor", u"kindlegen.exe")
else:
kindlegen = os.path.join(config.MAIN_DIR, "vendor", u"kindlegen")
kindlegen_version=_('not installed')
if os.path.exists(kindlegen):
p = subprocess.Popen(kindlegen, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
check = p.wait()
for lines in p.stdout.readlines():
if re.search('Amazon kindlegen\(', lines):
Versions['KindlegenVersion'] = lines
Versions['PythonVersion']=sys.version
return render_template('stats.html', bookcounter=counter, authorcounter=authors, Versions=Versions, title=_(u"Statistics"))
@app.route("/shutdown")
def shutdown():
logout_user()
# add restart command to queue
global_queue.put("something")
flash(_(u"Server restarts"), category="info")
return redirect('/' or url_for("index", _external=True))
@app.route("/search", methods=["GET"])
@login_required_if_no_ano
......@@ -819,11 +953,15 @@ def search():
filter = db.Books.languages.any(db.Languages.lang_code == current_user.filter_language())
else:
filter = True
entries = db.session.query(db.Books).filter(db.or_(db.Books.tags.any(db.Tags.name.like("%"+term+"%")),db.Books.series.any(db.Series.name.like("%"+term+"%")),db.Books.authors.any(db.Authors.name.like("%"+term+"%")),db.Books.title.like("%"+term+"%"))).filter(filter).all()
entries = db.session.query(db.Books).filter(db.or_(db.Books.tags.any(db.Tags.name.like("%" + term + "%")),
db.Books.series.any(db.Series.name.like("%" + term + "%")),
db.Books.authors.any(db.Authors.name.like("%" + term + "%")),
db.Books.title.like("%" + term + "%"))).filter(filter).all()
return render_template('search.html', searchterm=term, entries=entries)
else:
return render_template('search.html', searchterm="")
@app.route("/advanced_search", methods=["GET"])
@login_required_if_no_ano
def advanced_search():
......@@ -833,15 +971,16 @@ def advanced_search():
exclude_tag_inputs = request.args.getlist('exclude_tag')
author_name = request.args.get("author_name")
book_title = request.args.get("book_title")
if author_name: author_name=author_name.strip()
if book_title : book_title=book_title.strip()
if author_name: author_name = author_name.strip()
if book_title: book_title = book_title.strip()
if include_tag_inputs or exclude_tag_inputs or author_name or book_title:
searchterm = []
searchterm.extend((author_name, book_title))
tag_names = db.session.query(db.Tags).filter(db.Tags.id.in_(include_tag_inputs)).all()
searchterm.extend(tag.name for tag in tag_names)
searchterm = " + ".join(filter(None, searchterm))
q = q.filter(db.Books.authors.any(db.Authors.name.like("%" + author_name + "%")), db.Books.title.like("%"+book_title+"%"))
q = q.filter(db.Books.authors.any(db.Authors.name.like("%" + author_name + "%")),
db.Books.title.like("%" + book_title + "%"))
# random = db.session.query(db.Books).order_by(func.random()).limit(config.RANDOM_BOOKS)
for tag in include_tag_inputs:
q = q.filter(db.Books.tags.any(db.Tags.id == tag))
......@@ -854,28 +993,31 @@ def advanced_search():
tags = db.session.query(db.Tags).order_by(db.Tags.name).all()
return render_template('search_form.html', tags=tags)
@app.route("/cover/<path:cover_path>")
@login_required_if_no_ano
def get_cover(cover_path):
return send_from_directory(os.path.join(config.DB_ROOT, cover_path), "cover.jpg")
@app.route("/feed/cover/<path:cover_path>")
@requires_basic_auth_if_no_ano
def feed_get_cover(cover_path):
return send_from_directory(os.path.join(config.DB_ROOT, cover_path), "cover.jpg")
@app.route("/read/<int:book_id>/<format>")
@login_required
def read_book(book_id,format):
def read_book(book_id, format):
book = db.session.query(db.Books).filter(db.Books.id == book_id).first()
if book :
book_dir = os.path.join(config.MAIN_DIR, "cps","static", str(book_id))
if book:
book_dir = os.path.join(config.MAIN_DIR, "cps", "static", str(book_id))
if not os.path.exists(book_dir):
os.mkdir(book_dir)
if format.lower() == "epub":
#check if mimetype file is exists
mime_file = str(book_id) +"/mimetype"
if os.path.exists(mime_file) == False:
# check if mimetype file is exists
mime_file = str(book_id) + "/mimetype"
if not os.path.exists(mime_file):
epub_file = os.path.join(config.DB_ROOT, book.path, book.data[0].name) + ".epub"
if not os.path.isfile(epub_file):
raise ValueError('Error opening eBook. File does not exist: ', epub_file)
......@@ -898,23 +1040,32 @@ def read_book(book_id,format):
zfile.close()
return render_template('read.html', bookid=book_id, title=_(u"Read a Book"))
elif format.lower() == "pdf":
all_name = str(book_id) +"/"+ urllib.quote(book.data[0].name) +".pdf"
tmp_file = os.path.join(book_dir,urllib.quote(book.data[0].name)) + ".pdf"
if os.path.exists(tmp_file) == False:
pdf_file = os.path.join(config.DB_ROOT, book.path, book.data[0].name) + ".pdf"
copyfile(pdf_file,tmp_file)
all_name = str(book_id) + "/" + urllib.quote(book.data[0].name) + ".pdf"
tmp_file = os.path.join(book_dir, urllib.quote(book.data[0].name)) + ".pdf"
if not os.path.exists(tmp_file):
pdf_file = os.path.join(config.DB_ROOT, book.path, book.data[0].name) + ".pdf"
copyfile(pdf_file, tmp_file)
return render_template('readpdf.html', pdffile=all_name, title=_(u"Read a Book"))
elif format.lower() == "txt":
all_name = str(book_id) +"/"+ urllib.quote(book.data[0].name) +".txt"
tmp_file = os.path.join(book_dir,urllib.quote(book.data[0].name)) + ".txt"
if os.path.exists(all_name) == False:
txt_file = os.path.join(config.DB_ROOT, book.path, book.data[0].name) + ".txt"
copyfile(txt_file,tmp_file)
elif format.lower() == "txt":
all_name = str(book_id) + "/" + urllib.quote(book.data[0].name) + ".txt"
tmp_file = os.path.join(book_dir, urllib.quote(book.data[0].name)) + ".txt"
if not os.path.exists(all_name):
txt_file = os.path.join(config.DB_ROOT, book.path, book.data[0].name) + ".txt"
copyfile(txt_file, tmp_file)
return render_template('readtxt.html', txtfile=all_name, title=_(u"Read a Book"))
else :
elif format.lower() == "cbr":
all_name = str(book_id) + "/" + urllib.quote(book.data[0].name) + ".cbr"
tmp_file = os.path.join(book_dir, urllib.quote(book.data[0].name)) + ".cbr"
if not os.path.exists(all_name):
cbr_file = os.path.join(config.DB_ROOT, book.path, book.data[0].name) + ".cbr"
copyfile(cbr_file, tmp_file)
return render_template('readcbr.html', comicfile=all_name, title=_(u"Read a Book"))
else:
flash(_(u"Error opening eBook. File does not exist or file is not accessible:"), category="error")
return redirect('/' or url_for("index", _external=True))
@app.route("/download/<int:book_id>/<format>")
@login_required
@download_required
......@@ -926,19 +1077,20 @@ def get_download_link(book_id, format):
author = helper.get_normalized_author(book.author_sort)
file_name = book.title
if len(author) > 0:
file_name = author+'-'+file_name
file_name = author + '-' + file_name
file_name = helper.get_valid_filename(file_name)
response = make_response(send_from_directory(os.path.join(config.DB_ROOT, book.path), data.name + "." +format))
response = make_response(send_from_directory(os.path.join(config.DB_ROOT, book.path), data.name + "." + format))
response.headers["Content-Disposition"] = \
"attachment; " \
"filename={utf_filename}.{suffix};" \
"filename*=UTF-8''{utf_filename}.{suffix}".format(
utf_filename=file_name.encode('utf-8'),
suffix=format
)
utf_filename=file_name.encode('utf-8'),
suffix=format
)
return response
@app.route('/register', methods = ['GET', 'POST'])
@app.route('/register', methods=['GET', 'POST'])
def register():
error = None
if not config.PUBLIC_REG:
......@@ -975,7 +1127,8 @@ def register():
return render_template('register.html', title=_(u"register"))
@app.route('/login', methods = ['GET', 'POST'])
@app.route('/login', methods=['GET', 'POST'])
def login():
error = None
......@@ -987,13 +1140,14 @@ def login():
user = ub.session.query(ub.User).filter(ub.User.nickname == form['username'].strip()).first()
if user and check_password_hash(user.password, form['password']):
login_user(user, remember = True)
login_user(user, remember=True)
flash(_(u"you are now logged in as: '%(nickname)s'", nickname=user.nickname), category="success")
return redirect('/' or url_for("index", _external=True))
else:
flash(_(u"Wrong Username or Password"), category="error")
return render_template('login.html',title=_(u"login"))
return render_template('login.html', title=_(u"login"))
@app.route('/logout')
@login_required
......@@ -1013,14 +1167,16 @@ def send_to_kindle(book_id):
elif current_user.kindle_mail:
result = helper.send_mail(book_id, current_user.kindle_mail)
if result is None:
flash(_(u"Book successfully send to %(kindlemail)s", kindlemail=current_user.kindle_mail), category="success")
flash(_(u"Book successfully send to %(kindlemail)s", kindlemail=current_user.kindle_mail),
category="success")
helper.update_download(book_id, int(current_user.id))
else:
flash(_(u"There was an error sending this book: %(res)s",res=result), category="error")
flash(_(u"There was an error sending this book: %(res)s", res=result), category="error")
else:
flash(_(u"Please configure your kindle email address first..."), category="error")
return redirect(request.environ["HTTP_REFERER"])
@app.route("/shelf/add/<int:shelf_id>/<int:book_id>")
@login_required
def add_to_shelf(shelf_id, book_id):
......@@ -1033,11 +1189,12 @@ def add_to_shelf(shelf_id, book_id):
ub.session.add(ins)
ub.session.commit()
flash(_(u"Book has been added to shelf: %(sname)s",sname=shelf.name), category="success")
flash(_(u"Book has been added to shelf: %(sname)s", sname=shelf.name), category="success")
#return redirect(url_for('show_book', id=book_id))
# return redirect(url_for('show_book', id=book_id))
return redirect(request.environ["HTTP_REFERER"])
@app.route("/shelf/remove/<int:shelf_id>/<int:book_id>")
@login_required
def remove_from_shelf(shelf_id, book_id):
......@@ -1046,16 +1203,18 @@ def remove_from_shelf(shelf_id, book_id):
flash("Sorry you are not allowed to remove a book from this shelf: %s" % shelf.name)
return redirect(url_for('index', _external=True))
book_shelf = ub.session.query(ub.BookShelf).filter(ub.BookShelf.shelf == shelf_id, ub.BookShelf.book_id == book_id).first()
book_shelf = ub.session.query(ub.BookShelf).filter(ub.BookShelf.shelf == shelf_id,
ub.BookShelf.book_id == book_id).first()
#rem = ub.BookShelf(shelf=shelf.id, book_id=book_id)
# rem = ub.BookShelf(shelf=shelf.id, book_id=book_id)
ub.session.delete(book_shelf)
ub.session.commit()
flash(_(u"Book has been removed from shelf: %(sname)s",sname=shelf.name), category="success")
flash(_(u"Book has been removed from shelf: %(sname)s", sname=shelf.name), category="success")
return redirect(request.environ["HTTP_REFERER"])
@app.route("/shelf/create", methods=["GET", "POST"])
@login_required
def create_shelf():
......@@ -1068,17 +1227,18 @@ def create_shelf():
shelf.user_id = int(current_user.id)
existing_shelf = ub.session.query(ub.Shelf).filter(ub.Shelf.name == shelf.name).first()
if existing_shelf:
flash(_(u"A shelf with the name '%(title)s' already exists.",title=to_save["title"]), category="error")
flash(_(u"A shelf with the name '%(title)s' already exists.", title=to_save["title"]), category="error")
else:
try:
ub.session.add(shelf)
ub.session.commit()
flash(_(u"Shelf %(title)s created",title=to_save["title"]), category="success")
flash(_(u"Shelf %(title)s created", title=to_save["title"]), category="success")
except:
flash(_(u"There was an error"), category="error")
return render_template('shelf_edit.html',title=_(u"create a shelf"))
return render_template('shelf_edit.html', title=_(u"create a shelf"))
else:
return render_template('shelf_edit.html',title=_(u"create a shelf"))
return render_template('shelf_edit.html', title=_(u"create a shelf"))
@app.route("/shelf/delete/<int:shelf_id>")
@login_required
......@@ -1089,21 +1249,28 @@ def delete_shelf(shelf_id):
deleted = ub.session.query(ub.Shelf).filter(ub.Shelf.id == shelf_id).delete()
else:
deleted = ub.session.query(ub.Shelf).filter(ub.or_(ub.and_(ub.Shelf.user_id == int(current_user.id), ub.Shelf.id == shelf_id), ub.and_(ub.Shelf.is_public == 1, ub.Shelf.id == shelf_id))).delete()
deleted = ub.session.query(ub.Shelf).filter(ub.or_(ub.and_(ub.Shelf.user_id == int(current_user.id),
ub.Shelf.id == shelf_id),
ub.and_(ub.Shelf.is_public == 1,
ub.Shelf.id == shelf_id))).delete()
if deleted:
ub.session.query(ub.BookShelf).filter(ub.BookShelf.shelf == shelf_id).delete()
ub.session.commit()
flash( _("successfully deleted shelf %(name)s", name=cur_shelf.name, category="success") )
flash(_("successfully deleted shelf %(name)s", name=cur_shelf.name, category="success"))
return redirect(url_for('index'))
@app.route("/shelf/<int:shelf_id>")
@login_required_if_no_ano
def show_shelf(shelf_id):
if current_user.is_anonymous():
shelf = ub.session.query(ub.Shelf).filter(ub.Shelf.is_public == 1, ub.Shelf.id == shelf_id).first()
else :
shelf = ub.session.query(ub.Shelf).filter(ub.or_(ub.and_(ub.Shelf.user_id == int(current_user.id), ub.Shelf.id == shelf_id), ub.and_(ub.Shelf.is_public == 1, ub.Shelf.id == shelf_id))).first()
else:
shelf = ub.session.query(ub.Shelf).filter(ub.or_(ub.and_(ub.Shelf.user_id == int(current_user.id),
ub.Shelf.id == shelf_id),
ub.and_(ub.Shelf.is_public == 1,
ub.Shelf.id == shelf_id))).first()
result = list()
if shelf:
books_in_shelf = ub.session.query(ub.BookShelf).filter(ub.BookShelf.shelf == shelf_id).all()
......@@ -1111,9 +1278,10 @@ def show_shelf(shelf_id):
cur_book = db.session.query(db.Books).filter(db.Books.id == book.book_id).first()
result.append(cur_book)
return render_template('shelf.html', entries=result, title=_(u"Shelf: '%(name)s'" ,name=shelf.name), shelf=shelf)
return render_template('shelf.html', entries=result, title=_(u"Shelf: '%(name)s'", name=shelf.name), shelf=shelf)
@app.route("/me", methods = ["GET", "POST"])
@app.route("/me", methods=["GET", "POST"])
@login_required
def profile():
content = ub.session.query(ub.User).filter(ub.User.id == int(current_user.id)).first()
......@@ -1125,7 +1293,7 @@ def profile():
lang.name = cur_l.get_language_name(get_locale())
except:
lang.name = _(isoLanguages.get(part3=lang.lang_code).name)
translations=babel.list_translations()+[LC('en')]
translations = babel.list_translations() + [LC('en')]
for book in content.downloads:
downloads.append(db.session.query(db.Books).filter(db.Books.id == book.book_id).first())
if request.method == "POST":
......@@ -1144,14 +1312,34 @@ def profile():
content.default_language = to_save["default_language"]
if to_save["locale"]:
content.locale = to_save["locale"]
content.random_books = 0
content.language_books = 0
content.series_books = 0
content.category_books = 0
content.hot_books = 0
if "show_random" in to_save and to_save["show_random"] == "on":
content.random_books = 1
if "show_language" in to_save and to_save["show_language"] == "on":
content.language_books = 1
if "show_series" in to_save and to_save["show_series"] == "on":
content.series_books = 1
if "show_category" in to_save and to_save["show_category"] == "on":
content.category_books = 1
if "show_hot" in to_save and to_save["show_hot"] == "on":
content.hot_books = 1
if "default_language" in to_save:
content.default_language = to_save["default_language"]
try:
ub.session.commit()
except IntegrityError:
ub.session.rollback()
flash(_(u"Found an existing account for this email address."), category="error")
return render_template("user_edit.html", content=content, downloads=downloads, title=_(u"%(name)s's profile", name=current_user.nickname))
return render_template("user_edit.html", content=content, downloads=downloads,
title=_(u"%(name)s's profile", name=current_user.nickname))
flash(_(u"Profile updated"), category="success")
return render_template("user_edit.html", translations=translations, profile=1, languages=languages, content=content, downloads=downloads, title=_(u"%(name)s's profile", name=current_user.nickname))
return render_template("user_edit.html", translations=translations, profile=1, languages=languages, content=content,
downloads=downloads, title=_(u"%(name)s's profile", name=current_user.nickname))
@app.route("/admin/user")
@login_required
......@@ -1161,7 +1349,8 @@ def user_list():
settings = ub.session.query(ub.Settings).first()
return render_template("user_list.html", content=content, email=settings, title=_(u"User list"))
@app.route("/admin/user/new", methods = ["GET", "POST"])
@app.route("/admin/user/new", methods=["GET", "POST"])
@login_required
@admin_required
def new_user():
......@@ -1173,7 +1362,7 @@ def new_user():
lang.name = cur_l.get_language_name(get_locale())
except:
lang.name = _(isoLanguages.get(part3=lang.lang_code).name)
translations=babel.list_translations()+[LC('en')]
translations = babel.list_translations() + [LC('en')]
if request.method == "POST":
to_save = request.form.to_dict()
if not to_save["nickname"] or not to_save["email"] or not to_save["password"]:
......@@ -1211,14 +1400,16 @@ def new_user():
try:
ub.session.add(content)
ub.session.commit()
flash(_("User '%(user)s' created" , user=content.nickname), category="success")
flash(_("User '%(user)s' created", user=content.nickname), category="success")
return redirect(url_for('user_list', _external=True))
except IntegrityError:
ub.session.rollback()
flash(_(u"Found an existing account for this email address or nickname."), category="error")
return render_template("user_edit.html", new_user=1, content=content, translations=translations, languages=languages, title="Add new user")
return render_template("user_edit.html", new_user=1, content=content, translations=translations,
languages=languages, title="Add new user")
@app.route("/admin/user/mailsettings", methods = ["GET", "POST"])
@app.route("/admin/user/mailsettings", methods=["GET", "POST"])
@login_required
@admin_required
def edit_mailsettings():
......@@ -1237,11 +1428,21 @@ def edit_mailsettings():
try:
ub.session.commit()
flash(_(u"Mail settings updated"), category="success")
except (e):
except e:
flash(e, category="error")
if to_save["test"]:
result=helper.send_test_mail(current_user.kindle_mail)
if result is None:
flash(_(u"Test E-Mail successfully send to %(kindlemail)s", kindlemail=current_user.kindle_mail),
category="success")
else:
flash(_(u"There was an error sending the Test E-Mail: %(res)s", res=result), category="error")
else:
flash(_(u"Mail settings updated"), category="success")
return render_template("email_edit.html", content=content, title=_("Edit mail settings"))
@app.route("/admin/user/<int:user_id>", methods = ["GET", "POST"])
@app.route("/admin/user/<int:user_id>", methods=["GET", "POST"])
@login_required
@admin_required
def edit_user(user_id):
......@@ -1261,35 +1462,35 @@ def edit_user(user_id):
to_save = request.form.to_dict()
if "delete" in to_save:
ub.session.delete(content)
flash(_(u"User '%(nick)s' deleted",nick=content.nickname), category="success")
flash(_(u"User '%(nick)s' deleted", nick=content.nickname), category="success")
return redirect(url_for('user_list', _external=True))
else:
if to_save["password"]:
content.password = generate_password_hash(to_save["password"])
if "admin_role" in to_save and not content.role_admin():
content.role = content.role + ub.ROLE_ADMIN
elif not "admin_role" in to_save and content.role_admin():
elif "admin_role" not in to_save and content.role_admin():
content.role = content.role - ub.ROLE_ADMIN
if "download_role" in to_save and not content.role_download():
content.role = content.role + ub.ROLE_DOWNLOAD
elif not "download_role" in to_save and content.role_download():
elif "download_role" not in to_save and content.role_download():
content.role = content.role - ub.ROLE_DOWNLOAD
if "upload_role" in to_save and not content.role_upload():
content.role = content.role + ub.ROLE_UPLOAD
elif not "upload_role" in to_save and content.role_upload():
elif "upload_role" not in to_save and content.role_upload():
content.role = content.role - ub.ROLE_UPLOAD
if "edit_role" in to_save and not content.role_edit():
content.role = content.role + ub.ROLE_EDIT
elif not "edit_role" in to_save and content.role_edit():
elif "edit_role" not in to_save and content.role_edit():
content.role = content.role - ub.ROLE_EDIT
if "passwd_role" in to_save and not content.role_passwd():
content.role = content.role + ub.ROLE_PASSWD
elif not "passwd_role" in to_save and content.role_passwd():
elif "passwd_role" not in to_save and content.role_passwd():
content.role = content.role - ub.ROLE_PASSWD
content.random_books = 0
content.language_books = 0
......@@ -1316,18 +1517,20 @@ def edit_user(user_id):
content.kindle_mail = to_save["kindle_mail"]
try:
ub.session.commit()
flash(_(u"User '%(nick)s' updated",nick= content.nickname), category="success")
flash(_(u"User '%(nick)s' updated", nick=content.nickname), category="success")
except IntegrityError:
ub.session.rollback()
flash(_(u"An unknown error occured."), category="error")
return render_template("user_edit.html", translations=translations, languages=languages, new_user=0, content=content, downloads=downloads, title=_(u"Edit User %(nick)s",nick=content.nickname))
return render_template("user_edit.html", translations=translations, languages=languages, new_user=0,
content=content, downloads=downloads, title=_(u"Edit User %(nick)s", nick=content.nickname))
@app.route("/admin/book/<int:book_id>", methods=['GET', 'POST'])
@login_required
@edit_required
def edit_book(book_id):
## create the function for sorting...
db.session.connection().connection.connection.create_function("title_sort",1,db.title_sort)
# create the function for sorting...
db.session.connection().connection.connection.create_function("title_sort", 1, db.title_sort)
cc = db.session.query(db.Custom_Columns).filter(db.Custom_Columns.datatype.notin_(db.cc_exceptions)).all()
if current_user.filter_language() != "all":
filter = db.Books.languages.any(db.Languages.lang_code == current_user.filter_language())
......@@ -1338,9 +1541,10 @@ def edit_book(book_id):
if book:
for index in range(0, len(book.languages)):
try:
book.languages[index].language_name=LC.parse(book.languages[index].lang_code).get_language_name(get_locale())
book.languages[index].language_name = LC.parse(book.languages[index].lang_code).get_language_name(
get_locale())
except:
book.languages[index].language_name=_(isoLanguages.get(part3=book.languages[index].lang_code).name)
book.languages[index].language_name = _(isoLanguages.get(part3=book.languages[index].lang_code).name)
for author in book.authors:
author_names.append(author.name)
if request.method == 'POST':
......@@ -1356,7 +1560,7 @@ def edit_book(book_id):
modify_database_object(input_authors, book.authors, db.Authors, db.session, 'author')
if author0_before_edit != book.authors[0].name:
edited_books_id.add(book.id)
if to_save["cover_url"] and os.path.splitext(to_save["cover_url"])[1].lower() == ".jpg":
img = requests.get(to_save["cover_url"])
f = open(os.path.join(config.DB_ROOT, book.path, "cover.jpg"), "wb")
......@@ -1384,7 +1588,7 @@ def edit_book(book_id):
# retranslate displayed text to language codes
languages = db.session.query(db.Languages).all()
input_l=[]
input_l = []
for lang in languages:
try:
lang.name = LC.parse(lang.lang_code).get_language_name(get_locale()).lower()
......@@ -1399,7 +1603,7 @@ def edit_book(book_id):
old_rating = False
if len(book.ratings) > 0:
old_rating = book.ratings[0].rating
ratingx2 = int(float(to_save["rating"]) *2)
ratingx2 = int(float(to_save["rating"]) * 2)
if ratingx2 != old_rating:
is_rating = db.session.query(db.Ratings).filter(db.Ratings.rating == ratingx2).first()
if is_rating:
......@@ -1423,43 +1627,45 @@ def edit_book(book_id):
if to_save[cc_string].strip():
if c.datatype == 'bool':
if to_save[cc_string] == 'None':
to_save[cc_string]= None
to_save[cc_string] = None
else:
to_save[cc_string] = 1 if to_save[cc_string] == 'True' else 0
if to_save[cc_string] != cc_db_value:
if cc_db_value is not None:
if to_save[cc_string] is not None:
setattr(getattr(book, cc_string)[0], 'value', to_save[cc_string])
else :
else:
del_cc = getattr(book, cc_string)[0]
getattr(book, cc_string).remove(del_cc)
db.session.delete(del_cc)
else :
else:
cc_class = db.cc_classes[c.id]
new_cc = cc_class(value=to_save[cc_string],book=book_id)
new_cc = cc_class(value=to_save[cc_string], book=book_id)
db.session.add(new_cc)
else:
if c.datatype == 'rating':
to_save[cc_string] = str(int(float(to_save[cc_string]) *2))
to_save[cc_string] = str(int(float(to_save[cc_string]) * 2))
if to_save[cc_string].strip() != cc_db_value:
if cc_db_value != None:
#remove old cc_val
if cc_db_value is not None:
# remove old cc_val
del_cc = getattr(book, cc_string)[0]
getattr(book, cc_string).remove(del_cc)
if len(del_cc.books) == 0:
db.session.delete(del_cc)
cc_class = db.cc_classes[c.id]
new_cc = db.session.query(cc_class).filter(cc_class.value == to_save[cc_string].strip()).first()
new_cc = db.session.query(cc_class).filter(
cc_class.value == to_save[cc_string].strip()).first()
# if no cc val is found add it
if new_cc == None:
if new_cc is None:
new_cc = cc_class(value=to_save[cc_string].strip())
db.session.add(new_cc)
new_cc = db.session.query(cc_class).filter(cc_class.value == to_save[cc_string].strip()).first()
new_cc = db.session.query(cc_class).filter(
cc_class.value == to_save[cc_string].strip()).first()
# add cc value to book
getattr(book, cc_string).append(new_cc)
else:
if cc_db_value != None:
#remove old cc_val
if cc_db_value is not None:
# remove old cc_val
del_cc = getattr(book, cc_string)[0]
getattr(book, cc_string).remove(del_cc)
if len(del_cc.books) == 0:
......@@ -1476,7 +1682,7 @@ def edit_book(book_id):
for inp_tag in input_tags:
if inp_tag == c_tag.value:
found = True
break;
break
# if the tag was not found in the new list, add him to remove list
if not found:
del_tags.append(c_tag)
......@@ -1487,7 +1693,7 @@ def edit_book(book_id):
for c_tag in getattr(book, cc_string):
if inp_tag == c_tag.value:
found = True
break;
break
if not found:
add_tags.append(inp_tag)
# if there are tags to remove, we remove them now
......@@ -1500,12 +1706,14 @@ def edit_book(book_id):
if len(add_tags) > 0:
for add_tag in add_tags:
# check if a tag with that name exists
new_tag = db.session.query(db.cc_classes[c.id]).filter(db.cc_classes[c.id].value == add_tag).first()
new_tag = db.session.query(db.cc_classes[c.id]).filter(
db.cc_classes[c.id].value == add_tag).first()
# if no tag is found add it
if new_tag == None:
if new_tag is None:
new_tag = db.cc_classes[c.id](value=add_tag)
db.session.add(new_tag)
new_tag = db.session.query(db.cc_classes[c.id]).filter(db.cc_classes[c.id].value == add_tag).first()
new_tag = db.session.query(db.cc_classes[c.id]).filter(
db.cc_classes[c.id].value == add_tag).first()
# add tag to book
getattr(book, cc_string).append(new_tag)
......@@ -1525,18 +1733,16 @@ def edit_book(book_id):
flash(_(u"Error opening eBook. File does not exist or file is not accessible:"), category="error")
return redirect('/' or url_for("index", _external=True))
import uploader
from shutil import move
@app.route("/upload", methods = ["GET", "POST"])
@app.route("/upload", methods=["GET", "POST"])
@login_required
@upload_required
def upload():
if not config.UPLOADING:
abort(404)
## create the function for sorting...
db.session.connection().connection.connection.create_function("title_sort",1,db.title_sort)
db.session.connection().connection.connection.create_function('uuid4', 0, lambda : str(uuid4()))
# create the function for sorting...
db.session.connection().connection.connection.create_function("title_sort", 1, db.title_sort)
db.session.connection().connection.connection.create_function('uuid4', 0, lambda: str(uuid4()))
if request.method == 'POST' and 'btn-upload' in request.files:
file = request.files['btn-upload']
meta = uploader.upload(file)
......@@ -1582,11 +1788,12 @@ def upload():
db_author = db.Authors(author, "", "")
db.session.add(db_author)
path = os.path.join(author_dir, title_dir)
db_book = db.Books(title, "", "", datetime.datetime.now(), datetime.datetime(101, 01,01), 1, datetime.datetime.now(), path, has_cover, db_author, [])
db_book = db.Books(title, "", "", datetime.datetime.now(), datetime.datetime(101, 01, 01), 1,
datetime.datetime.now(), path, has_cover, db_author, [])
db_book.authors.append(db_author)
db_data = db.Data(db_book, meta.extension.upper()[1:], file_size, data_name)
db_book.data.append(db_data)
db.session.add(db_book)
db.session.commit()
author_names = []
......@@ -1596,4 +1803,4 @@ def upload():
if current_user.role_edit() or current_user.role_admin():
return render_template('edit_book.html', book=db_book, authors=author_names, cc=cc)
book_in_shelfs = []
return render_template('detail.html', entry=db_book, cc=cc, title=db_book.title, books_shelfs=book_in_shelfs)
\ No newline at end of file
return render_template('detail.html', entry=db_book, cc=cc, title=db_book.title, books_shelfs=book_in_shelfs)
......@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\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"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
......@@ -17,244 +17,266 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.3.4\n"
#: cps/helper.py:80 cps/templates/detail.html:113
msgid "Send to Kindle"
#: cps/book_formats.py:108 cps/book_formats.py:112 cps/web.py:923
msgid "not installed"
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."
msgstr ""
#: cps/helper.py:103 cps/helper.py:118
msgid "Could not find any formats suitable for sending by email"
#: cps/helper.py:128 cps/helper.py:216
#, python-format
msgid "Failed to send mail: %s"
msgstr ""
#: cps/helper.py:112
msgid "Could not convert epub to mobi"
#: cps/helper.py:146 cps/templates/detail.html:113
msgid "Send to Kindle"
msgstr ""
#: cps/helper.py:142
#, python-format
msgid "Failed to send mail: %s"
#: cps/helper.py:169 cps/helper.py:184
msgid "Could not find any formats suitable for sending by email"
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?"
msgstr ""
#: cps/web.py:639
#: cps/web.py:717
msgid "Latest Books"
msgstr ""
#: cps/web.py:661
#: cps/web.py:742
msgid "Hot Books (most downloaded)"
msgstr ""
#: cps/templates/index.xml:41 cps/web.py:668
#: cps/templates/index.xml:41 cps/web.py:750
msgid "Random Books"
msgstr ""
#: cps/web.py:679
#: cps/web.py:763
msgid "Author list"
msgstr ""
#: cps/web.py:695
#: cps/web.py:780
#, python-format
msgid "Author: %(nam)s"
msgstr ""
#: cps/templates/index.xml:65 cps/web.py:706
#: cps/templates/index.xml:65 cps/web.py:793
msgid "Series list"
msgstr ""
#: cps/web.py:714
#: cps/web.py:804
#, python-format
msgid "Series: %(serie)s"
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:"
msgstr ""
#: cps/web.py:742
#: cps/web.py:837
msgid "Available languages"
msgstr ""
#: cps/web.py:754
#: cps/web.py:852
#, python-format
msgid "Language: %(name)s"
msgstr ""
#: cps/templates/index.xml:57 cps/web.py:765
#: cps/templates/index.xml:57 cps/web.py:865
msgid "Category list"
msgstr ""
#: cps/web.py:772
#: cps/web.py:875
#, python-format
msgid "Category: %(name)s"
msgstr ""
#: cps/web.py:810
#: cps/web.py:931
msgid "Statistics"
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"
msgstr ""
#: cps/web.py:951 cps/web.py:1179
#: cps/web.py:1100 cps/web.py:1365
msgid "Please fill out all fields!"
msgstr ""
#: cps/web.py:967
#: cps/web.py:1116
msgid "An unknown error occured. Please try again later."
msgstr ""
#: cps/web.py:972
#: cps/web.py:1121
msgid "This username or email address is already in use."
msgstr ""
#: cps/web.py:975
#: cps/web.py:1124
msgid "register"
msgstr ""
#: cps/web.py:990
#: cps/web.py:1140
#, python-format
msgid "you are now logged in as: '%(nickname)s'"
msgstr ""
#: cps/web.py:993
#: cps/web.py:1143
msgid "Wrong Username or Password"
msgstr ""
#: cps/web.py:995
#: cps/web.py:1145
msgid "login"
msgstr ""
#: cps/web.py:1011
#: cps/web.py:1162
msgid "Please configure the SMTP mail settings first..."
msgstr ""
#: cps/web.py:1015
#: cps/web.py:1166
#, python-format
msgid "Book successfully send to %(kindlemail)s"
msgstr ""
#: cps/web.py:1018
#: cps/web.py:1170
#, python-format
msgid "There was an error sending this book: %(res)s"
msgstr ""
#: cps/web.py:1020
#: cps/web.py:1172
msgid "Please configure your kindle email address first..."
msgstr ""
#: cps/web.py:1035
#: cps/web.py:1188
#, python-format
msgid "Book has been added to shelf: %(sname)s"
msgstr ""
#: cps/web.py:1054
#: cps/web.py:1209
#, python-format
msgid "Book has been removed from shelf: %(sname)s"
msgstr ""
#: cps/web.py:1070
#: cps/web.py:1226
#, python-format
msgid "A shelf with the name '%(title)s' already exists."
msgstr ""
#: cps/web.py:1075
#: cps/web.py:1231
#, python-format
msgid "Shelf %(title)s created"
msgstr ""
#: cps/web.py:1077
#: cps/web.py:1233
msgid "There was an error"
msgstr ""
#: cps/web.py:1078 cps/web.py:1080
#: cps/web.py:1234 cps/web.py:1236
msgid "create a shelf"
msgstr ""
#: cps/web.py:1096
#: cps/web.py:1256
#, python-format
msgid "successfully deleted shelf %(name)s"
msgstr ""
#: cps/web.py:1113
#: cps/web.py:1277
#, python-format
msgid "Shelf: '%(name)s'"
msgstr ""
#: cps/web.py:1150
#: cps/web.py:1332
msgid "Found an existing account for this email address."
msgstr ""
#: cps/web.py:1151 cps/web.py:1153
#: cps/web.py:1334 cps/web.py:1337
#, python-format
msgid "%(name)s's profile"
msgstr ""
#: cps/web.py:1152
#: cps/web.py:1335
msgid "Profile updated"
msgstr ""
#: cps/web.py:1161
#: cps/web.py:1346
msgid "User list"
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"
msgstr ""
#: cps/web.py:1213
#: cps/web.py:1399
#, python-format
msgid "User '%(user)s' created"
msgstr ""
#: cps/web.py:1217
#: cps/web.py:1403
msgid "Found an existing account for this email address or nickname."
msgstr ""
#: cps/web.py:1238
#: cps/web.py:1426 cps/web.py:1437
msgid "Mail settings updated"
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"
msgstr ""
#: cps/web.py:1263
#: cps/web.py:1461
#, python-format
msgid "User '%(nick)s' deleted"
msgstr ""
#: cps/web.py:1318
#: cps/web.py:1516
#, python-format
msgid "User '%(nick)s' updated"
msgstr ""
#: cps/web.py:1321
#: cps/web.py:1519
msgid "An unknown error occured."
msgstr ""
#: cps/web.py:1322
#: cps/web.py:1521
#, python-format
msgid "Edit User %(nick)s"
msgstr ""
#: cps/web.py:1556
#: cps/web.py:1759
#, python-format
msgid "Failed to create path %s (Permission denied)."
msgstr ""
#: cps/web.py:1561
#: cps/web.py:1764
#, python-format
msgid "Failed to store file %s (Permission denied)."
msgstr ""
#: cps/web.py:1566
#: cps/web.py:1769
#, python-format
msgid "Failed to delete file %s (Permission denied)."
msgstr ""
......@@ -339,14 +361,14 @@ msgstr ""
msgid "view book after edit"
msgstr ""
#: cps/templates/edit_book.html:105 cps/templates/email_edit.html:30
#: cps/templates/login.html:19 cps/templates/search_form.html:33
#: cps/templates/shelf_edit.html:15 cps/templates/user_edit.html:93
#: cps/templates/edit_book.html:105 cps/templates/login.html:19
#: cps/templates/search_form.html:33 cps/templates/shelf_edit.html:15
#: cps/templates/user_edit.html:94
msgid "Submit"
msgstr ""
#: cps/templates/edit_book.html:106 cps/templates/email_edit.html:31
#: cps/templates/user_edit.html:95
#: cps/templates/edit_book.html:106 cps/templates/email_edit.html:32
#: cps/templates/user_edit.html:96
msgid "Back"
msgstr ""
......@@ -374,6 +396,14 @@ msgstr ""
msgid "From e-mail"
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
msgid "Next"
msgstr ""
......@@ -504,6 +534,14 @@ msgstr ""
msgid "Remember me"
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
msgid "Register a new account"
msgstr ""
......@@ -544,6 +582,10 @@ msgstr ""
msgid "Delete this Shelf"
msgstr ""
#: cps/templates/shelf.html:7
msgid "Edit Shelf name"
msgstr ""
#: cps/templates/shelf_edit.html:7
msgid "Title"
msgstr ""
......@@ -552,11 +594,27 @@ msgstr ""
msgid "should the shelf be public?"
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"
msgstr ""
#: cps/templates/stats.html:5
#: cps/templates/stats.html:41
msgid "Authors in this Library"
msgstr ""
......@@ -572,51 +630,51 @@ msgstr ""
msgid "Show all"
msgstr ""
#: cps/templates/user_edit.html:46
#: cps/templates/user_edit.html:45
msgid "Show random books"
msgstr ""
#: cps/templates/user_edit.html:50
#: cps/templates/user_edit.html:49
msgid "Show hot books"
msgstr ""
#: cps/templates/user_edit.html:54
#: cps/templates/user_edit.html:53
msgid "Show language selection"
msgstr ""
#: cps/templates/user_edit.html:58
#: cps/templates/user_edit.html:57
msgid "Show series selection"
msgstr ""
#: cps/templates/user_edit.html:62
#: cps/templates/user_edit.html:61
msgid "Show category selection"
msgstr ""
#: cps/templates/user_edit.html:67
#: cps/templates/user_edit.html:68
msgid "Admin user"
msgstr ""
#: cps/templates/user_edit.html:71
#: cps/templates/user_edit.html:72
msgid "Allow Downloads"
msgstr ""
#: cps/templates/user_edit.html:75
#: cps/templates/user_edit.html:76
msgid "Allow Uploads"
msgstr ""
#: cps/templates/user_edit.html:79
#: cps/templates/user_edit.html:80
msgid "Allow Edit"
msgstr ""
#: cps/templates/user_edit.html:83
#: cps/templates/user_edit.html:84
msgid "Allow Changing Password"
msgstr ""
#: cps/templates/user_edit.html:89
#: cps/templates/user_edit.html:90
msgid "Delete this user"
msgstr ""
#: cps/templates/user_edit.html:100
#: cps/templates/user_edit.html:101
msgid "Recent Downloads"
msgstr ""
......@@ -664,5 +722,3 @@ msgstr ""
msgid "Change SMTP settings"
msgstr ""
msgid "Latin"
msgstr ""
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment