Commit 9e159ed5 authored by Ozzieisaacs's avatar Ozzieisaacs

Merge branch 'master' into Develop

# Conflicts:
#	cps/config_sql.py
#	cps/ub.py
#	cps/web.py
parents 3e1c34ef 2c429722
...@@ -17,6 +17,9 @@ Steps to reproduce the behavior: ...@@ -17,6 +17,9 @@ Steps to reproduce the behavior:
3. Scroll down to '....' 3. Scroll down to '....'
4. See error 4. See error
**Logfile**
Add content of calibre-web.log file or the relevant error, try to reproduce your problem with "debug" log-level to get more output.
**Expected behavior** **Expected behavior**
A clear and concise description of what you expected to happen. A clear and concise description of what you expected to happen.
......
...@@ -23,12 +23,12 @@ Calibre-Web is a web app providing a clean interface for browsing, reading and d ...@@ -23,12 +23,12 @@ Calibre-Web is a web app providing a clean interface for browsing, reading and d
- Send eBooks to Kindle devices with the click of a button - Send eBooks to Kindle devices with the click of a button
- Sync your Kobo devices through Calibre-Web with your Calibre library - Sync your Kobo devices through Calibre-Web with your Calibre library
- Support for reading eBooks directly in the browser (.txt, .epub, .pdf, .cbr, .cbt, .cbz) - Support for reading eBooks directly in the browser (.txt, .epub, .pdf, .cbr, .cbt, .cbz)
- Upload new books in many formats - Upload new books in many formats, including audio formats (.mp3, .m4a, .m4b)
- Support for Calibre custom columns - Support for Calibre Custom Columns
- Ability to hide content based on categories and Custom Column content per user - Ability to hide content based on categories and Custom Column content per user
- Self-update capability - Self-update capability
- "Magic Link" login to make it easy to log on eReaders - "Magic Link" login to make it easy to log on eReaders
- Login via google/github oauth and via proxy authentication - Login via LDAP, google/github oauth and via proxy authentication
## Quick start ## Quick start
......
...@@ -43,6 +43,11 @@ try: ...@@ -43,6 +43,11 @@ try:
except ImportError: except ImportError:
unidecode_version = _(u'not installed') unidecode_version = _(u'not installed')
try:
from flask_dance import __version__ as flask_danceVersion
except ImportError:
flask_danceVersion = None
from . import services from . import services
about = flask.Blueprint('about', __name__) about = flask.Blueprint('about', __name__)
...@@ -68,9 +73,11 @@ _VERSIONS = OrderedDict( ...@@ -68,9 +73,11 @@ _VERSIONS = OrderedDict(
iso639=isoLanguages.__version__, iso639=isoLanguages.__version__,
pytz=pytz.__version__, pytz=pytz.__version__,
Unidecode = unidecode_version, Unidecode = unidecode_version,
Flask_SimpleLDAP = u'installed' if bool(services.ldap) else u'not installed', Flask_SimpleLDAP = u'installed' if bool(services.ldap) else None,
Goodreads = u'installed' if bool(services.goodreads_support) else u'not installed', python_LDAP = services.ldapVersion if bool(services.ldapVersion) else None,
jsonschema = services.SyncToken.__version__ if bool(services.SyncToken) else u'not installed', Goodreads = u'installed' if bool(services.goodreads_support) else None,
jsonschema = services.SyncToken.__version__ if bool(services.SyncToken) else None,
flask_dance = flask_danceVersion
) )
_VERSIONS.update(uploader.get_versions()) _VERSIONS.update(uploader.get_versions())
......
This diff is collapsed.
...@@ -71,7 +71,7 @@ class _Settings(_Base): ...@@ -71,7 +71,7 @@ class _Settings(_Base):
config_kobo_sync = Column(Boolean, default=False) config_kobo_sync = Column(Boolean, default=False)
config_default_role = Column(SmallInteger, default=0) config_default_role = Column(SmallInteger, default=0)
config_default_show = Column(SmallInteger, default=38911) config_default_show = Column(SmallInteger, default=constants.ADMIN_USER_SIDEBAR)
config_columns_to_ignore = Column(String) config_columns_to_ignore = Column(String)
config_denied_tags = Column(String, default="") config_denied_tags = Column(String, default="")
...@@ -93,18 +93,21 @@ class _Settings(_Base): ...@@ -93,18 +93,21 @@ class _Settings(_Base):
config_kobo_proxy = Column(Boolean, default=False) config_kobo_proxy = Column(Boolean, default=False)
config_ldap_provider_url = Column(String, default='localhost') config_ldap_provider_url = Column(String, default='example.org')
config_ldap_port = Column(SmallInteger, default=389) config_ldap_port = Column(SmallInteger, default=389)
config_ldap_schema = Column(String, default='ldap') # config_ldap_schema = Column(String, default='ldap')
config_ldap_serv_username = Column(String) config_ldap_serv_username = Column(String, default='cn=admin,dc=example,dc=org')
config_ldap_serv_password = Column(String) config_ldap_serv_password = Column(String, default="")
config_ldap_use_ssl = Column(Boolean, default=False) config_ldap_encryption = Column(SmallInteger, default=0)
config_ldap_use_tls = Column(Boolean, default=False) # config_ldap_use_tls = Column(Boolean, default=False)
config_ldap_require_cert = Column(Boolean, default=False) # config_ldap_require_cert = Column(Boolean, default=False)
config_ldap_cert_path = Column(String) config_ldap_cert_path = Column(String, default="")
config_ldap_dn = Column(String) config_ldap_dn = Column(String, default='dc=example,dc=org')
config_ldap_user_object = Column(String) config_ldap_user_object = Column(String, default='uid=%s')
config_ldap_openldap = Column(Boolean, default=False) config_ldap_openldap = Column(Boolean, default=True)
config_ldap_group_object_filter = Column(String, default='(&(objectclass=posixGroup)(cn=%s))')
config_ldap_group_members_field = Column(String, default='memberUid')
config_ldap_group_name = Column(String, default='calibreweb')
config_ebookconverter = Column(Integer, default=0) config_ebookconverter = Column(Integer, default=0)
config_converterpath = Column(String) config_converterpath = Column(String)
...@@ -212,7 +215,7 @@ class _ConfigSQL(object): ...@@ -212,7 +215,7 @@ class _ConfigSQL(object):
return not bool(self.mail_server == constants.DEFAULT_MAIL_SERVER) return not bool(self.mail_server == constants.DEFAULT_MAIL_SERVER)
def set_from_dictionary(self, dictionary, field, convertor=None, default=None): def set_from_dictionary(self, dictionary, field, convertor=None, default=None, encode=None):
'''Possibly updates a field of this object. '''Possibly updates a field of this object.
The new value, if present, is grabbed from the given dictionary, and optionally passed through a convertor. The new value, if present, is grabbed from the given dictionary, and optionally passed through a convertor.
...@@ -228,6 +231,9 @@ class _ConfigSQL(object): ...@@ -228,6 +231,9 @@ class _ConfigSQL(object):
return False return False
if convertor is not None: if convertor is not None:
if encode:
new_value = convertor(new_value.encode(encode))
else:
new_value = convertor(new_value) new_value = convertor(new_value)
current_value = self.__dict__.get(field) current_value = self.__dict__.get(field)
...@@ -277,7 +283,9 @@ class _ConfigSQL(object): ...@@ -277,7 +283,9 @@ class _ConfigSQL(object):
self._session.commit() self._session.commit()
self.load() self.load()
def invalidate(self): def invalidate(self, error=None):
if error:
log.error(error)
log.warning("invalidating configuration") log.warning("invalidating configuration")
self.db_configured = False self.db_configured = False
self.config_calibre_dir = None self.config_calibre_dir = None
......
...@@ -344,14 +344,13 @@ def setup_db(config): ...@@ -344,14 +344,13 @@ def setup_db(config):
isolation_level="SERIALIZABLE", isolation_level="SERIALIZABLE",
connect_args={'check_same_thread': False}) connect_args={'check_same_thread': False})
conn = engine.connect() conn = engine.connect()
except: # conn.text_factory = lambda b: b.decode(errors = 'ignore') possible fix for #1302
config.invalidate() except Exception as e:
config.invalidate(e)
return False return False
config.db_configured = True config.db_configured = True
update_title_sort(config, conn.connection) update_title_sort(config, conn.connection)
# conn.connection.create_function('lower', 1, lcase)
# conn.connection.create_function('upper', 1, ucase)
if not cc_classes: if not cc_classes:
cc = conn.execute("SELECT id, datatype FROM custom_columns") cc = conn.execute("SELECT id, datatype FROM custom_columns")
......
...@@ -796,6 +796,7 @@ def fill_indexpage_with_archived_books(page, database, db_filter, order, allow_s ...@@ -796,6 +796,7 @@ def fill_indexpage_with_archived_books(page, database, db_filter, order, allow_s
def get_typeahead(database, query, replace=('',''), tag_filter=true()): def get_typeahead(database, query, replace=('',''), tag_filter=true()):
query = query or ''
db.session.connection().connection.connection.create_function("lower", 1, lcase) db.session.connection().connection.connection.create_function("lower", 1, lcase)
entries = db.session.query(database).filter(tag_filter).filter(func.lower(database.name).ilike("%" + query + "%")).all() entries = db.session.query(database).filter(tag_filter).filter(func.lower(database.name).ilike("%" + query + "%")).all()
json_dumps = json.dumps([dict(name=r.name.replace(*replace)) for r in entries]) json_dumps = json.dumps([dict(name=r.name.replace(*replace)) for r in entries])
......
...@@ -23,7 +23,18 @@ from flask import session ...@@ -23,7 +23,18 @@ from flask import session
try: try:
from flask_dance.consumer.backend.sqla import SQLAlchemyBackend, first, _get_real_user from flask_dance.consumer.backend.sqla import SQLAlchemyBackend, first, _get_real_user
from sqlalchemy.orm.exc import NoResultFound from sqlalchemy.orm.exc import NoResultFound
backend_resultcode = False # prevent storing values with this resultcode
except ImportError:
# fails on flask-dance >1.3, due to renaming
try:
from flask_dance.consumer.storage.sqla import SQLAlchemyStorage as SQLAlchemyBackend
from flask_dance.consumer.storage.sqla import first, _get_real_user
from sqlalchemy.orm.exc import NoResultFound
backend_resultcode = True # prevent storing values with this resultcode
except ImportError:
pass
try:
class OAuthBackend(SQLAlchemyBackend): class OAuthBackend(SQLAlchemyBackend):
""" """
Stores and retrieves OAuth tokens using a relational database through Stores and retrieves OAuth tokens using a relational database through
...@@ -39,7 +50,7 @@ try: ...@@ -39,7 +50,7 @@ try:
def get(self, blueprint, user=None, user_id=None): def get(self, blueprint, user=None, user_id=None):
if self.provider_id + '_oauth_token' in session and session[self.provider_id + '_oauth_token'] != '': if self.provider_id + '_oauth_token' in session and session[self.provider_id + '_oauth_token'] != '':
return session[blueprint.name + '_oauth_token'] return session[self.provider_id + '_oauth_token']
# check cache # check cache
cache_key = self.make_cache_key(blueprint=blueprint, user=user, user_id=user_id) cache_key = self.make_cache_key(blueprint=blueprint, user=user, user_id=user_id)
token = self.cache.get(cache_key) token = self.cache.get(cache_key)
...@@ -61,7 +72,7 @@ try: ...@@ -61,7 +72,7 @@ try:
use_provider_user_id = True use_provider_user_id = True
if self.user_required and not u and not uid and not use_provider_user_id: if self.user_required and not u and not uid and not use_provider_user_id:
#raise ValueError("Cannot get OAuth token without an associated user") # raise ValueError("Cannot get OAuth token without an associated user")
return None return None
# check for user ID # check for user ID
if hasattr(self.model, "user_id") and uid: if hasattr(self.model, "user_id") and uid:
...@@ -152,5 +163,5 @@ try: ...@@ -152,5 +163,5 @@ try:
blueprint=blueprint, user=user, user_id=user_id, blueprint=blueprint, user=user, user_id=user_id,
)) ))
except ImportError: except Exception:
pass pass
This diff is collapsed.
...@@ -26,19 +26,22 @@ log = logger.create() ...@@ -26,19 +26,22 @@ log = logger.create()
try: from . import goodreads_support try: from . import goodreads_support
except ImportError as err: except ImportError as err:
log.debug("cannot import goodreads, showing authors-metadata will not work: %s", err) log.debug("Cannot import goodreads, showing authors-metadata will not work: %s", err)
goodreads_support = None goodreads_support = None
try: from . import simpleldap as ldap try:
from . import simpleldap as ldap
from .simpleldap import ldapVersion
except ImportError as err: except ImportError as err:
log.debug("cannot import simpleldap, logging in with ldap will not work: %s", err) log.debug("Cannot import simpleldap, logging in with ldap will not work: %s", err)
ldap = None ldap = None
ldapVersion = None
try: try:
from . import SyncToken as SyncToken from . import SyncToken as SyncToken
kobo = True kobo = True
except ImportError as err: except ImportError as err:
log.debug("cannot import SyncToken, syncing books with Kobo Devices will not work: %s", err) log.debug("Cannot import SyncToken, syncing books with Kobo Devices will not work: %s", err)
kobo = None kobo = None
SyncToken = None SyncToken = None
...@@ -23,6 +23,10 @@ from flask_simpleldap import LDAP, LDAPException ...@@ -23,6 +23,10 @@ from flask_simpleldap import LDAP, LDAPException
from .. import constants, logger from .. import constants, logger
try:
from ldap.pkginfo import __version__ as ldapVersion
except ImportError:
pass
log = logger.create() log = logger.create()
_ldap = LDAP() _ldap = LDAP()
...@@ -34,44 +38,69 @@ def init_app(app, config): ...@@ -34,44 +38,69 @@ def init_app(app, config):
app.config['LDAP_HOST'] = config.config_ldap_provider_url app.config['LDAP_HOST'] = config.config_ldap_provider_url
app.config['LDAP_PORT'] = config.config_ldap_port app.config['LDAP_PORT'] = config.config_ldap_port
app.config['LDAP_SCHEMA'] = config.config_ldap_schema if config.config_ldap_encryption == 2:
app.config['LDAP_USERNAME'] = config.config_ldap_user_object.replace('%s', config.config_ldap_serv_username)\ app.config['LDAP_SCHEMA'] = 'ldaps'
+ ',' + config.config_ldap_dn else:
app.config['LDAP_SCHEMA'] = 'ldap'
# app.config['LDAP_SCHEMA'] = config.config_ldap_schema
app.config['LDAP_USERNAME'] = config.config_ldap_serv_username
if config.config_ldap_serv_password is None:
config.config_ldap_serv_password = ''
app.config['LDAP_PASSWORD'] = base64.b64decode(config.config_ldap_serv_password) app.config['LDAP_PASSWORD'] = base64.b64decode(config.config_ldap_serv_password)
app.config['LDAP_REQUIRE_CERT'] = bool(config.config_ldap_require_cert) if bool(config.config_ldap_cert_path):
if config.config_ldap_require_cert: app.config['LDAP_REQUIRE_CERT'] = True
app.config['LDAP_CERT_PATH'] = config.config_ldap_cert_path app.config['LDAP_CERT_PATH'] = config.config_ldap_cert_path
app.config['LDAP_BASE_DN'] = config.config_ldap_dn app.config['LDAP_BASE_DN'] = config.config_ldap_dn
app.config['LDAP_USER_OBJECT_FILTER'] = config.config_ldap_user_object app.config['LDAP_USER_OBJECT_FILTER'] = config.config_ldap_user_object
app.config['LDAP_USE_SSL'] = bool(config.config_ldap_use_ssl)
app.config['LDAP_USE_TLS'] = bool(config.config_ldap_use_tls) app.config['LDAP_USE_TLS'] = bool(config.config_ldap_encryption == 1)
app.config['LDAP_USE_SSL'] = bool(config.config_ldap_encryption == 2)
app.config['LDAP_OPENLDAP'] = bool(config.config_ldap_openldap) app.config['LDAP_OPENLDAP'] = bool(config.config_ldap_openldap)
app.config['LDAP_GROUP_OBJECT_FILTER'] = config.config_ldap_group_object_filter
app.config['LDAP_GROUP_MEMBERS_FIELD'] = config.config_ldap_group_members_field
# app.config['LDAP_CUSTOM_OPTIONS'] = {'OPT_NETWORK_TIMEOUT': 10}
_ldap.init_app(app) _ldap.init_app(app)
def get_object_details(user=None, group=None, query_filter=None, dn_only=False):
return _ldap.get_object_details(user, group, query_filter, dn_only)
def bind():
return _ldap.bind()
def get_group_members(group):
return _ldap.get_group_members(group)
def basic_auth_required(func): def basic_auth_required(func):
return _ldap.basic_auth_required(func) return _ldap.basic_auth_required(func)
def bind_user(username, password): def bind_user(username, password):
# ulf= _ldap.get_object_details('admin')
'''Attempts a LDAP login. '''Attempts a LDAP login.
:returns: True if login succeeded, False if login failed, None if server unavailable. :returns: True if login succeeded, False if login failed, None if server unavailable.
''' '''
try: try:
if _ldap.get_object_details(username):
result = _ldap.bind_user(username, password) result = _ldap.bind_user(username, password)
log.debug("LDAP login '%s': %r", username, result) log.debug("LDAP login '%s': %r", username, result)
return result is not None return result is not None, None
return None, None # User not found
except (TypeError, AttributeError) as ex:
error = ("LDAP bind_user: %s" % ex)
return None, error
except LDAPException as ex: except LDAPException as ex:
if ex.message == 'Invalid credentials': if ex.message == 'Invalid credentials':
log.info("LDAP login '%s' failed: %s", username, ex) error = ("LDAP admin login failed")
return False return None, error
if ex.message == "Can't contact LDAP server": if ex.message == "Can't contact LDAP server":
log.warning('LDAP Server down: %s', ex) # log.warning('LDAP Server down: %s', ex)
return None error = ('LDAP Server down: %s' % ex)
return None, error
else: else:
log.warning('LDAP Server error: %s', ex.message) error = ('LDAP Server error: %s' % ex.message)
return None return None, error
This diff is collapsed.
...@@ -29,15 +29,15 @@ $(function () { ...@@ -29,15 +29,15 @@ $(function () {
var msg = i18nMsg; var msg = i18nMsg;
var douban = "https://api.douban.com"; var douban = "https://api.douban.com";
var dbSearch = "/v2/book/search"; var dbSearch = "/v2/book/search";
var dbDone = true; var dbDone = 0;
var google = "https://www.googleapis.com"; var google = "https://www.googleapis.com";
var ggSearch = "/books/v1/volumes"; var ggSearch = "/books/v1/volumes";
var ggDone = false; var ggDone = 0;
var comicvine = "https://comicvine.gamespot.com"; var comicvine = "https://comicvine.gamespot.com";
var cvSearch = "/api/search/"; var cvSearch = "/api/search/";
var cvDone = false; var cvDone = 0;
var showFlag = 0; var showFlag = 0;
...@@ -73,7 +73,9 @@ $(function () { ...@@ -73,7 +73,9 @@ $(function () {
if (showFlag === 1) { if (showFlag === 1) {
$("#meta-info").html("<ul id=\"book-list\" class=\"media-list\"></ul>"); $("#meta-info").html("<ul id=\"book-list\" class=\"media-list\"></ul>");
} }
if (!ggDone && !dbDone) { if ((ggDone == 3 || (ggDone == 1 && ggResults.length === 0)) &&
(dbDone == 3 || (ggDone == 1 && dbResults.length === 0)) &&
(cvDone == 3 || (ggDone == 1 && cvResults.length === 0))) {
$("#meta-info").html("<p class=\"text-danger\">" + msg.no_result + "</p>"); $("#meta-info").html("<p class=\"text-danger\">" + msg.no_result + "</p>");
return; return;
} }
...@@ -93,7 +95,8 @@ $(function () { ...@@ -93,7 +95,8 @@ $(function () {
return [year, month, day].join("-"); return [year, month, day].join("-");
} }
if (ggDone && ggResults.length > 0) { if (ggResults.length > 0) {
if (ggDone < 2) {
ggResults.forEach(function(result) { ggResults.forEach(function(result) {
var book = { var book = {
id: result.id, id: result.id,
...@@ -121,9 +124,14 @@ $(function () { ...@@ -121,9 +124,14 @@ $(function () {
$("#book-list").append($book); $("#book-list").append($book);
}); });
ggDone = false; ggDone = 2;
} else {
ggDone = 3;
} }
if (dbDone && dbResults.length > 0) { }
if (dbResults.length > 0) {
if (dbDone < 2) {
dbResults.forEach(function(result) { dbResults.forEach(function(result) {
var seriesTitle = ""; var seriesTitle = "";
if (result.series) { if (result.series) {
...@@ -168,9 +176,13 @@ $(function () { ...@@ -168,9 +176,13 @@ $(function () {
$("#book-list").append($book); $("#book-list").append($book);
}); });
dbDone = false; dbDone = 2;
} else {
dbDone = 3;
} }
if (cvDone && cvResults.length > 0) { }
if (cvResults.length > 0) {
if (cvDone < 2) {
cvResults.forEach(function(result) { cvResults.forEach(function(result) {
var seriesTitle = ""; var seriesTitle = "";
if (result.volume.name) { if (result.volume.name) {
...@@ -214,7 +226,10 @@ $(function () { ...@@ -214,7 +226,10 @@ $(function () {
$("#book-list").append($book); $("#book-list").append($book);
}); });
cvDone = false; cvDone = 2;
} else {
cvDone = 3;
}
} }
} }
...@@ -227,11 +242,10 @@ $(function () { ...@@ -227,11 +242,10 @@ $(function () {
success: function success(data) { success: function success(data) {
if ("items" in data) { if ("items" in data) {
ggResults = data.items; ggResults = data.items;
ggDone = true;
} }
}, },
complete: function complete() { complete: function complete() {
ggDone = true; ggDone = 1;
showResult(); showResult();
$("#show-google").trigger("change"); $("#show-google").trigger("change");
} }
...@@ -252,7 +266,7 @@ $(function () { ...@@ -252,7 +266,7 @@ $(function () {
$("#meta-info").html("<p class=\"text-danger\">" + msg.search_error + "!</p>" + $("#meta-info")[0].innerHTML); $("#meta-info").html("<p class=\"text-danger\">" + msg.search_error + "!</p>" + $("#meta-info")[0].innerHTML);
}, },
complete: function complete() { complete: function complete() {
dbDone = true; dbDone = 1;
showResult(); showResult();
$("#show-douban").trigger("change"); $("#show-douban").trigger("change");
} }
...@@ -274,7 +288,7 @@ $(function () { ...@@ -274,7 +288,7 @@ $(function () {
$("#meta-info").html("<p class=\"text-danger\">" + msg.search_error + "!</p>" + $("#meta-info")[0].innerHTML); $("#meta-info").html("<p class=\"text-danger\">" + msg.search_error + "!</p>" + $("#meta-info")[0].innerHTML);
}, },
complete: function complete() { complete: function complete() {
cvDone = true; cvDone = 1;
showResult(); showResult();
$("#show-comics").trigger("change"); $("#show-comics").trigger("change");
} }
...@@ -283,6 +297,10 @@ $(function () { ...@@ -283,6 +297,10 @@ $(function () {
function doSearch (keyword) { function doSearch (keyword) {
showFlag = 0; showFlag = 0;
dbDone = ggDone = cvDone = 0;
dbResults = [];
ggResults = [];
cvResults = [];
$("#meta-info").text(msg.loading); $("#meta-info").text(msg.loading);
if (keyword) { if (keyword) {
dbSearchBook(keyword); dbSearchBook(keyword);
......
...@@ -29,7 +29,6 @@ $(document).on("change", "input[type=\"checkbox\"][data-control]", function () { ...@@ -29,7 +29,6 @@ $(document).on("change", "input[type=\"checkbox\"][data-control]", function () {
}); });
}); });
// Generic control/related handler to show/hide fields based on a select' value // Generic control/related handler to show/hide fields based on a select' value
$(document).on("change", "select[data-control]", function() { $(document).on("change", "select[data-control]", function() {
var $this = $(this); var $this = $(this);
...@@ -39,10 +38,23 @@ $(document).on("change", "select[data-control]", function() { ...@@ -39,10 +38,23 @@ $(document).on("change", "select[data-control]", function() {
for (var i = 0; i < $(this)[0].length; i++) { for (var i = 0; i < $(this)[0].length; i++) {
var element = parseInt($(this)[0][i].value); var element = parseInt($(this)[0][i].value);
if (element === showOrHide) { if (element === showOrHide) {
$("[data-related=" + name + "-" + element + "]").show(); $("[data-related^=" + name + "][data-related*=-" + element + "]").show();
} else { } else {
$("[data-related=" + name + "-" + element + "]").hide(); $("[data-related^=" + name + "][data-related*=-" + element + "]").hide();
}
} }
});
// Generic control/related handler to show/hide fields based on a select' value
// this one is made to show all values if select value is not 0
$(document).on("change", "select[data-controlall]", function() {
var $this = $(this);
var name = $this.data("controlall");
var showOrHide = parseInt($this.val());
if (showOrHide) {
$("[data-related=" + name + "]").show();
} else {
$("[data-related=" + name + "]").hide();
} }
}); });
...@@ -64,7 +76,7 @@ $(function() { ...@@ -64,7 +76,7 @@ $(function() {
function cleanUp() { function cleanUp() {
clearInterval(updateTimerID); clearInterval(updateTimerID);
$("#spinner2").hide(); $("#spinner2").hide();
$("#updateFinished").removeClass("hidden"); $("#DialogFinished").removeClass("hidden");
$("#check_for_update").removeClass("hidden"); $("#check_for_update").removeClass("hidden");
$("#perform_update").addClass("hidden"); $("#perform_update").addClass("hidden");
$("#message").alert("close"); $("#message").alert("close");
...@@ -81,13 +93,13 @@ $(function() { ...@@ -81,13 +93,13 @@ $(function() {
url: window.location.pathname + "/../../get_updater_status", url: window.location.pathname + "/../../get_updater_status",
success: function success(data) { success: function success(data) {
// console.log(data.status); // console.log(data.status);
$("#Updatecontent").html(updateText[data.status]); $("#DialogContent").html(updateText[data.status]);
if (data.status > 6) { if (data.status > 6) {
cleanUp(); cleanUp();
} }
}, },
error: function error() { error: function error() {
$("#Updatecontent").html(updateText[7]); $("#DialogContent").html(updateText[7]);
cleanUp(); cleanUp();
}, },
timeout: 2000 timeout: 2000
...@@ -146,8 +158,8 @@ $(function() { ...@@ -146,8 +158,8 @@ $(function() {
var $this = $(this); var $this = $(this);
var buttonText = $this.html(); var buttonText = $this.html();
$this.html("..."); $this.html("...");
$("#Updatecontent").html(""); $("#DialogContent").html("");
$("#updateFinished").addClass("hidden"); $("#DialogFinished").addClass("hidden");
$("#update_error").addClass("hidden"); $("#update_error").addClass("hidden");
if ($("#message").length) { if ($("#message").length) {
$("#message").alert("close"); $("#message").alert("close");
...@@ -189,13 +201,24 @@ $(function() { ...@@ -189,13 +201,24 @@ $(function() {
}); });
}); });
$("#restart_database").click(function() { $("#restart_database").click(function() {
$("#DialogHeader").addClass("hidden");
$("#DialogFinished").addClass("hidden");
$("#DialogContent").html("");
$("#spinner2").show();
$.ajax({ $.ajax({
dataType: "json", dataType: "json",
url: window.location.pathname + "/../../shutdown", url: window.location.pathname + "/../../shutdown",
data: {"parameter":2} data: {"parameter":2},
success: function success(data) {
$("#spinner2").hide();
ResultText = data.text;
$("#DialogContent").html(ResultText);
$("#DialogFinished").removeClass("hidden");
}
}); });
}); });
$("#perform_update").click(function() { $("#perform_update").click(function() {
$("#DialogHeader").removeClass("hidden");
$("#spinner2").show(); $("#spinner2").show();
$.ajax({ $.ajax({
type: "POST", type: "POST",
...@@ -204,7 +227,7 @@ $(function() { ...@@ -204,7 +227,7 @@ $(function() {
url: window.location.pathname + "/../../get_updater_status", url: window.location.pathname + "/../../get_updater_status",
success: function success(data) { success: function success(data) {
updateText = data.text; updateText = data.text;
$("#Updatecontent").html(updateText[data.status]); $("#DialogContent").html(updateText[data.status]);
// console.log(data.status); // console.log(data.status);
updateTimerID = setInterval(updateTimer, 2000); updateTimerID = setInterval(updateTimer, 2000);
} }
...@@ -214,6 +237,7 @@ $(function() { ...@@ -214,6 +237,7 @@ $(function() {
// Init all data control handlers to default // Init all data control handlers to default
$("input[data-control]").trigger("change"); $("input[data-control]").trigger("change");
$("select[data-control]").trigger("change"); $("select[data-control]").trigger("change");
$("select[data-controlall]").trigger("change");
$("#bookDetailsModal") $("#bookDetailsModal")
.on("show.bs.modal", function(e) { .on("show.bs.modal", function(e) {
...@@ -274,6 +298,26 @@ $(function() { ...@@ -274,6 +298,26 @@ $(function() {
$(".discover .row").isotope("layout"); $(".discover .row").isotope("layout");
}); });
$('#import_ldap_users').click(function() {
$("#DialogHeader").addClass("hidden");
$("#DialogFinished").addClass("hidden");
$("#DialogContent").html("");
$("#spinner2").show();
var pathname = document.getElementsByTagName("script"), src = pathname[pathname.length - 1].src;
var path = src.substring(0, src.lastIndexOf("/"));
$.ajax({
method:"get",
dataType: "json",
url: path + "/../../import_ldap_users",
success: function success(data) {
$("#spinner2").hide();
ResultText = data.text;
$("#DialogContent").html(ResultText);
$("#DialogFinished").removeClass("hidden");
}
});
});
$(".author-expand").click(function() { $(".author-expand").click(function() {
$(this).parent().find("a.author-name").slice($(this).data("authors-max")).toggle(); $(this).parent().find("a.author-name").slice($(this).data("authors-max")).toggle();
$(this).parent().find("span.author-hidden-divider").toggle(); $(this).parent().find("span.author-hidden-divider").toggle();
......
...@@ -36,6 +36,10 @@ ...@@ -36,6 +36,10 @@
{% endfor %} {% endfor %}
</table> </table>
<div class="btn btn-default" id="admin_new_user"><a href="{{url_for('admin.new_user')}}">{{_('Add New User')}}</a></div> <div class="btn btn-default" id="admin_new_user"><a href="{{url_for('admin.new_user')}}">{{_('Add New User')}}</a></div>
{% if (config.config_login_type == 1) %}
<div class="btn btn-default" id="import_ldap_users" data-toggle="modal" data-target="#StatusDialog">{{_('Import LDAP Users')}}</div>
<!--a href="#" id="import_ldap_users" name="import_ldap_users"><button type="submit" class="btn btn-default">{{_('Import LDAP Users')}}</button></a-->
{% endif %}
</div> </div>
</div> </div>
...@@ -120,7 +124,7 @@ ...@@ -120,7 +124,7 @@
<div class="col"> <div class="col">
<h2>{{_('Administration')}}</h2> <h2>{{_('Administration')}}</h2>
<div class="btn btn-default"><a id="logfile" href="{{url_for('admin.view_logfile')}}">{{_('View Logs')}}</a></div> <div class="btn btn-default"><a id="logfile" href="{{url_for('admin.view_logfile')}}">{{_('View Logs')}}</a></div>
<div class="btn btn-default" id="restart_database">{{_('Reconnect Calibre Database')}}</div> <div class="btn btn-default" id="restart_database" data-toggle="modal" data-target="#StatusDialog">{{_('Reconnect Calibre Database')}}</div>
<div class="btn btn-default" id="admin_restart" data-toggle="modal" data-target="#RestartDialog">{{_('Restart')}}</div> <div class="btn btn-default" id="admin_restart" data-toggle="modal" data-target="#RestartDialog">{{_('Restart')}}</div>
<div class="btn btn-default" id="admin_stop" data-toggle="modal" data-target="#ShutdownDialog">{{_('Shutdown')}}</div> <div class="btn btn-default" id="admin_stop" data-toggle="modal" data-target="#ShutdownDialog">{{_('Shutdown')}}</div>
</div> </div>
...@@ -146,7 +150,7 @@ ...@@ -146,7 +150,7 @@
<div class="hidden" id="update_error"> <span>{{update_error}}</span></div> <div class="hidden" id="update_error"> <span>{{update_error}}</span></div>
<div class="btn btn-default" id="check_for_update">{{_('Check for Update')}}</div> <div class="btn btn-default" id="check_for_update">{{_('Check for Update')}}</div>
<div class="btn btn-default hidden" id="perform_update" data-toggle="modal" data-target="#UpdateprogressDialog">{{_('Perform Update')}}</div> <div class="btn btn-default hidden" id="perform_update" data-toggle="modal" data-target="#StatusDialog">{{_('Perform Update')}}</div>
</div> </div>
</div> </div>
</div> </div>
...@@ -183,21 +187,21 @@ ...@@ -183,21 +187,21 @@
</div> </div>
</div> </div>
</div> </div>
<div id="UpdateprogressDialog" class="modal fade" role="dialog"> <div id="StatusDialog" class="modal fade" role="dialog">
<div class="modal-dialog modal-sm"> <div class="modal-dialog modal-sm">
<!-- Modal content--> <!-- Modal content-->
<div class="modal-content"> <div class="modal-content">
<div class="modal-header bg-info text-center"> <div class="modal-header bg-info text-center">
<span>{{_('Updating, please do not reload this page')}}</span> <span id="DialogHeader">{{_('Updating, please do not reload this page')}}</span>
</div> </div>
<div class="modal-body text-center"> <div class="modal-body text-center">
<div id="spinner2" class="spinner2" style="display:none;"> <div id="spinner2" class="spinner2" style="display:none;">
<img id="img-spinner2" src="{{ url_for('static', filename='css/libs/images/loading-icon.gif') }}"/> <img id="img-spinner2" src="{{ url_for('static', filename='css/libs/images/loading-icon.gif') }}"/>
</div> </div>
<p></p> <p></p>
<div id="Updatecontent"></div> <div id="DialogContent"></div>
<p></p> <p></p>
<button type="button" class="btn btn-default hidden" id="updateFinished" data-dismiss="modal">{{_('OK')}}</button> <button type="button" class="btn btn-default hidden" id="DialogFinished" data-dismiss="modal">{{_('OK')}}</button>
</div> </div>
</div> </div>
</div> </div>
......
...@@ -198,6 +198,17 @@ ...@@ -198,6 +198,17 @@
</div> </div>
</div> </div>
{% endif %} {% endif %}
<div class="form-group">
<input type="checkbox" id="config_allow_reverse_proxy_header_login" name="config_allow_reverse_proxy_header_login" data-control="reverse-proxy-login-settings" {% if config.config_allow_reverse_proxy_header_login %}checked{% endif %}>
<label for="config_allow_reverse_proxy_header_login">{{_('Allow Reverse Proxy Authentication')}}</label>
</div>
<div data-related="reverse-proxy-login-settings">
<div class="form-group">
<label for="config_reverse_proxy_login_header_name">{{_('Reverse Proxy Header Name')}}</label>
<input type="text" class="form-control" id="config_reverse_proxy_login_header_name" name="config_reverse_proxy_login_header_name" value="{% if config.config_reverse_proxy_login_header_name != None %}{{ config.config_reverse_proxy_login_header_name }}{% endif %}" autocomplete="off">
</div>
</div>
{% if not config.config_is_initial %}
{% if feature_support['ldap'] or feature_support['oauth'] %} {% if feature_support['ldap'] or feature_support['oauth'] %}
<div class="form-group"> <div class="form-group">
<label for="config_login_type">{{_('Login type')}}</label> <label for="config_login_type">{{_('Login type')}}</label>
...@@ -219,37 +230,31 @@ ...@@ -219,37 +230,31 @@
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="config_ldap_port">{{_('LDAP Server Port')}}</label> <label for="config_ldap_port">{{_('LDAP Server Port')}}</label>
<input type="text" class="form-control" id="config_ldap_port" name="config_ldap_port" value="{% if config.config_ldap_port != None %}{{ config.config_ldap_port }}{% endif %}" autocomplete="off"> <input type="number" min="1" max="65535" class="form-control" id="config_ldap_port" name="config_ldap_port" value="{% if config.config_ldap_port != None %}{{ config.config_ldap_port }}{% endif %}" autocomplete="off" required>
</div>
<div class="form-group">
<label for="config_ldap_schema">{{_('LDAP Schema (LDAP or LPAPS)')}}</label>
<input type="text" class="form-control" id="config_ldap_schema" name="config_ldap_schema" value="{% if config.config_ldap_schema != None %}{{ config.config_ldap_schema }}{% endif %}" autocomplete="off">
</div>
<div class="form-group">
<label for="config_ldap_serv_username">{{_('LDAP Administrator Username')}}</label>
<input type="text" class="form-control" id="config_ldap_serv_username" name="config_ldap_serv_username" value="{% if config.config_ldap_serv_username != None %}{{ config.config_ldap_serv_username }}{% endif %}" autocomplete="off">
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="config_ldap_serv_password">{{_('LDAP Administrator Password')}}</label> <label for="config_ldap_encryption">{{_('LDAP Encryption')}}</label>
<input type="password" class="form-control" id="config_ldap_serv_password" name="config_ldap_serv_password" value="{% if config.config_ldap_serv_password != None %}{{ config.config_ldap_serv_password }}{% endif %}" autocomplete="off"> <label for="config_ldap_encryption">{{_('LDAP Encryption')}}</label>
<select name="config_ldap_encryption" id="config_ldap_encryption" class="form-control" data-controlall="ldap-cert-settings">
<option value="0" {% if config.config_ldap_encryption == 0 %}selected{% endif %}>{{ _('None') }}</option>
<option value="1" {% if config.config_ldap_encryption == 1 %}selected{% endif %}>{{ _('TLS') }}</option>
<option value="2" {% if config.config_ldap_encryption == 2 %}selected{% endif %}>{{ _('SSL') }}</option>
</select>
</div> </div>
<div data-related="ldap-cert-settings">
<div class="form-group"> <div class="form-group">
<input type="checkbox" id="config_ldap_use_ssl" name="config_ldap_use_ssl" {% if config.config_ldap_use_ssl %}checked{% endif %}> <label for="config_ldap_cert_path">{{_('LDAP Certificate Path')}}</label>
<label for="config_ldap_use_ssl">{{_('LDAP Server Enable SSL')}}</label> <input type="text" class="form-control" id="config_ldap_cert_path" name="config_ldap_cert_path" value="{% if config.config_ldap_cert_path != None %}{{ config.config_ldap_cert_path }}{% endif %}" autocomplete="off">
</div> </div>
<div class="form-group">
<input type="checkbox" id="config_ldap_use_tls" name="config_ldap_use_tls" {% if config.config_ldap_use_tls %}checked{% endif %}>
<label for="config_ldap_use_tls">{{_('LDAP Server Enable TLS')}}</label>
</div> </div>
<div class="form-group"> <div class="form-group">
<input type="checkbox" id="config_ldap_require_cert" name="config_ldap_require_cert" data-control="ldap-cert-settings" {% if config.config_ldap_require_cert %}checked{% endif %}> <label for="config_ldap_serv_username">{{_('LDAP Administrator Username')}}</label>
<label for="config_ldap_require_cert">{{_('LDAP Server Certificate')}}</label> <input type="text" class="form-control" id="config_ldap_serv_username" name="config_ldap_serv_username" value="{% if config.config_ldap_serv_username != None %}{{ config.config_ldap_serv_username }}{% endif %}" autocomplete="off">
</div> </div>
<div data-related="ldap-cert-settings">
<div class="form-group"> <div class="form-group">
<label for="config_ldap_cert_path">{{_('LDAP SSL Certificate Path')}}</label> <label for="config_ldap_serv_password">{{_('LDAP Administrator Password')}}</label>
<input type="text" class="form-control" id="config_ldap_cert_path" name="config_ldap_cert_path" value="{% if config.config_ldap_cert_path != None and config.config_ldap_require_cert !=None %}{{ config.config_ldap_cert_path }}{% endif %}" autocomplete="off"> <input type="password" class="form-control" id="config_ldap_serv_password" name="config_ldap_serv_password" value="" autocomplete="off">
</div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="config_ldap_dn">{{_('LDAP Distinguished Name (DN)')}}</label> <label for="config_ldap_dn">{{_('LDAP Distinguished Name (DN)')}}</label>
...@@ -263,6 +268,19 @@ ...@@ -263,6 +268,19 @@
<input type="checkbox" id="config_ldap_openldap" name="config_ldap_openldap" {% if config.config_ldap_openldap %}checked{% endif %}> <input type="checkbox" id="config_ldap_openldap" name="config_ldap_openldap" {% if config.config_ldap_openldap %}checked{% endif %}>
<label for="config_ldap_openldap">{{_('LDAP Server is OpenLDAP?')}}</label> <label for="config_ldap_openldap">{{_('LDAP Server is OpenLDAP?')}}</label>
</div> </div>
<h4 class="text-center">{{_('Following Settings are Needed For User Import')}}</h4>
<div class="form-group">
<label for="config_ldap_group_object_filter">{{_('LDAP Group Object Filter')}}</label>
<input type="text" class="form-control" id="config_ldap_group_object_filter" name="config_ldap_group_object_filter" value="{% if config.config_ldap_group_object_filter != None %}{{ config.config_ldap_group_object_filter }}{% endif %}" autocomplete="off">
</div>
<div class="form-group">
<label for="config_ldap_group_name">{{_('LDAP Group Name')}}</label>
<input type="text" class="form-control" id="config_ldap_group_name" name="config_ldap_group_name" value="{% if config.config_ldap_group_name != None %}{{ config.config_ldap_group_name }}{% endif %}" autocomplete="off">
</div>
<div class="form-group">
<label for="config_ldap_group_members_field">{{_('LDAP Group Members Field')}}</label>
<input type="text" class="form-control" id="config_ldap_group_members_field" name="config_ldap_group_members_field" value="{% if config.config_ldap_group_members_field != None %}{{ config.config_ldap_group_members_field }}{% endif %}" autocomplete="off">
</div>
</div> </div>
{% endif %} {% endif %}
{% if feature_support['oauth'] %} {% if feature_support['oauth'] %}
...@@ -283,16 +301,7 @@ ...@@ -283,16 +301,7 @@
</div> </div>
{% endif %} {% endif %}
{% endif %} {% endif %}
<div class="form-group"> {% endif %}
<input type="checkbox" id="config_allow_reverse_proxy_header_login" name="config_allow_reverse_proxy_header_login" data-control="reverse-proxy-login-settings" {% if config.config_allow_reverse_proxy_header_login %}checked{% endif %}>
<label for="config_allow_reverse_proxy_header_login">{{_('Allow Reverse Proxy Authentication')}}</label>
</div>
<div data-related="reverse-proxy-login-settings">
<div class="form-group">
<label for="config_reverse_proxy_login_header_name">{{_('Reverse Proxy Header Name')}}</label>
<input type="text" class="form-control" id="config_reverse_proxy_login_header_name" name="config_reverse_proxy_login_header_name" value="{% if config.config_reverse_proxy_login_header_name != None %}{{ config.config_reverse_proxy_login_header_name }}{% endif %}" autocomplete="off">
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
......
...@@ -25,20 +25,22 @@ ...@@ -25,20 +25,22 @@
<a href="{{url_for('web.remote_login')}}" class="pull-right">{{_('Log in with Magic Link')}}</a> <a href="{{url_for('web.remote_login')}}" class="pull-right">{{_('Log in with Magic Link')}}</a>
{% endif %} {% endif %}
{% if config.config_login_type == 2 %} {% if config.config_login_type == 2 %}
<a href="{{url_for('oauth.github_login')}}" class="pull-right"> {% if 1 in oauth_check %}
<a href="{{url_for('oauth.github_login')}}" class="pull-right github">
<svg height="32" class="octicon octicon-mark-github" viewBox="0 0 16 16" version="1.1" width="32" aria-hidden="true"> <svg height="32" class="octicon octicon-mark-github" viewBox="0 0 16 16" version="1.1" width="32" aria-hidden="true">
<path fill-rule="evenodd" d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0 0 16 8c0-4.42-3.58-8-8-8z"></path> <path fill-rule="evenodd" d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0 0 16 8c0-4.42-3.58-8-8-8z"></path>
</svg> </svg>
</a> </a>
{% endif %} {% endif %}
{% if config.config_login_type == 3 %} {% if 2 in oauth_check %}
<a href="{{url_for('oauth.google_login')}}" class="pull-right"> <a href="{{url_for('oauth.google_login')}}" class="pull-right google">
<svg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" <svg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px"
width="40" height="40" width="40" height="40"
viewBox="0 3 48 49" viewBox="0 3 48 49"
style="fill:#000000;"><g id="surface1"><path style=" fill:#FFC107;" d="M 43.609375 20.082031 L 42 20.082031 L 42 20 L 24 20 L 24 28 L 35.304688 28 C 33.652344 32.65625 29.222656 36 24 36 C 17.371094 36 12 30.628906 12 24 C 12 17.371094 17.371094 12 24 12 C 27.058594 12 29.84375 13.152344 31.960938 15.039063 L 37.617188 9.382813 C 34.046875 6.054688 29.269531 4 24 4 C 12.953125 4 4 12.953125 4 24 C 4 35.046875 12.953125 44 24 44 C 35.046875 44 44 35.046875 44 24 C 44 22.660156 43.863281 21.351563 43.609375 20.082031 Z "></path><path style=" fill:#FF3D00;" d="M 6.304688 14.691406 L 12.878906 19.511719 C 14.65625 15.109375 18.960938 12 24 12 C 27.058594 12 29.84375 13.152344 31.960938 15.039063 L 37.617188 9.382813 C 34.046875 6.054688 29.269531 4 24 4 C 16.316406 4 9.65625 8.335938 6.304688 14.691406 Z "></path><path style=" fill:#4CAF50;" d="M 24 44 C 29.164063 44 33.859375 42.023438 37.410156 38.808594 L 31.21875 33.570313 C 29.210938 35.089844 26.714844 36 24 36 C 18.796875 36 14.382813 32.683594 12.71875 28.054688 L 6.195313 33.078125 C 9.503906 39.554688 16.226563 44 24 44 Z "></path><path style=" fill:#1976D2;" d="M 43.609375 20.082031 L 42 20.082031 L 42 20 L 24 20 L 24 28 L 35.304688 28 C 34.511719 30.238281 33.070313 32.164063 31.214844 33.570313 C 31.21875 33.570313 31.21875 33.570313 31.21875 33.570313 L 37.410156 38.808594 C 36.972656 39.203125 44 34 44 24 C 44 22.660156 43.863281 21.351563 43.609375 20.082031 Z "></path></g></svg> style="fill:#000000;"><g id="surface1"><path style=" fill:#FFC107;" d="M 43.609375 20.082031 L 42 20.082031 L 42 20 L 24 20 L 24 28 L 35.304688 28 C 33.652344 32.65625 29.222656 36 24 36 C 17.371094 36 12 30.628906 12 24 C 12 17.371094 17.371094 12 24 12 C 27.058594 12 29.84375 13.152344 31.960938 15.039063 L 37.617188 9.382813 C 34.046875 6.054688 29.269531 4 24 4 C 12.953125 4 4 12.953125 4 24 C 4 35.046875 12.953125 44 24 44 C 35.046875 44 44 35.046875 44 24 C 44 22.660156 43.863281 21.351563 43.609375 20.082031 Z "></path><path style=" fill:#FF3D00;" d="M 6.304688 14.691406 L 12.878906 19.511719 C 14.65625 15.109375 18.960938 12 24 12 C 27.058594 12 29.84375 13.152344 31.960938 15.039063 L 37.617188 9.382813 C 34.046875 6.054688 29.269531 4 24 4 C 16.316406 4 9.65625 8.335938 6.304688 14.691406 Z "></path><path style=" fill:#4CAF50;" d="M 24 44 C 29.164063 44 33.859375 42.023438 37.410156 38.808594 L 31.21875 33.570313 C 29.210938 35.089844 26.714844 36 24 36 C 18.796875 36 14.382813 32.683594 12.71875 28.054688 L 6.195313 33.078125 C 9.503906 39.554688 16.226563 44 24 44 Z "></path><path style=" fill:#1976D2;" d="M 43.609375 20.082031 L 42 20.082031 L 42 20 L 24 20 L 24 28 L 35.304688 28 C 34.511719 30.238281 33.070313 32.164063 31.214844 33.570313 C 31.21875 33.570313 31.21875 33.570313 31.21875 33.570313 L 37.410156 38.808594 C 36.972656 39.203125 44 34 44 24 C 44 22.660156 43.863281 21.351563 43.609375 20.082031 Z "></path></g></svg>
</a> </a>
{% endif %} {% endif %}
{% endif %}
</form> </form>
</div> </div>
{% if error %} {% if error %}
......
...@@ -35,10 +35,12 @@ ...@@ -35,10 +35,12 @@
</thead> </thead>
<tbody> <tbody>
{% for library,version in versions.items() %} {% for library,version in versions.items() %}
{% if version %}
<tr> <tr>
<th>{{library}}</th> <th>{{library}}</th>
<td>{{_(version)}}</td> <td>{{_(version)}}</td>
</tr> </tr>
{% endif %}
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>
......
...@@ -46,7 +46,7 @@ ...@@ -46,7 +46,7 @@
{% endfor %} {% endfor %}
</select> </select>
</div> </div>
{% if registered_oauth.keys()| length > 0 %} {% if registered_oauth.keys()| length > 0 and not new_user %}
{% for id, name in registered_oauth.items() %} {% for id, name in registered_oauth.items() %}
<div class="form-group"> <div class="form-group">
<label>{{ name }} {{_('OAuth Settings')}}</label> <label>{{ name }} {{_('OAuth Settings')}}</label>
...@@ -161,7 +161,7 @@ ...@@ -161,7 +161,7 @@
</div> </div>
<div class="modal-body">...</div> <div class="modal-body">...</div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">{{_('Close')}}</button> <button type="button" id="kobo_close" class="btn btn-default" data-dismiss="modal">{{_('Close')}}</button>
</div> </div>
</div> </div>
</div> </div>
......
...@@ -31,6 +31,11 @@ try: ...@@ -31,6 +31,11 @@ try:
from flask_dance.consumer.backend.sqla import OAuthConsumerMixin from flask_dance.consumer.backend.sqla import OAuthConsumerMixin
oauth_support = True oauth_support = True
except ImportError: except ImportError:
# fails on flask-dance >1.3, due to renaming
try:
from flask_dance.consumer.storage.sqla import OAuthConsumerMixin
oauth_support = True
except ImportError:
oauth_support = False oauth_support = False
from sqlalchemy import create_engine, exc, exists, event from sqlalchemy import create_engine, exc, exists, event
from sqlalchemy import Column, ForeignKey from sqlalchemy import Column, ForeignKey
...@@ -237,7 +242,7 @@ class Anonymous(AnonymousUserMixin, UserBase): ...@@ -237,7 +242,7 @@ class Anonymous(AnonymousUserMixin, UserBase):
self.sidebar_view = data.sidebar_view self.sidebar_view = data.sidebar_view
self.default_language = data.default_language self.default_language = data.default_language
self.locale = data.locale self.locale = data.locale
self.mature_content = data.mature_content # self.mature_content = data.mature_content
self.kindle_mail = data.kindle_mail self.kindle_mail = data.kindle_mail
self.denied_tags = data.denied_tags self.denied_tags = data.denied_tags
self.allowed_tags = data.allowed_tags self.allowed_tags = data.allowed_tags
...@@ -531,14 +536,12 @@ def migrate_Database(session): ...@@ -531,14 +536,12 @@ def migrate_Database(session):
"locale VARCHAR(2)," "locale VARCHAR(2),"
"sidebar_view INTEGER," "sidebar_view INTEGER,"
"default_language VARCHAR(3)," "default_language VARCHAR(3),"
"mature_content BOOLEAN,"
"UNIQUE (nickname)," "UNIQUE (nickname),"
"UNIQUE (email)," "UNIQUE (email))")
"CHECK (mature_content IN (0, 1)))")
conn.execute("INSERT INTO user_id(id, nickname, email, role, password, kindle_mail,locale," conn.execute("INSERT INTO user_id(id, nickname, email, role, password, kindle_mail,locale,"
"sidebar_view, default_language, mature_content) " "sidebar_view, default_language) "
"SELECT id, nickname, email, role, password, kindle_mail, locale," "SELECT id, nickname, email, role, password, kindle_mail, locale,"
"sidebar_view, default_language, mature_content FROM user") "sidebar_view, default_language FROM user")
# delete old user table and rename new user_id table to user: # delete old user table and rename new user_id table to user:
conn.execute("DROP TABLE user") conn.execute("DROP TABLE user")
conn.execute("ALTER TABLE user_id RENAME TO user") conn.execute("ALTER TABLE user_id RENAME TO user")
......
This diff is collapsed.
# GDrive Integration # GDrive Integration
google-api-python-client==1.7.11,<1.8.0 google-api-python-client==1.7.11,<1.8.0
gevent>=1.2.1,<1.5.0 gevent>=1.2.1,<1.6.0
greenlet>=0.4.12,<0.5.0 greenlet>=0.4.12,<0.5.0
httplib2>=0.9.2,<0.18.0 httplib2>=0.9.2,<0.18.0
oauth2client>=4.0.0,<4.14.0 oauth2client>=4.0.0,<4.14.0
uritemplate>=3.0.0,<3.1.0 uritemplate>=3.0.0,<3.1.0
pyasn1-modules>=0.0.8,<0.3.0 pyasn1-modules>=0.0.8,<0.3.0
pyasn1>=0.1.9,<0.5.0 pyasn1>=0.1.9,<0.5.0
PyDrive>=1.3.1,<1.14.0 PyDrive>=1.3.1,<1.4.0
PyYAML>=3.12 PyYAML>=3.12
rsa==3.4.2,<4.1.0 rsa==3.4.2,<4.1.0
six>=1.10.0,<1.14.0 six>=1.10.0,<1.15.0
# goodreads # goodreads
goodreads>=0.3.2,<0.4.0 goodreads>=0.3.2,<0.4.0
...@@ -18,15 +18,15 @@ python-Levenshtein>=0.12.0,<0.13.0 ...@@ -18,15 +18,15 @@ python-Levenshtein>=0.12.0,<0.13.0
# ldap login # ldap login
python_ldap>=3.0.0,<3.3.0 python_ldap>=3.0.0,<3.3.0
flask-simpleldap>1.3.0,<1.5.0 flask-simpleldap>=1.4.0,<1.5.0
#oauth #oauth
flask-dance>=0.13.0 flask-dance>=1.4.0,<3.1.0
sqlalchemy_utils>=0.33.5,<0.37.0 sqlalchemy_utils>=0.33.5,<0.37.0
# extracting metadata # extracting metadata
lxml>=3.8.0,<4.6.0 lxml>=3.8.0,<4.6.0
Pillow>=4.0.0,<7.1.0 Pillow>=4.0.0,<7.2.0
rarfile>=2.7 rarfile>=2.7
# other # other
......
Babel>=1.3, <2.9 Babel>=1.3, <2.9
Flask-Babel>=0.11.1,<1.1.0 Flask-Babel>=0.11.1,<1.1.0
Flask-Login>=0.3.2,<0.5.1 Flask-Login>=0.3.2,<0.5.1
Flask-Principal>=0.3.2,<0.5.0 Flask-Principal>=0.3.2,<0.5.1
singledispatch>=3.4.0.0,<3.5.0.0 singledispatch>=3.4.0.0,<3.5.0.0
backports_abc>=0.4 backports_abc>=0.4
Flask>=1.0.2,<1.2.0 Flask>=1.0.2,<1.2.0
iso-639>=0.4.5,<0.5.0 iso-639>=0.4.5,<0.5.0
PyPDF2==1.26.0,<1.27.0 PyPDF2==1.26.0,<1.27.0
pytz>=2016.10 pytz>=2016.10
requests>=2.11.1,<2.23.0 requests>=2.11.1,<2.24.0
SQLAlchemy>=1.1.0,<1.4.0 SQLAlchemy>=1.1.0,<1.4.0
tornado>=4.1,<6.1 tornado>=4.1,<6.1
Wand>=0.4.4,<0.6.0 Wand>=0.4.4,<0.6.0
......
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment