Started migration of config to app database
This commit is contained in:
parent
a2a48515d4
commit
4eee58c21c
58
cps.py
58
cps.py
|
@ -1,9 +1,5 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
import os
|
||||
import sys
|
||||
from threading import Thread
|
||||
from multiprocessing import Queue
|
||||
import time
|
||||
|
||||
base_path = os.path.dirname(os.path.abspath(__file__))
|
||||
|
@ -12,50 +8,22 @@ base_path = os.path.dirname(os.path.abspath(__file__))
|
|||
sys.path.insert(0, os.path.join(base_path, 'vendor'))
|
||||
|
||||
from cps import web
|
||||
from cps import config
|
||||
# from cps import config
|
||||
from tornado.wsgi import WSGIContainer
|
||||
from tornado.httpserver import HTTPServer
|
||||
from tornado.ioloop import IOLoop
|
||||
|
||||
global title_sort
|
||||
|
||||
|
||||
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()
|
||||
'''if config.DEVELOPMENT:
|
||||
web.app.run(host="0.0.0.0", port=web.config.config_port, debug=True)
|
||||
else:'''
|
||||
http_server = HTTPServer(WSGIContainer(web.app))
|
||||
http_server.listen(web.config.config_port)
|
||||
IOLoop.instance().start()
|
||||
|
||||
if web.global_task == 0:
|
||||
print("Performing restart of Calibre-web")
|
||||
os.execl(sys.executable,sys.executable, *sys.argv)
|
||||
else:
|
||||
print("Performing shutdown of Calibre-web")
|
||||
os._exit(0)
|
||||
|
|
|
@ -1,97 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
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")
|
||||
CFG = ConfigObj(CONFIG_FILE)
|
||||
CFG.encoding = 'UTF-8'
|
||||
|
||||
|
||||
def CheckSection(sec):
|
||||
""" Check if INI section exists, if not create it """
|
||||
try:
|
||||
CFG[sec]
|
||||
return True
|
||||
except:
|
||||
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')
|
||||
if my_val == u"":
|
||||
my_val = def_val
|
||||
config[cfg_name][item_name] = my_val
|
||||
except:
|
||||
my_val = def_val
|
||||
try:
|
||||
config[cfg_name][item_name] = my_val
|
||||
except:
|
||||
config[cfg_name] = {}
|
||||
config[cfg_name][item_name] = my_val
|
||||
return my_val
|
||||
|
||||
|
||||
def check_setting_int(config, cfg_name, item_name, def_val):
|
||||
try:
|
||||
my_val = int(config[cfg_name][item_name])
|
||||
except:
|
||||
my_val = def_val
|
||||
try:
|
||||
config[cfg_name][item_name] = my_val
|
||||
except:
|
||||
config[cfg_name] = {}
|
||||
config[cfg_name][item_name] = my_val
|
||||
return my_val
|
||||
|
||||
CheckSection('General')
|
||||
DB_ROOT = check_setting_str(CFG, 'General', 'DB_ROOT', "")
|
||||
APP_DB_ROOT = check_setting_str(CFG, 'General', 'APP_DB_ROOT', os.getcwd())
|
||||
MAIN_DIR = check_setting_str(CFG, 'General', 'MAIN_DIR', os.getcwd())
|
||||
LOG_DIR = check_setting_str(CFG, 'General', 'LOG_DIR', os.getcwd())
|
||||
PORT = check_setting_int(CFG, 'General', 'PORT', 8083)
|
||||
NEWEST_BOOKS = check_setting_str(CFG, 'General', 'NEWEST_BOOKS', 60)
|
||||
RANDOM_BOOKS = check_setting_int(CFG, 'General', 'RANDOM_BOOKS', 4)
|
||||
|
||||
CheckSection('Advanced')
|
||||
TITLE_REGEX = check_setting_str(CFG, 'Advanced', 'TITLE_REGEX', '^(A|The|An|Der|Die|Das|Den|Ein|Eine|Einen|Dem|Des|Einem|Eines)\s+')
|
||||
DEVELOPMENT = bool(check_setting_int(CFG, 'Advanced', 'DEVELOPMENT', 0))
|
||||
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"
|
||||
|
||||
if DB_ROOT == "":
|
||||
print "Calibre database directory (DB_ROOT) is not configured"
|
||||
sys.exit(1)
|
||||
|
||||
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')
|
||||
new_config.filename = CONFIG_FILE
|
||||
new_config['General'] = {}
|
||||
new_config['General']['DB_ROOT'] = configval["DB_ROOT"]
|
||||
new_config['General']['APP_DB_ROOT'] = configval["APP_DB_ROOT"]
|
||||
new_config['General']['MAIN_DIR'] = configval["MAIN_DIR"]
|
||||
new_config['General']['LOG_DIR'] = configval["LOG_DIR"]
|
||||
new_config['General']['PORT'] = configval["PORT"]
|
||||
new_config['General']['NEWEST_BOOKS'] = configval["NEWEST_BOOKS"]
|
||||
new_config['Advanced'] = {}
|
||||
new_config['Advanced']['TITLE_REGEX'] = configval["TITLE_REGEX"]
|
||||
new_config['Advanced']['DEVELOPMENT'] = int(configval["DEVELOPMENT"])
|
||||
new_config['Advanced']['PUBLIC_REG'] = int(configval["PUBLIC_REG"])
|
||||
new_config['Advanced']['UPLOADING'] = int(configval["UPLOADING"])
|
||||
new_config['Advanced']['ANON_BROWSE'] = int(configval["ANON_BROWSE"])
|
||||
new_config.write()
|
||||
return "Saved"
|
||||
|
||||
save_config(configval)
|
130
cps/db.py
130
cps/db.py
|
@ -5,13 +5,11 @@ from sqlalchemy import *
|
|||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from sqlalchemy.orm import *
|
||||
import os
|
||||
import config
|
||||
# import config
|
||||
import re
|
||||
import ast
|
||||
|
||||
# calibre sort stuff
|
||||
title_pat = re.compile(config.TITLE_REGEX, re.IGNORECASE)
|
||||
|
||||
global session
|
||||
|
||||
def title_sort(title):
|
||||
match = title_pat.search(title)
|
||||
|
@ -21,59 +19,32 @@ def title_sort(title):
|
|||
return title.strip()
|
||||
|
||||
|
||||
dbpath = os.path.join(config.DB_ROOT, "metadata.db")
|
||||
engine = create_engine('sqlite:///{0}'.format(dbpath.encode('utf-8')), echo=False)
|
||||
conn = engine.connect()
|
||||
conn.connection.create_function('title_sort', 1, title_sort)
|
||||
Base = declarative_base()
|
||||
|
||||
books_authors_link = Table('books_authors_link', Base.metadata,
|
||||
Column('book', Integer, ForeignKey('books.id'), primary_key=True),
|
||||
Column('author', Integer, ForeignKey('authors.id'), primary_key=True)
|
||||
)
|
||||
Column('book', Integer, ForeignKey('books.id'), primary_key=True),
|
||||
Column('author', Integer, ForeignKey('authors.id'), primary_key=True)
|
||||
)
|
||||
|
||||
books_tags_link = Table('books_tags_link', Base.metadata,
|
||||
Column('book', Integer, ForeignKey('books.id'), primary_key=True),
|
||||
Column('tag', Integer, ForeignKey('tags.id'), primary_key=True)
|
||||
)
|
||||
Column('book', Integer, ForeignKey('books.id'), primary_key=True),
|
||||
Column('tag', Integer, ForeignKey('tags.id'), primary_key=True)
|
||||
)
|
||||
|
||||
books_series_link = Table('books_series_link', Base.metadata,
|
||||
Column('book', Integer, ForeignKey('books.id'), primary_key=True),
|
||||
Column('series', Integer, ForeignKey('series.id'), primary_key=True)
|
||||
)
|
||||
Column('book', Integer, ForeignKey('books.id'), primary_key=True),
|
||||
Column('series', Integer, ForeignKey('series.id'), primary_key=True)
|
||||
)
|
||||
|
||||
books_ratings_link = Table('books_ratings_link', Base.metadata,
|
||||
Column('book', Integer, ForeignKey('books.id'), primary_key=True),
|
||||
Column('rating', Integer, ForeignKey('ratings.id'), primary_key=True)
|
||||
)
|
||||
Column('book', Integer, ForeignKey('books.id'), primary_key=True),
|
||||
Column('rating', Integer, ForeignKey('ratings.id'), primary_key=True)
|
||||
)
|
||||
|
||||
books_languages_link = Table('books_languages_link', Base.metadata,
|
||||
Column('book', Integer, ForeignKey('books.id'), primary_key=True),
|
||||
Column('lang_code', Integer, ForeignKey('languages.id'), primary_key=True)
|
||||
)
|
||||
|
||||
cc = conn.execute("SELECT id, datatype FROM custom_columns")
|
||||
cc_ids = []
|
||||
cc_exceptions = ['datetime', 'int', 'comments', 'float', 'composite', 'series']
|
||||
books_custom_column_links = {}
|
||||
cc_classes = {}
|
||||
for row in cc:
|
||||
if row.datatype not in cc_exceptions:
|
||||
books_custom_column_links[row.id] = Table('books_custom_column_' + str(row.id) + '_link', Base.metadata,
|
||||
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])
|
||||
if row.datatype == 'bool':
|
||||
ccdict = {'__tablename__': 'custom_column_' + str(row.id),
|
||||
'id': Column(Integer, primary_key=True),
|
||||
'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)}
|
||||
cc_classes[row.id] = type('Custom_Column_' + str(row.id), (Base,), ccdict)
|
||||
Column('book', Integer, ForeignKey('books.id'), primary_key=True),
|
||||
Column('lang_code', Integer, ForeignKey('languages.id'), primary_key=True)
|
||||
)
|
||||
|
||||
|
||||
class Identifiers(Base):
|
||||
|
@ -260,15 +231,6 @@ class Books(Base):
|
|||
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'))
|
||||
else:
|
||||
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):
|
||||
|
@ -288,7 +250,57 @@ class Custom_Columns(Base):
|
|||
display_dict = ast.literal_eval(self.display)
|
||||
return display_dict
|
||||
|
||||
# Base.metadata.create_all(engine)
|
||||
Session = sessionmaker()
|
||||
Session.configure(bind=engine)
|
||||
session = Session()
|
||||
def setup_db(config):
|
||||
global session
|
||||
# calibre sort stuff
|
||||
title_pat = re.compile(config.config_title_regex, re.IGNORECASE)
|
||||
|
||||
if config.config_calibre_dir is None:
|
||||
return
|
||||
dbpath = os.path.join(config.config_calibre_dir, "metadata.db")
|
||||
engine = create_engine('sqlite:///{0}'.format(dbpath.encode('utf-8')), echo=False)
|
||||
conn = engine.connect()
|
||||
conn.connection.create_function('title_sort', 1, title_sort)
|
||||
|
||||
|
||||
cc = conn.execute("SELECT id, datatype FROM custom_columns")
|
||||
cc_ids = []
|
||||
cc_exceptions = ['datetime', 'int', 'comments', 'float', 'composite', 'series']
|
||||
books_custom_column_links = {}
|
||||
cc_classes = {}
|
||||
for row in cc:
|
||||
if row.datatype not in cc_exceptions:
|
||||
books_custom_column_links[row.id] = Table('books_custom_column_' + str(row.id) + '_link', Base.metadata,
|
||||
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])
|
||||
if row.datatype == 'bool':
|
||||
ccdict = {'__tablename__': 'custom_column_' + str(row.id),
|
||||
'id': Column(Integer, primary_key=True),
|
||||
'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)}
|
||||
cc_classes[row.id] = type('Custom_Column_' + str(row.id), (Base,), ccdict)
|
||||
|
||||
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'))
|
||||
else:
|
||||
setattr(Books, 'custom_column_' + str(id[0]), relationship(cc_classes[id[0]],
|
||||
secondary=books_custom_column_links[id[0]],
|
||||
backref='books'))
|
||||
|
||||
# Base.metadata.create_all(engine)
|
||||
Session = sessionmaker()
|
||||
Session.configure(bind=engine)
|
||||
session = Session()
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
import db, ub
|
||||
import config
|
||||
# import config
|
||||
from flask import current_app as app
|
||||
import logging
|
||||
import smtplib
|
||||
|
@ -33,11 +33,12 @@ def update_download(book_id, user_id):
|
|||
ub.session.commit()
|
||||
|
||||
|
||||
def make_mobi(book_id):
|
||||
def make_mobi(book_id,calibrepath):
|
||||
vendorpath = os.path.join(os.path.normpath(os.path.dirname(os.path.realpath(__file__)) + os.sep + "../vendor" + os.sep))
|
||||
if sys.platform == "win32":
|
||||
kindlegen = os.path.join(config.MAIN_DIR, "vendor", u"kindlegen.exe")
|
||||
kindlegen = os.path.join(vendorpath, u"kindlegen.exe")
|
||||
else:
|
||||
kindlegen = os.path.join(config.MAIN_DIR, "vendor", u"kindlegen")
|
||||
kindlegen = os.path.join(vendorpath, u"kindlegen")
|
||||
if not os.path.exists(kindlegen):
|
||||
app.logger.error("make_mobi: kindlegen binary not found in: %s" % kindlegen)
|
||||
return None
|
||||
|
@ -47,7 +48,7 @@ def make_mobi(book_id):
|
|||
app.logger.error("make_mobi: epub format not found for book id: %d" % book_id)
|
||||
return None
|
||||
|
||||
file_path = os.path.join(config.DB_ROOT, book.path, data.name)
|
||||
file_path = os.path.join(calibrepath, 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)
|
||||
|
@ -90,15 +91,13 @@ class StderrLogger(object):
|
|||
else:
|
||||
self.buffer=self.buffer+message
|
||||
|
||||
def send_test_mail(kindle_mail):
|
||||
def send_raw_email(kindle_mail,msg):
|
||||
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)
|
||||
use_ssl = int(settings.get('mail_use_ssl', 0))
|
||||
|
||||
# convert MIME message to string
|
||||
fp = StringIO()
|
||||
|
@ -113,16 +112,14 @@ def send_test_mail(kindle_mail):
|
|||
org_stderr = smtplib.stderr
|
||||
smtplib.stderr = StderrLogger()
|
||||
|
||||
if int(use_ssl) == 2:
|
||||
if use_ssl == 2:
|
||||
mailserver = smtplib.SMTP_SSL(settings["mail_server"], settings["mail_port"], timeout)
|
||||
else:
|
||||
mailserver = smtplib.SMTP(settings["mail_server"], settings["mail_port"], timeout)
|
||||
mailserver.set_debuglevel(1)
|
||||
|
||||
if int(use_ssl) == 1:
|
||||
#mailserver.ehlo()
|
||||
if use_ssl == 1:
|
||||
mailserver.starttls()
|
||||
#mailserver.ehlo()
|
||||
|
||||
if settings["mail_password"]:
|
||||
mailserver.login(settings["mail_login"], settings["mail_password"])
|
||||
|
@ -138,7 +135,15 @@ def send_test_mail(kindle_mail):
|
|||
return None
|
||||
|
||||
|
||||
def send_mail(book_id, kindle_mail):
|
||||
def send_test_mail(kindle_mail):
|
||||
msg = MIMEMultipart()
|
||||
msg['Subject'] = _(u'Calibre-web test email')
|
||||
text = _(u'This email has been sent via calibre web.')
|
||||
msg.attach(MIMEText(text.encode('UTF-8'), 'plain', 'UTF-8'))
|
||||
return send_raw_email(kindle_mail,msg)
|
||||
|
||||
|
||||
def send_mail(book_id, kindle_mail,calibrepath):
|
||||
"""Send email with attachments"""
|
||||
is_mobi = False
|
||||
is_azw = False
|
||||
|
@ -149,17 +154,10 @@ def send_mail(book_id, kindle_mail):
|
|||
settings = ub.get_mail_settings()
|
||||
# create MIME message
|
||||
msg = MIMEMultipart()
|
||||
msg['From'] = settings["mail_from"]
|
||||
msg['To'] = kindle_mail
|
||||
msg['Subject'] = _(u'Send to Kindle')
|
||||
text = _(u'This email has been sent via calibre web.')
|
||||
msg.attach(MIMEText(text.encode('UTF-8'), 'plain', 'UTF-8'))
|
||||
|
||||
use_ssl = settings.get('mail_use_ssl', 0)
|
||||
|
||||
# attach files
|
||||
# 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)
|
||||
|
||||
|
@ -167,11 +165,11 @@ def send_mail(book_id, kindle_mail):
|
|||
|
||||
for entry in data:
|
||||
if entry.format == "MOBI":
|
||||
formats["mobi"] = os.path.join(config.DB_ROOT, book.path, entry.name + ".mobi")
|
||||
formats["mobi"] = os.path.join(calibrepath, book.path, entry.name + ".mobi")
|
||||
if entry.format == "EPUB":
|
||||
formats["epub"] = os.path.join(config.DB_ROOT, book.path, entry.name + ".epub")
|
||||
formats["epub"] = os.path.join(calibrepath, book.path, entry.name + ".epub")
|
||||
if entry.format == "PDF":
|
||||
formats["pdf"] = os.path.join(config.DB_ROOT, book.path, entry.name + ".pdf")
|
||||
formats["pdf"] = os.path.join(calibrepath, book.path, entry.name + ".pdf")
|
||||
|
||||
if len(formats) == 0:
|
||||
return _("Could not find any formats suitable for sending by email")
|
||||
|
@ -179,7 +177,7 @@ def send_mail(book_id, kindle_mail):
|
|||
if 'mobi' in formats:
|
||||
msg.attach(get_attachment(formats['mobi']))
|
||||
elif 'epub' in formats:
|
||||
filepath = make_mobi(book.id)
|
||||
filepath = make_mobi(book.id,calibrepath)
|
||||
if filepath is not None:
|
||||
msg.attach(get_attachment(filepath))
|
||||
elif filepath is None:
|
||||
|
@ -191,40 +189,7 @@ def send_mail(book_id, kindle_mail):
|
|||
else:
|
||||
return _("Could not find any formats suitable for sending by email")
|
||||
|
||||
# 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()
|
||||
|
||||
if int(use_ssl) == 2:
|
||||
mailserver = smtplib.SMTP_SSL(settings["mail_server"], settings["mail_port"], timeout)
|
||||
else:
|
||||
mailserver = smtplib.SMTP(settings["mail_server"], settings["mail_port"], timeout)
|
||||
mailserver.set_debuglevel(1)
|
||||
|
||||
if int(use_ssl) == 1:
|
||||
mailserver.starttls()
|
||||
|
||||
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
|
||||
return send_raw_email(kindle_mail, msg)
|
||||
|
||||
|
||||
def get_attachment(file_path):
|
||||
|
@ -273,10 +238,10 @@ def get_normalized_author(value):
|
|||
return value
|
||||
|
||||
|
||||
def update_dir_stucture(book_id):
|
||||
def update_dir_stucture(book_id,calibrepath):
|
||||
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)
|
||||
path = os.path.join(calibrepath, book.path)
|
||||
|
||||
authordir = book.path.split(os.sep)[0]
|
||||
new_authordir = get_valid_filename(book.authors[0].name, False)
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -73,9 +73,66 @@
|
|||
<td>{% if config.PUBLIC_REG %}<span class="glyphicon glyphicon-ok"></span>{% else %}<span class="glyphicon glyphicon-remove"></span>{% endif %}</td>
|
||||
<td>{% if config.ANON_BROWSE %}<span class="glyphicon glyphicon-ok"></span>{% else %}<span class="glyphicon glyphicon-remove"></span>{% endif %}</td>
|
||||
</table>
|
||||
<div class="btn btn-default"><a href="{{url_for('configuration')}}">{{_('Configuration')}}</a></div>
|
||||
<h2>{{_('Administration')}}</h2>
|
||||
{% if not config.DEVELOPMENT %}
|
||||
<div class="btn btn-default"><a href="{{url_for('shutdown')}}">{{_('Restart Calibre-web')}}</a></div>
|
||||
<div class="btn btn-default" data-toggle="modal" data-target="#RestartDialog">{{_('Restart Calibre-web')}}</a></div>
|
||||
<div class="btn btn-default" data-toggle="modal" data-target="#ShutdownDialog">{{_('Stop Calibre-web')}}</a></div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<!-- Modal -->
|
||||
<div id="RestartDialog" class="modal fade" role="dialog">
|
||||
<div class="modal-dialog modal-sm">
|
||||
<!-- Modal content-->
|
||||
<div class="modal-content">
|
||||
<div class="modal-header bg-info">
|
||||
</div>
|
||||
<div class="modal-body text-center">
|
||||
<p>{{_('Do you really want to restart Calibre-web?')}}</p>
|
||||
<button type="button" class="btn btn-default" id="restart" data-dismiss="modal">{{_('Ok')}}</button>
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">{{_('Back')}}</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div id="ShutdownDialog" class="modal fade" role="dialog">
|
||||
<div class="modal-dialog modal-sm">
|
||||
<!-- Modal content-->
|
||||
<div class="modal-content">
|
||||
<div class="modal-header bg-info">
|
||||
</div>
|
||||
<div class="modal-body text-center">
|
||||
<p>{{_('Do you really want to stop Calibre-web?')}}</p>
|
||||
<button type="button" class="btn btn-default" id="shutdown" data-dismiss="modal">{{_('Ok')}}</button>
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">{{_('Back')}}</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
{% block js %}
|
||||
<script type="text/javascript">
|
||||
$("#restart").click(function() {
|
||||
$.ajax({
|
||||
dataType: 'json',
|
||||
url: "{{url_for('shutdown')}}",
|
||||
data: {"parameter":0},
|
||||
//data: data,
|
||||
success: function(data) {
|
||||
return alert(data.text);}
|
||||
});
|
||||
});
|
||||
$("#shutdown").click(function() {
|
||||
$.ajax({
|
||||
dataType: 'json',
|
||||
url: "{{url_for('shutdown')}}",
|
||||
data: {"parameter":1},
|
||||
success: function(data) {
|
||||
return alert(data.text);}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
|
55
cps/templates/config_edit.html
Normal file
55
cps/templates/config_edit.html
Normal file
|
@ -0,0 +1,55 @@
|
|||
{% extends "layout.html" %}
|
||||
{% block body %}
|
||||
<div class="discover">
|
||||
<h1>{{title}}</h1>
|
||||
<form role="form" method="POST" autocomplete="off">
|
||||
<div class="form-group required">
|
||||
<label for="config_calibre_dir">{{_('Location of Calibre database')}}</label>
|
||||
<input type="text" class="form-control" name="config_calibre_dir" id="config_calibre_dir" value="{% if content.config_calibre_dir != None %}{{ content.config_calibre_dir }}{% endif %}" autocomplete="off">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="config_port">{{_('Server Port')}}</label>
|
||||
<input type="text" class="form-control" name="config_port" id="port" value="{% if content.config_port != None %}{{ content.config_port }}{% endif %}" autocomplete="off" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="config_calibre_web_title">{{_('Title')}}</label>
|
||||
<input type="text" class="form-control" name="config_calibre_web_title" id="config_calibre_web_title" value="{% if content.config_calibre_web_title != None %}{{ content.config_calibre_web_title }}{% endif %}" autocomplete="off" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="config_books_per_page">{{_('Books per page')}}</label>
|
||||
<input type="text" class="form-control" name="config_books_per_page" id="config_books_per_page" value="{% if content.config_books_per_page != None %}{{ content.config_books_per_page }}{% endif %}" autocomplete="off">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="config_random_books">{{_('No. of random books to show')}}</label>
|
||||
<input type="text" class="form-control" name="config_random_books" id="config_random_books" value="{% if content.config_random_books != None %}{{ content.config_random_books }}{% endif %}" autocomplete="off">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="config_title_regex">{{_('Regular expression for title sorting')}}</label>
|
||||
<input type="text" class="form-control" name="config_title_regex" id="config_title_regex" value="{% if content.config_title_regex != None %}{{ content.config_title_regex }}{% endif %}" autocomplete="off">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="config_log_level">{{_('Log Level')}}</label>
|
||||
<select name="config_log_level" id="config_log_level" class="form-control">
|
||||
<option value="DEBUG" {% if content.config_log_level == 'DEBUG' %}selected{% endif %}>DEBUG</option>
|
||||
<option value="INFO" {% if content.config_log_level == 'INFO' or content.config_log_level == None %}selected{% endif %}>INFO</option>
|
||||
<option value="WARNING" {% if content.config_log_level == 'WARNING' %}selected{% endif %}>WARNING</option>
|
||||
<option value="ERROR" {% if content.config_log_level == 'ERROR' %}selected{% endif %}>ERROR</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="checkbox" id="config_uploading" name="config_uploading" {% if content.config_uploading %}checked{% endif %}>
|
||||
<label for="config_uploading">{{_('Enable uploading')}}</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="checkbox" id="config_anonbrowse" name="config_anonbrowse" {% if content.config_anonbrowse %}checked{% endif %}>
|
||||
<label for="config_anonbrowse">{{_('Enable anonymous browsing')}}</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="checkbox" id="config_public_reg" name="config_public_reg" {% if content.config_public_reg %}checked{% endif %}>
|
||||
<label for="config_public_reg">{{_('Enable public registration')}}</label>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-default">{{_('Submit')}}</button>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -191,7 +191,6 @@
|
|||
<div class="btn-toolbar" role="toolbar">
|
||||
<div class="btn-group" role="group" aria-label="Edit/Delete book">
|
||||
<a href="{{ url_for('edit_book', book_id=entry.id) }}" class="btn btn-sm btn-warning" role="button"><span class="glyphicon glyphicon-edit"></span> {{_('Edit metadata')}}</a>
|
||||
<!-- <a href="{{ url_for('edit_book', book_id=entry.id) }}" class="btn btn-sm btn-danger" role="button"><span class="glyphicon glyphicon-trash"></span> Delete</a> -->
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
|
|
|
@ -44,7 +44,6 @@
|
|||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<!-- <p><a href="{{ url_for('edit_book', book_id=entry.id) }}">{{entry.authors[0].name}}: {{entry.title}}</a></p> -->
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -36,7 +36,6 @@
|
|||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<!-- <p><a href="{{ url_for('edit_book', book_id=entry.id) }}">{{entry.authors[0].name}}: {{entry.title}}</a></p> -->
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -41,23 +41,23 @@
|
|||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="checkbox" name="show_random" {% if content.random_books %}checked{% endif %}>
|
||||
<input type="checkbox" name="show_random" id="show_random" {% if content.random_books %}checked{% endif %}>
|
||||
<label for="show_random">{{_('Show random books')}}</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="checkbox" name="show_hot" {% if content.hot_books %}checked{% endif %}>
|
||||
<input type="checkbox" name="show_hot" id="show_hot" {% if content.hot_books %}checked{% endif %}>
|
||||
<label for="show_hot">{{_('Show hot books')}}</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="checkbox" name="show_language" {% if content.language_books %}checked{% endif %}>
|
||||
<input type="checkbox" name="show_language" id="show_language" {% if content.language_books %}checked{% endif %}>
|
||||
<label for="show_language">{{_('Show language selection')}}</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="checkbox" name="show_series" {% if content.series_books %}checked{% endif %}>
|
||||
<input type="checkbox" name="show_series" id="show_series" {% if content.series_books %}checked{% endif %}>
|
||||
<label for="show_series">{{_('Show series selection')}}</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="checkbox" name="show_category" {% if content.category_books %}checked{% endif %}>
|
||||
<input type="checkbox" name="show_category" id="show_category" {% if content.category_books %}checked{% endif %}>
|
||||
<label for="show_category">{{_('Show category selection')}}</label>
|
||||
</div>
|
||||
|
||||
|
|
51
cps/ub.py
51
cps/ub.py
|
@ -7,12 +7,12 @@ from sqlalchemy.ext.declarative import declarative_base
|
|||
from sqlalchemy.orm import *
|
||||
from flask_login import AnonymousUserMixin
|
||||
import os
|
||||
import config
|
||||
# import config
|
||||
import traceback
|
||||
from werkzeug.security import generate_password_hash
|
||||
from flask_babel import gettext as _
|
||||
|
||||
dbpath = os.path.join(config.APP_DB_ROOT, "app.db")
|
||||
dbpath = os.path.join(os.path.normpath(os.path.dirname(os.path.realpath(__file__))+os.sep+".."+os.sep), "app.db")
|
||||
engine = create_engine('sqlite:///{0}'.format(dbpath), echo=False)
|
||||
Base = declarative_base()
|
||||
|
||||
|
@ -96,6 +96,23 @@ class UserBase():
|
|||
def __repr__(self):
|
||||
return '<User %r>' % self.nickname
|
||||
|
||||
class Config():
|
||||
def __init__(self):
|
||||
self.loadSettings()
|
||||
|
||||
def loadSettings(self):
|
||||
data=session.query(Settings).first()
|
||||
self.config_calibre_dir = data.config_calibre_dir
|
||||
self.config_port = data.config_port
|
||||
self.config_calibre_web_title = data.config_calibre_web_title
|
||||
self.config_books_per_page = data.config_books_per_page
|
||||
self.config_random_books = data.config_random_books
|
||||
self.config_title_regex = data.config_title_regex
|
||||
self.config_log_level = data.config_log_level
|
||||
self.config_uploading = data.config_uploading
|
||||
self.config_anonbrowse = data.config_anonbrowse
|
||||
self.config_public_reg = data.config_public_reg
|
||||
|
||||
|
||||
class User(UserBase,Base):
|
||||
__tablename__ = 'user'
|
||||
|
@ -118,11 +135,14 @@ class User(UserBase,Base):
|
|||
|
||||
|
||||
class Anonymous(AnonymousUserMixin,UserBase):
|
||||
anon_browse = None
|
||||
|
||||
def __init__(self):
|
||||
self.loadSettings()
|
||||
|
||||
def loadSettings(self):
|
||||
data=session.query(User).filter(User.role.op('&')(ROLE_ANONYMOUS) == ROLE_ANONYMOUS).first()
|
||||
settings=session.query(Settings).first()
|
||||
self.nickname = data.nickname
|
||||
self.role = data.role
|
||||
self.random_books = data.random_books
|
||||
|
@ -133,6 +153,7 @@ class Anonymous(AnonymousUserMixin,UserBase):
|
|||
self.hot_books = data.hot_books
|
||||
self.default_language = data.default_language
|
||||
self.locale = data.locale
|
||||
self.anon_browse = settings.config_anonbrowse
|
||||
|
||||
def role_admin(self):
|
||||
return False
|
||||
|
@ -141,7 +162,7 @@ class Anonymous(AnonymousUserMixin,UserBase):
|
|||
return False
|
||||
|
||||
def is_anonymous(self):
|
||||
return config.ANON_BROWSE
|
||||
return self.anon_browse
|
||||
|
||||
|
||||
class Shelf(Base):
|
||||
|
@ -187,6 +208,16 @@ class Settings(Base):
|
|||
mail_login = Column(String)
|
||||
mail_password = Column(String)
|
||||
mail_from = Column(String)
|
||||
config_calibre_dir = Column(String)
|
||||
config_port = Column(Integer, default = 8083)
|
||||
config_calibre_web_title = Column(String,default = u'Calibre-web')
|
||||
config_books_per_page = Column(Integer, default = 60)
|
||||
config_random_books = Column(Integer, default = 4)
|
||||
config_title_regex = Column(String,default=u'^(A|The|An|Der|Die|Das|Den|Ein|Eine|Einen|Dem|Des|Einem|Eines)\s+')
|
||||
config_log_level = Column(String, default=u'INFO')
|
||||
config_uploading = Column(SmallInteger, default = 0)
|
||||
config_anonbrowse = Column(SmallInteger, default = 0)
|
||||
config_public_reg = Column(SmallInteger, default = 0)
|
||||
|
||||
def __repr__(self):
|
||||
#return '<Smtp %r>' % (self.mail_server)
|
||||
|
@ -216,14 +247,22 @@ def migrate_Database():
|
|||
conn.execute("ALTER TABLE user ADD column hot_books INTEGER DEFAULT 1")
|
||||
session.commit()
|
||||
try:
|
||||
session.query(exists().where(BookShelf.order)).scalar()
|
||||
session.query(exists().where(Settings.config_calibre_dir)).scalar()
|
||||
session.commit()
|
||||
except exc.OperationalError: # Database is not compatible, some rows are missing
|
||||
conn = engine.connect()
|
||||
conn.execute("ALTER TABLE book_shelf_link ADD column `order` INTEGER DEFAULT 1")
|
||||
conn.execute("ALTER TABLE Settings ADD column `config_calibre_dir` String")
|
||||
conn.execute("ALTER TABLE Settings ADD column `config_port` INTEGER DEFAULT 8083")
|
||||
conn.execute("ALTER TABLE Settings ADD column `config_calibre_web_title` String DEFAULT 'Calibre-web'")
|
||||
conn.execute("ALTER TABLE Settings ADD column `config_books_per_page` INTEGER DEFAULT 60")
|
||||
conn.execute("ALTER TABLE Settings ADD column `config_random_books` INTEGER DEFAULT 4")
|
||||
conn.execute("ALTER TABLE Settings ADD column `config_title_regex` String DEFAULT '^(A|The|An|Der|Die|Das|Den|Ein|Eine|Einen|Dem|Des|Einem|Eines)\s+'")
|
||||
conn.execute("ALTER TABLE Settings ADD column `config_log_level` String DEFAULT 'INFO'")
|
||||
conn.execute("ALTER TABLE Settings ADD column `config_uploading` SmallInteger DEFAULT 0")
|
||||
conn.execute("ALTER TABLE Settings ADD column `config_anonbrowse` SmallInteger DEFAULT 0")
|
||||
conn.execute("ALTER TABLE Settings ADD column `config_public_reg` SmallInteger DEFAULT 0")
|
||||
session.commit()
|
||||
|
||||
|
||||
def create_default_config():
|
||||
settings = Settings()
|
||||
settings.mail_server = "mail.example.com"
|
||||
|
|
223
cps/web.py
223
cps/web.py
|
@ -7,7 +7,7 @@ from logging.handlers import RotatingFileHandler
|
|||
import textwrap
|
||||
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 ub, helper
|
||||
import os
|
||||
import errno
|
||||
from sqlalchemy.sql.expression import func
|
||||
|
@ -33,32 +33,24 @@ from uuid import uuid4
|
|||
import os.path
|
||||
import sys
|
||||
import subprocess
|
||||
import shutil
|
||||
import re
|
||||
from shutil import move
|
||||
import db
|
||||
from shutil import move, copyfile
|
||||
from tornado.ioloop import IOLoop
|
||||
|
||||
try:
|
||||
from wand.image import Image
|
||||
|
||||
use_generic_pdf_cover = False
|
||||
except ImportError, e:
|
||||
use_generic_pdf_cover = True
|
||||
|
||||
from shutil import copyfile
|
||||
from cgi import escape
|
||||
|
||||
mimetypes.init()
|
||||
mimetypes.add_type('application/xhtml+xml', '.xhtml')
|
||||
mimetypes.add_type('application/epub+zip', '.epub')
|
||||
mimetypes.add_type('application/x-mobipocket-ebook', '.mobi')
|
||||
mimetypes.add_type('application/x-mobipocket-ebook', '.prc')
|
||||
mimetypes.add_type('application/vnd.amazon.ebook', '.azw')
|
||||
mimetypes.add_type('application/x-cbr', '.cbr')
|
||||
mimetypes.add_type('application/x-cbz', '.cbz')
|
||||
mimetypes.add_type('application/x-cbt', '.cbt')
|
||||
mimetypes.add_type('image/vnd.djvu', '.djvu')
|
||||
########################################## Global variables ########################################################
|
||||
global global_task
|
||||
global_task = None
|
||||
|
||||
|
||||
########################################## Proxy Helper class ######################################################
|
||||
class ReverseProxied(object):
|
||||
"""Wrap the application in this middleware and configure the
|
||||
front-end server to add these headers, to let you quietly bind
|
||||
|
@ -96,11 +88,23 @@ class ReverseProxied(object):
|
|||
environ['HTTP_HOST'] = server
|
||||
return self.app(environ, start_response)
|
||||
|
||||
########################################## Main code ##############################################################
|
||||
mimetypes.init()
|
||||
mimetypes.add_type('application/xhtml+xml', '.xhtml')
|
||||
mimetypes.add_type('application/epub+zip', '.epub')
|
||||
mimetypes.add_type('application/x-mobipocket-ebook', '.mobi')
|
||||
mimetypes.add_type('application/x-mobipocket-ebook', '.prc')
|
||||
mimetypes.add_type('application/vnd.amazon.ebook', '.azw')
|
||||
mimetypes.add_type('application/x-cbr', '.cbr')
|
||||
mimetypes.add_type('application/x-cbz', '.cbz')
|
||||
mimetypes.add_type('application/x-cbt', '.cbt')
|
||||
mimetypes.add_type('image/vnd.djvu', '.djvu')
|
||||
|
||||
|
||||
app = (Flask(__name__))
|
||||
app.wsgi_app = ReverseProxied(app.wsgi_app)
|
||||
|
||||
formatter = logging.Formatter(
|
||||
'''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=50000, backupCount=1)
|
||||
file_handler.setFormatter(formatter)
|
||||
|
@ -112,7 +116,7 @@ else:
|
|||
|
||||
app.logger.info('Starting Calibre Web...')
|
||||
logging.getLogger("book_formats").addHandler(file_handler)
|
||||
logging.getLogger("book_formats").setLevel(logging.INFO)
|
||||
logging.getLogger("book_formats").setLevel(logging.INFO)'''
|
||||
|
||||
Principal(app)
|
||||
|
||||
|
@ -120,10 +124,6 @@ babel = Babel(app)
|
|||
|
||||
import uploader
|
||||
|
||||
global global_queue
|
||||
global_queue = None
|
||||
|
||||
|
||||
lm = LoginManager(app)
|
||||
lm.init_app(app)
|
||||
lm.login_view = 'login'
|
||||
|
@ -131,6 +131,10 @@ lm.anonymous_user = ub.Anonymous
|
|||
|
||||
app.secret_key = 'A0Zr98j/3yX R~XHH!jmN]LWX/,?RT'
|
||||
|
||||
# establish connection to calibre-db
|
||||
config=ub.Config()
|
||||
db.setup_db(config)
|
||||
|
||||
|
||||
@babel.localeselector
|
||||
def get_locale():
|
||||
|
@ -190,7 +194,7 @@ def requires_basic_auth_if_no_ano(f):
|
|||
@wraps(f)
|
||||
def decorated(*args, **kwargs):
|
||||
auth = request.authorization
|
||||
if config.ANON_BROWSE != 1:
|
||||
if config.config_anonbrowse != 1:
|
||||
if not auth or not check_auth(auth.username, auth.password):
|
||||
return authenticate()
|
||||
return f(*args, **kwargs)
|
||||
|
@ -257,7 +261,7 @@ app.jinja_env.globals['url_for_other_page'] = url_for_other_page
|
|||
|
||||
|
||||
def login_required_if_no_ano(func):
|
||||
if config.ANON_BROWSE == 1:
|
||||
if config.config_anonbrowse == 1:
|
||||
return func
|
||||
return login_required(func)
|
||||
|
||||
|
@ -284,7 +288,6 @@ def admin_required(f):
|
|||
"""
|
||||
Checks if current_user.role == 1
|
||||
"""
|
||||
|
||||
@wraps(f)
|
||||
def inner(*args, **kwargs):
|
||||
if current_user.role_admin():
|
||||
|
@ -293,6 +296,18 @@ def admin_required(f):
|
|||
|
||||
return inner
|
||||
|
||||
def unconfigured(f):
|
||||
"""
|
||||
Checks if current_user.role == 1
|
||||
"""
|
||||
@wraps(f)
|
||||
def inner(*args, **kwargs):
|
||||
if not config.config_calibre_dir:
|
||||
return f(*args, **kwargs)
|
||||
abort(403)
|
||||
|
||||
return inner
|
||||
|
||||
|
||||
def download_required(f):
|
||||
@wraps(f)
|
||||
|
@ -331,14 +346,14 @@ def fill_indexpage(page, database, db_filter, order):
|
|||
else:
|
||||
filter = True
|
||||
if current_user.show_random_books():
|
||||
random = db.session.query(db.Books).filter(filter).order_by(func.random()).limit(config.RANDOM_BOOKS)
|
||||
random = db.session.query(db.Books).filter(filter).order_by(func.random()).limit(config.config_random_books)
|
||||
else:
|
||||
random = false
|
||||
off = int(int(config.NEWEST_BOOKS) * (page - 1))
|
||||
pagination = Pagination(page, config.NEWEST_BOOKS,
|
||||
off = int(int(config.config_books_per_page) * (page - 1))
|
||||
pagination = Pagination(page, config.config_books_per_page,
|
||||
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)
|
||||
config.config_books_per_page)
|
||||
return entries, random, pagination
|
||||
|
||||
|
||||
|
@ -400,9 +415,12 @@ def modify_database_object(input_elements, db_book_object, db_object, db_session
|
|||
def before_request():
|
||||
g.user = current_user
|
||||
g.public_shelfes = ub.session.query(ub.Shelf).filter(ub.Shelf.is_public == 1).all()
|
||||
g.allow_registration = config.PUBLIC_REG
|
||||
g.allow_upload = config.UPLOADING
|
||||
g.allow_registration = config.config_public_reg
|
||||
g.allow_upload = config.config_uploading
|
||||
|
||||
'''#################################################################################################################
|
||||
########################################## Routing functions #######################################################
|
||||
#################################################################################################################'''
|
||||
|
||||
@app.route("/opds")
|
||||
@requires_basic_auth_if_no_ano
|
||||
|
@ -467,8 +485,8 @@ def feed_new():
|
|||
if not off:
|
||||
off = 0
|
||||
entries = db.session.query(db.Books).filter(filter).order_by(db.Books.timestamp.desc()).offset(off).limit(
|
||||
config.NEWEST_BOOKS)
|
||||
pagination = Pagination((int(off)/(int(config.NEWEST_BOOKS))+1), config.NEWEST_BOOKS,
|
||||
config.config_books_per_page)
|
||||
pagination = Pagination((int(off)/(int(config.config_books_per_page))+1), config.config_books_per_page,
|
||||
len(db.session.query(db.Books).filter(filter).all()))
|
||||
xml = render_template('feed.xml', entries=entries, pagination=pagination)
|
||||
response = make_response(xml)
|
||||
|
@ -486,8 +504,8 @@ def feed_discover():
|
|||
filter = True
|
||||
# if not off:
|
||||
# off = 0
|
||||
entries = db.session.query(db.Books).filter(filter).order_by(func.random()).limit(config.NEWEST_BOOKS)
|
||||
pagination = Pagination(1, config.NEWEST_BOOKS,int(config.NEWEST_BOOKS))
|
||||
entries = db.session.query(db.Books).filter(filter).order_by(func.random()).limit(config.config_books_per_page)
|
||||
pagination = Pagination(1, config.config_books_per_page,int(config.config_books_per_page))
|
||||
xml = render_template('feed.xml', entries=entries, pagination=pagination)
|
||||
response = make_response(xml)
|
||||
response.headers["Content-Type"] = "application/xml"
|
||||
|
@ -505,8 +523,8 @@ def feed_hot():
|
|||
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)
|
||||
pagination = Pagination((int(off)/(int(config.NEWEST_BOOKS))+1), config.NEWEST_BOOKS,
|
||||
off).limit(config.config_books_per_page)
|
||||
pagination = Pagination((int(off)/(int(config.config_books_per_page))+1), config.config_books_per_page,
|
||||
len(db.session.query(db.Books).filter(filter).filter(db.Books.ratings.any(db.Ratings.rating > 9)).all()))
|
||||
xml = render_template('feed.xml', entries=entries, pagination=pagination)
|
||||
response = make_response(xml)
|
||||
|
@ -525,8 +543,8 @@ def feed_authorindex():
|
|||
filter = True
|
||||
if not off:
|
||||
off = 0
|
||||
authors = db.session.query(db.Authors).order_by(db.Authors.sort).offset(off).limit(config.NEWEST_BOOKS)
|
||||
pagination = Pagination((int(off)/(int(config.NEWEST_BOOKS))+1), config.NEWEST_BOOKS,
|
||||
authors = db.session.query(db.Authors).order_by(db.Authors.sort).offset(off).limit(config.config_books_per_page)
|
||||
pagination = Pagination((int(off)/(int(config.config_books_per_page))+1), config.config_books_per_page,
|
||||
len(db.session.query(db.Authors).all()))
|
||||
xml = render_template('feed.xml', authors=authors, pagination=pagination)
|
||||
response = make_response(xml)
|
||||
|
@ -545,8 +563,8 @@ def feed_author(id):
|
|||
if not off:
|
||||
off = 0
|
||||
entries = db.session.query(db.Books).filter(db.Books.authors.any(db.Authors.id == id )).filter(
|
||||
filter).offset(off).limit(config.NEWEST_BOOKS)
|
||||
pagination = Pagination((int(off)/(int(config.NEWEST_BOOKS))+1), config.NEWEST_BOOKS,
|
||||
filter).offset(off).limit(config.config_books_per_page)
|
||||
pagination = Pagination((int(off)/(int(config.config_books_per_page))+1), config.config_books_per_page,
|
||||
len(db.session.query(db.Books).filter(db.Books.authors.any(db.Authors.id == id )).filter(filter).all()))
|
||||
xml = render_template('feed.xml', entries=entries, pagination=pagination)
|
||||
response = make_response(xml)
|
||||
|
@ -560,8 +578,8 @@ def feed_categoryindex():
|
|||
off = request.args.get("offset")
|
||||
if not off:
|
||||
off = 0
|
||||
entries = db.session.query(db.Tags).order_by(db.Tags.name).offset(off).limit(config.NEWEST_BOOKS)
|
||||
pagination = Pagination((int(off)/(int(config.NEWEST_BOOKS))+1), config.NEWEST_BOOKS,
|
||||
entries = db.session.query(db.Tags).order_by(db.Tags.name).offset(off).limit(config.config_books_per_page)
|
||||
pagination = Pagination((int(off)/(int(config.config_books_per_page))+1), config.config_books_per_page,
|
||||
len(db.session.query(db.Tags).all()))
|
||||
xml = render_template('feed.xml', categorys=entries, pagination=pagination)
|
||||
response = make_response(xml)
|
||||
|
@ -580,8 +598,8 @@ def feed_category(id):
|
|||
if not off:
|
||||
off = 0
|
||||
entries = db.session.query(db.Books).filter(db.Books.tags.any(db.Tags.id==id)).order_by(
|
||||
db.Books.timestamp.desc()).filter(filter).offset(off).limit(config.NEWEST_BOOKS)
|
||||
pagination = Pagination((int(off)/(int(config.NEWEST_BOOKS))+1), config.NEWEST_BOOKS,
|
||||
db.Books.timestamp.desc()).filter(filter).offset(off).limit(config.config_books_per_page)
|
||||
pagination = Pagination((int(off)/(int(config.config_books_per_page))+1), config.config_books_per_page,
|
||||
len(db.session.query(db.Books).filter(db.Books.tags.any(db.Tags.id==id)).filter(filter).all()))
|
||||
xml = render_template('feed.xml', entries=entries, pagination=pagination)
|
||||
response = make_response(xml)
|
||||
|
@ -599,8 +617,8 @@ def feed_seriesindex():
|
|||
filter = True
|
||||
if not off:
|
||||
off = 0
|
||||
entries = db.session.query(db.Series).order_by(db.Series.name).offset(off).limit(config.NEWEST_BOOKS)
|
||||
pagination = Pagination((int(off)/(int(config.NEWEST_BOOKS))+1), config.NEWEST_BOOKS,
|
||||
entries = db.session.query(db.Series).order_by(db.Series.name).offset(off).limit(config.config_books_per_page)
|
||||
pagination = Pagination((int(off)/(int(config.config_books_per_page))+1), config.config_books_per_page,
|
||||
len(db.session.query(db.Series).all()))
|
||||
xml = render_template('feed.xml', series=entries, pagination=pagination)
|
||||
response = make_response(xml)
|
||||
|
@ -619,8 +637,8 @@ def feed_series(id):
|
|||
if not off:
|
||||
off = 0
|
||||
entries = db.session.query(db.Books).filter(db.Books.series.any(db.Series.id == id)).order_by(
|
||||
db.Books.timestamp.desc()).filter(filter).offset(off).limit(config.NEWEST_BOOKS)
|
||||
pagination = Pagination((int(off)/(int(config.NEWEST_BOOKS))+1), config.NEWEST_BOOKS,
|
||||
db.Books.timestamp.desc()).filter(filter).offset(off).limit(config.config_books_per_page)
|
||||
pagination = Pagination((int(off)/(int(config.config_books_per_page))+1), config.config_books_per_page,
|
||||
len(db.session.query(db.Books).filter(db.Books.series.any(db.Series.id == id)).filter(filter).all()))
|
||||
xml = render_template('feed.xml', entries=entries, pagination=pagination)
|
||||
response = make_response(xml)
|
||||
|
@ -754,15 +772,15 @@ def hot_books(page):
|
|||
random = db.session.query(db.Books).filter(filter).order_by(func.random()).limit(config.RANDOM_BOOKS)
|
||||
else:
|
||||
random = false
|
||||
off = int(int(config.NEWEST_BOOKS) * (page - 1))
|
||||
off = int(int(config.config_books_per_page) * (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)
|
||||
hot_books = all_books.offset(off).limit(config.NEWEST_BOOKS)
|
||||
hot_books = all_books.offset(off).limit(config.config_books_per_page)
|
||||
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)
|
||||
pagination = Pagination(page, config.config_books_per_page, numBooks)
|
||||
return render_template('index.html', random=random, entries=entries, pagination=pagination,
|
||||
title=_(u"Hot Books (most downloaded)"))
|
||||
|
||||
|
@ -958,13 +976,27 @@ def stats():
|
|||
|
||||
@app.route("/shutdown")
|
||||
@login_required
|
||||
@admin_required
|
||||
def shutdown():
|
||||
# logout_user()
|
||||
# add restart command to queue
|
||||
global_queue.put("something")
|
||||
flash(_(u"Server restarts"), category="info")
|
||||
return redirect(url_for("index"))
|
||||
|
||||
global global_task
|
||||
task = int(request.args.get("parameter").strip())
|
||||
global_task = task
|
||||
if task == 1 or task == 0: # valid commandos received
|
||||
# close all database connections
|
||||
db.session.close()
|
||||
db.engine.dispose()
|
||||
ub.session.close()
|
||||
ub.engine.dispose()
|
||||
# stop tornado server
|
||||
server=IOLoop.instance()
|
||||
server.add_callback(server.stop)
|
||||
if task == 0:
|
||||
text['text']=_(u'Performing Restart, please reload page')
|
||||
else:
|
||||
text['text']= _(u'Performing shutdown of server, please close window')
|
||||
return json.dumps(text)
|
||||
else:
|
||||
abort(404)
|
||||
|
||||
@app.route("/search", methods=["GET"])
|
||||
@login_required_if_no_ano
|
||||
|
@ -1200,6 +1232,9 @@ def register():
|
|||
def login():
|
||||
error = None
|
||||
|
||||
if config.config_calibre_dir == None:
|
||||
return redirect(url_for('basic_configuration'))
|
||||
|
||||
if current_user is not None and current_user.is_authenticated:
|
||||
return redirect(url_for('index'))
|
||||
|
||||
|
@ -1234,7 +1269,7 @@ def send_to_kindle(book_id):
|
|||
if settings.get("mail_server", "mail.example.com") == "mail.example.com":
|
||||
flash(_(u"Please configure the SMTP mail settings first..."), category="error")
|
||||
elif current_user.kindle_mail:
|
||||
result = helper.send_mail(book_id, current_user.kindle_mail)
|
||||
result = helper.send_mail(book_id, current_user.kindle_mail,config.config_calibre_dir)
|
||||
if result is None:
|
||||
flash(_(u"Book successfully send to %(kindlemail)s", kindlemail=current_user.kindle_mail),
|
||||
category="success")
|
||||
|
@ -1357,7 +1392,7 @@ def delete_shelf(shelf_id):
|
|||
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(_(u"successfully deleted shelf %(name)s", name=cur_shelf.name, category="success"))
|
||||
return redirect(url_for('index'))
|
||||
|
||||
|
||||
|
@ -1486,9 +1521,56 @@ def admin():
|
|||
@login_required
|
||||
@admin_required
|
||||
def configuration():
|
||||
content = ub.session.query(ub.User).all()
|
||||
settings = ub.session.query(ub.Settings).first()
|
||||
return render_template("admin.html", content=content, email=settings, config=config, title=_(u"Admin page"))
|
||||
return render_template("config_edit.html", content=config, title=_(u"Basic Configuration"))
|
||||
|
||||
@app.route("/config", methods=["GET", "POST"] )
|
||||
@unconfigured
|
||||
def basic_configuration():
|
||||
global global_task
|
||||
if request.method == "POST":
|
||||
to_save = request.form.to_dict()
|
||||
content = ub.session.query(ub.Settings).first()
|
||||
if "config_calibre_dir" in to_save:
|
||||
content.config_calibre_dir = to_save["config_calibre_dir"]
|
||||
if "config_port" in to_save:
|
||||
content.config_port = to_save["config_port"]
|
||||
if "config_calibre_web_title" in to_save:
|
||||
content.config_calibre_web_title = to_save["config_calibre_web_title"]
|
||||
if "config_calibre_web_title" in to_save:
|
||||
content.config_calibre_web_title = to_save["config_calibre_web_title"]
|
||||
if "config_title_regex" in to_save:
|
||||
content.config_title_regex = to_save["config_title_regex"]
|
||||
if "config_log_level" in to_save:
|
||||
content.config_log_level = to_save["config_log_level"]
|
||||
if "config_random_books" in to_save:
|
||||
content.config_random_books = int(to_save["config_random_books"])
|
||||
if "config_books_per_page" in to_save:
|
||||
content.config_books_per_page = int(to_save["config_books_per_page"])
|
||||
content.config_uploading = 0
|
||||
content.config_anonbrowse = 0
|
||||
content.config_public_reg = 0
|
||||
if "config_uploading" in to_save and to_save["config_uploading"] == "on":
|
||||
content.config_uploading = 1
|
||||
if "config_anonbrowse" in to_save and to_save["config_anonbrowse"] == "on":
|
||||
content.config_anonbrowse = 1
|
||||
if "config_public_reg" in to_save and to_save["config_public_reg"] == "on":
|
||||
content.config_public_reg = 1
|
||||
try:
|
||||
ub.session.commit()
|
||||
flash(_(u"Calibre-web configuration updated"), category="success")
|
||||
config.loadSettings()
|
||||
except e:
|
||||
flash(e, category="error")
|
||||
return render_template("config_edit.html", content=config, title=_(u"Basic Configuration"))
|
||||
|
||||
ub.session.close()
|
||||
ub.engine.dispose()
|
||||
# stop tornado server
|
||||
server = IOLoop.instance()
|
||||
server.add_callback(server.stop)
|
||||
global_task = 0
|
||||
|
||||
return render_template("config_edit.html", content=config, title=_(u"Basic Configuration"))
|
||||
|
||||
|
||||
@app.route("/admin/user/new", methods=["GET", "POST"])
|
||||
|
@ -1513,6 +1595,7 @@ def new_user():
|
|||
content.nickname = to_save["nickname"]
|
||||
content.email = to_save["email"]
|
||||
content.default_language = to_save["default_language"]
|
||||
if "locale" in to_save:
|
||||
content.locale = to_save["locale"]
|
||||
content.random_books = 0
|
||||
content.language_books = 0
|
||||
|
@ -1541,13 +1624,13 @@ def new_user():
|
|||
try:
|
||||
ub.session.add(content)
|
||||
ub.session.commit()
|
||||
flash(_("User '%(user)s' created", user=content.nickname), category="success")
|
||||
flash(_(u"User '%(user)s' created", user=content.nickname), category="success")
|
||||
return redirect(url_for('admin'))
|
||||
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"))
|
||||
languages=languages, title=_(u"Add new user"))
|
||||
|
||||
|
||||
@app.route("/admin/mailsettings", methods=["GET", "POST"])
|
||||
|
@ -1575,7 +1658,7 @@ def edit_mailsettings():
|
|||
category="success")
|
||||
else:
|
||||
flash(_(u"There was an error sending the Test E-Mail: %(res)s", res=result), category="error")
|
||||
return render_template("email_edit.html", content=content, title=_("Edit mail settings"))
|
||||
return render_template("email_edit.html", content=content, title=_(u"Edit mail settings"))
|
||||
|
||||
|
||||
@app.route("/admin/user/<int:user_id>", methods=["GET", "POST"])
|
||||
|
@ -1863,13 +1946,13 @@ def edit_book(book_id):
|
|||
for author in book.authors:
|
||||
author_names.append(author.name)
|
||||
for b in edited_books_id:
|
||||
helper.update_dir_stucture(b)
|
||||
helper.update_dir_stucture(b,config.config_calibre_dir)
|
||||
if "detail_view" in to_save:
|
||||
return redirect(url_for('show_book', id=book.id))
|
||||
else:
|
||||
return render_template('edit_book.html', book=book, authors=author_names, cc=cc)
|
||||
return render_template('book_edit.html', book=book, authors=author_names, cc=cc)
|
||||
else:
|
||||
return render_template('edit_book.html', book=book, authors=author_names, cc=cc)
|
||||
return render_template('book_edit.html', book=book, authors=author_names, cc=cc)
|
||||
else:
|
||||
flash(_(u"Error opening eBook. File does not exist or file is not accessible:"), category="error")
|
||||
return redirect(url_for("index"))
|
||||
|
@ -1942,6 +2025,6 @@ def upload():
|
|||
author_names.append(author.name)
|
||||
cc = db.session.query(db.Custom_Columns).filter(db.Custom_Columns.datatype.notin_(db.cc_exceptions)).all()
|
||||
if current_user.role_edit() or current_user.role_admin():
|
||||
return render_template('edit_book.html', book=db_book, authors=author_names, cc=cc)
|
||||
return render_template('book_edit.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)
|
||||
|
|
Loading…
Reference in New Issue
Block a user