Started migration of config to app database

pull/102/head
OzzieIsaacs 8 years ago
parent a2a48515d4
commit 4eee58c21c

@ -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:
if __name__ == '__main__':
'''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(config.PORT)
http_server.listen(web.config.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)
if web.global_task == 0:
print("Performing restart of Calibre-web")
os.execl(sys.executable,sys.executable, *sys.argv)
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()
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)

@ -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,10 +19,6 @@ 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,
@ -52,29 +46,6 @@ books_languages_link = Table('books_languages_link', Base.metadata,
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)
class Identifiers(Base):
__tablename__ = 'identifiers'
@ -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 %}

@ -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>

@ -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"

@ -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…
Cancel
Save