Commit 1ef2a964 authored by cbartondock's avatar cbartondock

Merge branch 'master' of https://github.com/janeczku/calibre-web

parents 85f1ca61 6e5a1a1f
...@@ -20,16 +20,18 @@ ...@@ -20,16 +20,18 @@
from __future__ import division, print_function, unicode_literals from __future__ import division, print_function, unicode_literals
import os import os
import sys import sys
import json
from sqlalchemy import exc, Column, String, Integer, SmallInteger, Boolean, BLOB, JSON from sqlalchemy import Column, String, Integer, SmallInteger, Boolean, BLOB, JSON
from sqlalchemy.exc import OperationalError from sqlalchemy.exc import OperationalError
from sqlalchemy.sql.expression import text
try: try:
# Compatibility with sqlalchemy 2.0 # Compatibility with sqlalchemy 2.0
from sqlalchemy.orm import declarative_base from sqlalchemy.orm import declarative_base
except ImportError: except ImportError:
from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.ext.declarative import declarative_base
from . import constants, cli, logger, ub from . import constants, cli, logger
log = logger.create() log = logger.create()
...@@ -260,7 +262,6 @@ class _ConfigSQL(object): ...@@ -260,7 +262,6 @@ class _ConfigSQL(object):
""" """
new_value = dictionary.get(field, default) new_value = dictionary.get(field, default)
if new_value is None: if new_value is None:
# log.debug("_ConfigSQL set_from_dictionary field '%s' not found", field)
return False return False
if field not in self.__dict__: if field not in self.__dict__:
...@@ -277,7 +278,6 @@ class _ConfigSQL(object): ...@@ -277,7 +278,6 @@ class _ConfigSQL(object):
if current_value == new_value: if current_value == new_value:
return False return False
# log.debug("_ConfigSQL set_from_dictionary '%s' = %r (was %r)", field, new_value, current_value)
setattr(self, field, new_value) setattr(self, field, new_value)
return True return True
...@@ -368,20 +368,23 @@ def _migrate_table(session, orm_class): ...@@ -368,20 +368,23 @@ def _migrate_table(session, orm_class):
column_default = "" column_default = ""
else: else:
if isinstance(column.default.arg, bool): if isinstance(column.default.arg, bool):
column_default = ("DEFAULT %r" % int(column.default.arg)) column_default = "DEFAULT {}".format(int(column.default.arg))
else: else:
column_default = ("DEFAULT '%r'" % column.default.arg) column_default = "DEFAULT `{}`".format(column.default.arg)
if isinstance(column.type, JSON): if isinstance(column.type, JSON):
column_type = "JSON" column_type = "JSON"
else: else:
column_type = column.type column_type = column.type
alter_table = "ALTER TABLE %s ADD COLUMN `%s` %s %s" % (orm_class.__tablename__, alter_table = text("ALTER TABLE %s ADD COLUMN `%s` %s %s" % (orm_class.__tablename__,
column_name, column_name,
column_type, column_type,
column_default) column_default))
log.debug(alter_table) log.debug(alter_table)
session.execute(alter_table) session.execute(alter_table)
changed = True changed = True
except json.decoder.JSONDecodeError as e:
log.error("Database corrupt column: {}".format(column_name))
log.debug(e)
if changed: if changed:
try: try:
......
...@@ -121,6 +121,8 @@ class Identifiers(Base): ...@@ -121,6 +121,8 @@ class Identifiers(Base):
return u"Douban" return u"Douban"
elif format_type == "goodreads": elif format_type == "goodreads":
return u"Goodreads" return u"Goodreads"
elif format_type == "babelio":
return u"Babelio"
elif format_type == "google": elif format_type == "google":
return u"Google Books" return u"Google Books"
elif format_type == "kobo": elif format_type == "kobo":
...@@ -148,6 +150,8 @@ class Identifiers(Base): ...@@ -148,6 +150,8 @@ class Identifiers(Base):
return u"https://dx.doi.org/{0}".format(self.val) return u"https://dx.doi.org/{0}".format(self.val)
elif format_type == "goodreads": elif format_type == "goodreads":
return u"https://www.goodreads.com/book/show/{0}".format(self.val) return u"https://www.goodreads.com/book/show/{0}".format(self.val)
elif format_type == "babelio":
return u"https://www.babelio.com/livres/titre/{0}".format(self.val)
elif format_type == "douban": elif format_type == "douban":
return u"https://book.douban.com/subject/{0}".format(self.val) return u"https://book.douban.com/subject/{0}".format(self.val)
elif format_type == "google": elif format_type == "google":
...@@ -702,14 +706,21 @@ class CalibreDB(): ...@@ -702,14 +706,21 @@ class CalibreDB():
return self.session.query(Books) \ return self.session.query(Books) \
.filter(and_(Books.authors.any(and_(*q)), func.lower(Books.title).ilike("%" + title + "%"))).first() .filter(and_(Books.authors.any(and_(*q)), func.lower(Books.title).ilike("%" + title + "%"))).first()
def search_query(self, term): def search_query(self, term, *join):
term.strip().lower() term.strip().lower()
self.session.connection().connection.connection.create_function("lower", 1, lcase) self.session.connection().connection.connection.create_function("lower", 1, lcase)
q = list() q = list()
authorterms = re.split("[, ]+", term) authorterms = re.split("[, ]+", term)
for authorterm in authorterms: for authorterm in authorterms:
q.append(Books.authors.any(func.lower(Authors.name).ilike("%" + authorterm + "%"))) q.append(Books.authors.any(func.lower(Authors.name).ilike("%" + authorterm + "%")))
return self.session.query(Books).filter(self.common_filters(True)).filter( query = self.session.query(Books)
if len(join) == 3:
query = query.outerjoin(join[0], join[1]).outerjoin(join[2])
elif len(join) == 2:
query = query.outerjoin(join[0], join[1])
elif len(join) == 1:
query = query.outerjoin(join[0])
return query.filter(self.common_filters(True)).filter(
or_(Books.tags.any(func.lower(Tags.name).ilike("%" + term + "%")), or_(Books.tags.any(func.lower(Tags.name).ilike("%" + term + "%")),
Books.series.any(func.lower(Series.name).ilike("%" + term + "%")), Books.series.any(func.lower(Series.name).ilike("%" + term + "%")),
Books.authors.any(and_(*q)), Books.authors.any(and_(*q)),
...@@ -718,10 +729,10 @@ class CalibreDB(): ...@@ -718,10 +729,10 @@ class CalibreDB():
)) ))
# read search results from calibre-database and return it (function is used for feed and simple search # read search results from calibre-database and return it (function is used for feed and simple search
def get_search_results(self, term, offset=None, order=None, limit=None): def get_search_results(self, term, offset=None, order=None, limit=None, *join):
order = order or [Books.sort] order = order or [Books.sort]
pagination = None pagination = None
result = self.search_query(term).order_by(*order).all() result = self.search_query(term, *join).order_by(*order).all()
result_count = len(result) result_count = len(result)
if offset != None and limit != None: if offset != None and limit != None:
offset = int(offset) offset = int(offset)
......
...@@ -49,5 +49,5 @@ except ImportError as err: ...@@ -49,5 +49,5 @@ except ImportError as err:
try: try:
from . import gmail from . import gmail
except ImportError as err: except ImportError as err:
log.debug("Cannot import Gmail, sending books via G-Mail Accounts will not work: %s", err) log.debug("Cannot import gmail, sending books via Gmail Oauth2 Verification will not work: %s", err)
gmail = None gmail = None
...@@ -53,6 +53,7 @@ def setup_gmail(token): ...@@ -53,6 +53,7 @@ def setup_gmail(token):
'expiry': creds.expiry.isoformat(), 'expiry': creds.expiry.isoformat(),
'email': user_info 'email': user_info
} }
return {}
def get_user_info(credentials): def get_user_info(credentials):
user_info_service = build(serviceName='oauth2', version='v2',credentials=credentials) user_info_service = build(serviceName='oauth2', version='v2',credentials=credentials)
......
...@@ -84,15 +84,24 @@ body { ...@@ -84,15 +84,24 @@ body {
#progress .bar-load, #progress .bar-load,
#progress .bar-read { #progress .bar-read {
display: flex; display: flex;
align-items: flex-end;
justify-content: flex-end;
position: absolute; position: absolute;
top: 0; top: 0;
left: 0;
bottom: 0; bottom: 0;
transition: width 150ms ease-in-out; transition: width 150ms ease-in-out;
} }
#progress .from-left {
left: 0;
align-items: flex-end;
justify-content: flex-end;
}
#progress .from-right {
right: 0;
align-items: flex-start;
justify-content: flex-start;
}
#progress .bar-load { #progress .bar-load {
color: #000; color: #000;
background-color: #ccc; background-color: #ccc;
......
...@@ -171,7 +171,10 @@ kthoom.ImageFile = function(file) { ...@@ -171,7 +171,10 @@ kthoom.ImageFile = function(file) {
function initProgressClick() { function initProgressClick() {
$("#progress").click(function(e) { $("#progress").click(function(e) {
var page = Math.max(1, Math.ceil((e.offsetX / $(this).width()) * totalImages)) - 1; var offset = $(this).offset();
var x = e.pageX - offset.left;
var rate = settings.direction === 0 ? x / $(this).width() : 1 - x / $(this).width();
var page = Math.max(1, Math.ceil(rate * totalImages)) - 1;
currentImage = page; currentImage = page;
updatePage(); updatePage();
}); });
...@@ -285,6 +288,22 @@ function updatePage() { ...@@ -285,6 +288,22 @@ function updatePage() {
} }
function updateProgress(loadPercentage) { function updateProgress(loadPercentage) {
if (settings.direction === 0) {
$("#progress .bar-read")
.removeClass("from-right")
.addClass("from-left");
$("#progress .bar-load")
.removeClass("from-right")
.addClass("from-left");
} else {
$("#progress .bar-read")
.removeClass("from-left")
.addClass("from-right");
$("#progress .bar-load")
.removeClass("from-left")
.addClass("from-right");
}
// Set the load/unzip progress if it's passed in // Set the load/unzip progress if it's passed in
if (loadPercentage) { if (loadPercentage) {
$("#progress .bar-load").css({ width: loadPercentage + "%" }); $("#progress .bar-load").css({ width: loadPercentage + "%" });
...@@ -526,18 +545,17 @@ function keyHandler(evt) { ...@@ -526,18 +545,17 @@ function keyHandler(evt) {
break; break;
case kthoom.Key.SPACE: case kthoom.Key.SPACE:
var container = $("#mainContent"); var container = $("#mainContent");
var atTop = container.scrollTop() === 0; // var atTop = container.scrollTop() === 0;
var atBottom = container.scrollTop() >= container[0].scrollHeight - container.height(); // var atBottom = container.scrollTop() >= container[0].scrollHeight - container.height();
if (evt.shiftKey && atTop) { if (evt.shiftKey) {
evt.preventDefault(); evt.preventDefault();
// If it's Shift + Space and the container is at the top of the page // If it's Shift + Space and the container is at the top of the page
showLeftPage(); showPrevPage();
} else if (!evt.shiftKey && atBottom) { } else {
evt.preventDefault(); evt.preventDefault();
// If you're at the bottom of the page and you only pressed space // If you're at the bottom of the page and you only pressed space
showRightPage(); showNextPage();
container.scrollTop(0);
} }
break; break;
default: default:
......
...@@ -114,6 +114,20 @@ $(document).ready(function() { ...@@ -114,6 +114,20 @@ $(document).ready(function() {
} }
}); });
$(".session").click(function() {
window.sessionStorage.setItem("back", window.location.pathname);
});
$("#back").click(function() {
var loc = sessionStorage.getItem("back");
if (!loc) {
loc = $(this).data("back");
}
sessionStorage.removeItem("back");
window.location.href = loc;
});
function confirmDialog(id, dialogid, dataValue, yesFn, noFn) { function confirmDialog(id, dialogid, dataValue, yesFn, noFn) {
var $confirm = $("#" + dialogid); var $confirm = $("#" + dialogid);
$confirm.modal('show'); $confirm.modal('show');
......
This diff is collapsed.
...@@ -26,7 +26,7 @@ ...@@ -26,7 +26,7 @@
{% for user in allUser %} {% for user in allUser %}
{% if not user.role_anonymous() or config.config_anonbrowse %} {% if not user.role_anonymous() or config.config_anonbrowse %}
<tr> <tr>
<td><a href="{{url_for('admin.edit_user', user_id=user.id)}}">{{user.name}}</a></td> <td><a class="session" href="{{url_for('admin.edit_user', user_id=user.id)}}">{{user.name}}</a></td>
<td>{{user.email}}</td> <td>{{user.email}}</td>
<td>{{user.kindle_mail}}</td> <td>{{user.kindle_mail}}</td>
<td>{{user.downloads.count()}}</td> <td>{{user.downloads.count()}}</td>
......
...@@ -47,13 +47,13 @@ ...@@ -47,13 +47,13 @@
{{ text_table_row('title', _('Enter Title'),_('Title'), true, true) }} {{ text_table_row('title', _('Enter Title'),_('Title'), true, true) }}
{{ text_table_row('sort', _('Enter Title Sort'),_('Title Sort'), false, true) }} {{ text_table_row('sort', _('Enter Title Sort'),_('Title Sort'), false, true) }}
{{ text_table_row('author_sort', _('Enter Author Sort'),_('Author Sort'), false, true) }} {{ text_table_row('author_sort', _('Enter Author Sort'),_('Author Sort'), false, true) }}
{{ text_table_row('authors', _('Enter Authors'),_('Authors'), true) }} {{ text_table_row('authors', _('Enter Authors'),_('Authors'), true, true) }}
{{ text_table_row('tags', _('Enter Categories'),_('Categories'), false, false) }} {{ text_table_row('tags', _('Enter Categories'),_('Categories'), false, true) }}
{{ text_table_row('series', _('Enter Series'),_('Series'), false, false) }} {{ text_table_row('series', _('Enter Series'),_('Series'), false, true) }}
<th data-field="series_index" id="series_index" data-visible="{{visiblility.get('series_index')}}" data-edit-validate="{{ _('This Field is Required') }}" data-sortable="true" {% if g.user.role_edit() %} data-editable-type="number" data-editable-placeholder="1" data-editable-step="0.01" data-editable-min="0" data-editable-url="{{ url_for('editbook.edit_list_book', param='series_index')}}" data-edit="true" data-editable-title="{{_('Enter title')}}"{% endif %}>{{_('Series Index')}}</th> <th data-field="series_index" id="series_index" data-visible="{{visiblility.get('series_index')}}" data-edit-validate="{{ _('This Field is Required') }}" data-sortable="true" {% if g.user.role_edit() %} data-editable-type="number" data-editable-placeholder="1" data-editable-step="0.01" data-editable-min="0" data-editable-url="{{ url_for('editbook.edit_list_book', param='series_index')}}" data-edit="true" data-editable-title="{{_('Enter title')}}"{% endif %}>{{_('Series Index')}}</th>
{{ text_table_row('languages', _('Enter Languages'),_('Languages'), false, false) }} {{ text_table_row('languages', _('Enter Languages'),_('Languages'), false, true) }}
<!--th data-field="pubdate" data-type="date" data-visible="{{visiblility.get('pubdate')}}" data-viewformat="dd.mm.yyyy" id="pubdate" data-sortable="true">{{_('Publishing Date')}}</th--> <!--th data-field="pubdate" data-type="date" data-visible="{{visiblility.get('pubdate')}}" data-viewformat="dd.mm.yyyy" id="pubdate" data-sortable="true">{{_('Publishing Date')}}</th-->
{{ text_table_row('publishers', _('Enter Publishers'),_('Publishers'), false, false) }} {{ text_table_row('publishers', _('Enter Publishers'),_('Publishers'), false, true) }}
{% if g.user.role_delete_books() and g.user.role_edit()%} {% if g.user.role_delete_books() and g.user.role_edit()%}
<th data-align="right" data-formatter="EbookActions" data-switchable="false">{{_('Delete')}}</th> <th data-align="right" data-formatter="EbookActions" data-switchable="false">{{_('Delete')}}</th>
{% endif %} {% endif %}
......
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
<label for="mail_server_type">{{_('Choose Server Type')}}</label> <label for="mail_server_type">{{_('Choose Server Type')}}</label>
<select name="mail_server_type" id="config_email_type" class="form-control" data-control="email-settings"> <select name="mail_server_type" id="config_email_type" class="form-control" data-control="email-settings">
<option value="0" {% if content.mail_server_type == 0 %}selected{% endif %}>{{_('Use Standard E-Mail Account')}}</option> <option value="0" {% if content.mail_server_type == 0 %}selected{% endif %}>{{_('Use Standard E-Mail Account')}}</option>
<option value="1" {% if content.mail_server_type == 1 %}selected{% endif %}>{{_('G-Mail Account with OAuth2 Verfification')}}</option> <option value="1" {% if content.mail_server_type == 1 %}selected{% endif %}>{{_('Gmail Account with OAuth2 Verfification')}}</option>
</select> </select>
</div> </div>
<div data-related="email-settings-1"> <div data-related="email-settings-1">
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
{% if content.mail_gmail_token == {} %} {% if content.mail_gmail_token == {} %}
<button type="submit" id="gmail_server" name="gmail" value="submit" class="btn btn-default">{{_('Setup Gmail Account as E-Mail Server')}}</button> <button type="submit" id="gmail_server" name="gmail" value="submit" class="btn btn-default">{{_('Setup Gmail Account as E-Mail Server')}}</button>
{% else %} {% else %}
<button type="submit" id="invalidate_server" name="invalidate" value="submit" class="btn btn-danger">{{_('Revoke G-Mail Access')}}</button> <button type="submit" id="invalidate_server" name="invalidate" value="submit" class="btn btn-danger">{{_('Revoke Gmail Access')}}</button>
{% endif %} {% endif %}
</div> </div>
</div> </div>
...@@ -66,7 +66,7 @@ ...@@ -66,7 +66,7 @@
{% if feature_support['gmail'] %} {% if feature_support['gmail'] %}
</div> </div>
{% endif %} {% endif %}
<a href="{{ url_for('admin.admin') }}" id="back" class="btn btn-default">{{_('Back')}}</a> <a href="{{ url_for('admin.admin') }}" id="email_back" class="btn btn-default">{{_('Back')}}</a>
</form> </form>
{% if g.allow_registration %} {% if g.allow_registration %}
<div class="col-md-10 col-lg-6"> <div class="col-md-10 col-lg-6">
......
...@@ -60,12 +60,12 @@ ...@@ -60,12 +60,12 @@
<a id="fullscreen" class="icon-resize-full">Fullscreen</a> <a id="fullscreen" class="icon-resize-full">Fullscreen</a>
</div> </div>
<div id="progress" role="progressbar" class="loading"> <div id="progress" role="progressbar" class="loading">
<div class="bar-load"> <div class="bar-load from-left">
<div class="text load"> <div class="text load">
Loading... Loading...
</div> </div>
</div> </div>
<div class="bar-read"> <div class="bar-read from-left">
<div class="text page"></div> <div class="text page"></div>
</div> </div>
</div> </div>
......
...@@ -129,7 +129,7 @@ ...@@ -129,7 +129,7 @@
<div class="col-sm-12"> <div class="col-sm-12">
<div id="user_submit" class="btn btn-default">{{_('Save')}}</div> <div id="user_submit" class="btn btn-default">{{_('Save')}}</div>
{% if not profile %} {% if not profile %}
<a href="{{ url_for('admin.admin') }}" id="back" class="btn btn-default">{{_('Cancel')}}</a> <div class="btn btn-default" data-back="{{ url_for('admin.admin') }}" id="back">{{_('Cancel')}}</div>
{% endif %} {% endif %}
{% if g.user and g.user.role_admin() and not profile and not new_user and not content.role_anonymous() %} {% if g.user and g.user.role_admin() and not profile and not new_user and not content.role_anonymous() %}
<div class="btn btn-danger" id="btndeluser" data-value="{{ content.id }}" data-remote="false" >{{_('Delete User')}}</div> <div class="btn btn-danger" id="btndeluser" data-value="{{ content.id }}" data-remote="false" >{{_('Delete User')}}</div>
......
...@@ -23,16 +23,18 @@ ...@@ -23,16 +23,18 @@
data-visible="{{element.get(array_field)}}" data-visible="{{element.get(array_field)}}"
data-column="{{value.get(array_field)}}" data-column="{{value.get(array_field)}}"
data-formatter="checkboxFormatter"> data-formatter="checkboxFormatter">
<div class="form-check"> <div class="form-check">
<div>
<label> <label>
<input type="radio" class="check_head" name="options_{{array_field}}" onchange="checkboxHeader('false', '{{parameter}}', {{value.get(array_field)}})" disabled>{{_('Deny')}} <input type="radio" class="check_head" data-val={{value.get(array_field)}} name="options_{{array_field}}" data-name="{{parameter}}" disabled>{{_('Deny')}}
</label> </label>
</div> </div>
<div class="form-check"> <div>
<label> <label>
<input type="radio" class="check_head" name="options_{{array_field}}" onchange="checkboxHeader('true', '{{parameter}}', {{value.get(array_field)}})" disabled>{{_('Allow')}} <input type="radio" class="check_head" data-val={{value.get(array_field)}} name="options_{{array_field}}" data-name="{{parameter}}" disabled>{{_('Allow')}}
</label> </label>
</div> </div>
</div>
{{show_text}} {{show_text}}
</th> </th>
{%- endmacro %} {%- endmacro %}
...@@ -48,14 +50,13 @@ ...@@ -48,14 +50,13 @@
data-editable-source={{url}} data-editable-source={{url}}
{% if validate %}data-edit-validate="{{ _('This Field is Required') }}"{% endif %}> {% if validate %}data-edit-validate="{{ _('This Field is Required') }}"{% endif %}>
<div> <div>
<select id="select_{{ parameter }}" class="header_select" onchange="selectHeader(this, '{{parameter}}')" disabled=""> <select id="select_{{ parameter }}" class="header_select" disabled="">
<option value="all">{{ _('Show All') }}</option> <option value="all">{{ _('Show All') }}</option>
{% for language in languages %} {% for language in languages %}
<option value="{{language.lang_code}}">{{language.name}}</option> <option value="{{language.lang_code}}">{{language.name}}</option>
{% endfor %} {% endfor %}
</select> </select>
</div><br> </div>
{{ show_text }} {{ show_text }}
</th> </th>
{%- endmacro %} {%- endmacro %}
...@@ -71,13 +72,13 @@ ...@@ -71,13 +72,13 @@
data-editable-source={{url}} data-editable-source={{url}}
{% if validate %}data-edit-validate="{{ _('This Field is Required') }}"{% endif %}> {% if validate %}data-edit-validate="{{ _('This Field is Required') }}"{% endif %}>
<div> <div>
<select id="select_{{ parameter }}" class="header_select" onchange="selectHeader(this, '{{parameter}}')" disabled=""> <select id="select_{{ parameter }}" class="header_select" disabled="">
<option value="None">{{_('Select...')}}</option> <option value="None">{{_('Select...')}}</option>
{% for translation in translations %} {% for translation in translations %}
<option value="{{translation}}">{{translation.display_name|capitalize}}</option> <option value="{{translation}}">{{translation.display_name|capitalize}}</option>
{% endfor %} {% endfor %}
</select> </select>
</div><br> </div>
{{ show_text }} {{ show_text }}
</th> </th>
{%- endmacro %} {%- endmacro %}
...@@ -137,6 +138,9 @@ ...@@ -137,6 +138,9 @@
</tr> </tr>
</thead> </thead>
</table> </table>
<div class="errorlink">
<div class="btn btn-default" data-back="{{ url_for('admin.admin') }}" id="back">{{_('Back')}}</div>
</div>
{% endblock %} {% endblock %}
{% block modal %} {% block modal %}
{{ delete_confirm_modal() }} {{ delete_confirm_modal() }}
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
...@@ -75,8 +75,9 @@ def load_user_from_auth_header(header_val): ...@@ -75,8 +75,9 @@ def load_user_from_auth_header(header_val):
basic_username = basic_password = '' # nosec basic_username = basic_password = '' # nosec
try: try:
header_val = base64.b64decode(header_val).decode('utf-8') header_val = base64.b64decode(header_val).decode('utf-8')
basic_username = header_val.split(':')[0] # Users with colon are invalid: rfc7617 page 4
basic_password = header_val.split(':')[1] basic_username = header_val.split(':', 1)[0]
basic_password = header_val.split(':', 1)[1]
except (TypeError, UnicodeDecodeError, binascii.Error): except (TypeError, UnicodeDecodeError, binascii.Error):
pass pass
user = _fetch_user_by_name(basic_username) user = _fetch_user_by_name(basic_username)
......
...@@ -760,10 +760,26 @@ def list_books(): ...@@ -760,10 +760,26 @@ def list_books():
sort = request.args.get("sort", "id") sort = request.args.get("sort", "id")
order = request.args.get("order", "").lower() order = request.args.get("order", "").lower()
state = None state = None
join = tuple()
if sort == "state": if sort == "state":
state = json.loads(request.args.get("state", "[]")) state = json.loads(request.args.get("state", "[]"))
if sort != "state" and order: elif sort == "tags":
order = [db.Tags.name.asc()] if order == "asc" else [db.Tags.name.desc()]
join = db.books_tags_link,db.Books.id == db.books_tags_link.c.book, db.Tags
elif sort == "series":
order = [db.Series.name.asc()] if order == "asc" else [db.Series.name.desc()]
join = db.books_series_link,db.Books.id == db.books_series_link.c.book, db.Series
elif sort == "publishers":
order = [db.Publishers.name.asc()] if order == "asc" else [db.Publishers.name.desc()]
join = db.books_publishers_link,db.Books.id == db.books_publishers_link.c.book, db.Publishers
elif sort == "authors":
order = [db.Authors.name.asc()] if order == "asc" else [db.Authors.name.desc()]
join = db.books_authors_link,db.Books.id == db.books_authors_link.c.book, db.Authors
elif sort == "languages":
order = [db.Languages.lang_code.asc()] if order == "asc" else [db.Languages.lang_code.desc()]
join = db.books_languages_link,db.Books.id == db.books_languages_link.c.book, db.Languages
elif order and sort in ["sort", "title", "authors_sort", "series_index"]:
order = [text(sort + " " + order)] order = [text(sort + " " + order)]
elif not state: elif not state:
order = [db.Books.timestamp.desc()] order = [db.Books.timestamp.desc()]
...@@ -778,9 +794,9 @@ def list_books(): ...@@ -778,9 +794,9 @@ def list_books():
books = calibre_db.session.query(db.Books).filter(calibre_db.common_filters()).all() books = calibre_db.session.query(db.Books).filter(calibre_db.common_filters()).all()
entries = calibre_db.get_checkbox_sorted(books, state, off, limit,order) entries = calibre_db.get_checkbox_sorted(books, state, off, limit,order)
elif search: elif search:
entries, filtered_count, __ = calibre_db.get_search_results(search, off, order, limit) entries, filtered_count, __ = calibre_db.get_search_results(search, off, order, limit, *join)
else: else:
entries, __, __ = calibre_db.fill_indexpage((int(off) / (int(limit)) + 1), limit, db.Books, True, order) entries, __, __ = calibre_db.fill_indexpage((int(off) / (int(limit)) + 1), limit, db.Books, True, order, *join)
for entry in entries: for entry in entries:
for index in range(0, len(entry.languages)): for index in range(0, len(entry.languages)):
......
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