1
0
mirror of https://github.com/janeczku/calibre-web synced 2024-11-28 12:30:00 +00:00

Refactored load read status for web access and opds access

Refactored and removed discover html page
Bugfix show author
Bugfix open dialog in author page
Fix for #2341 (advanced search with linked read column and read column having a higher number than number of available custom columns)
This commit is contained in:
Ozzie Isaacs 2022-03-26 19:35:56 +01:00
parent 14a6e7c42c
commit 32a3c45ee0
13 changed files with 846 additions and 702 deletions

View File

@ -680,6 +680,25 @@ class CalibreDB:
return and_(lang_filter, pos_content_tags_filter, ~neg_content_tags_filter, return and_(lang_filter, pos_content_tags_filter, ~neg_content_tags_filter,
pos_content_cc_filter, ~neg_content_cc_filter, archived_filter) pos_content_cc_filter, ~neg_content_cc_filter, archived_filter)
def generate_linked_query(self, config_read_column, database):
if not config_read_column:
query = (self.session.query(database, ub.ArchivedBook.is_archived, ub.ReadBook.read_status)
.select_from(Books)
.outerjoin(ub.ReadBook,
and_(ub.ReadBook.user_id == int(current_user.id), ub.ReadBook.book_id == Books.id)))
else:
try:
read_column = cc_classes[config_read_column]
query = (self.session.query(database, ub.ArchivedBook.is_archived, read_column.value)
.select_from(Books)
.outerjoin(read_column, read_column.book == Books.id))
except (KeyError, AttributeError, IndexError):
log.error("Custom Column No.{} is not existing in calibre database".format(config_read_column))
# Skip linking read column and return None instead of read status
query = self.session.query(database, None, ub.ArchivedBook.is_archived)
return query.outerjoin(ub.ArchivedBook, and_(Books.id == ub.ArchivedBook.book_id,
int(current_user.id) == ub.ArchivedBook.user_id))
@staticmethod @staticmethod
def get_checkbox_sorted(inputlist, state, offset, limit, order, combo=False): def get_checkbox_sorted(inputlist, state, offset, limit, order, combo=False):
outcome = list() outcome = list()
@ -709,30 +728,14 @@ class CalibreDB:
join_archive_read, config_read_column, *join): join_archive_read, config_read_column, *join):
pagesize = pagesize or self.config.config_books_per_page pagesize = pagesize or self.config.config_books_per_page
if current_user.show_detail_random(): if current_user.show_detail_random():
randm = self.session.query(Books) \ random_query = self.generate_linked_query(config_read_column, database)
.filter(self.common_filters(allow_show_archived)) \ randm = (random_query.filter(self.common_filters(allow_show_archived))
.order_by(func.random()) \ .order_by(func.random())
.limit(self.config.config_random_books).all() .limit(self.config.config_random_books).all())
else: else:
randm = false() randm = false()
if join_archive_read: if join_archive_read:
if not config_read_column: query = self.generate_linked_query(config_read_column, database)
query = (self.session.query(database, ub.ReadBook.read_status, ub.ArchivedBook.is_archived)
.select_from(Books)
.outerjoin(ub.ReadBook,
and_(ub.ReadBook.user_id == int(current_user.id), ub.ReadBook.book_id == Books.id)))
else:
try:
read_column = cc_classes[config_read_column]
query = (self.session.query(database, read_column.value, ub.ArchivedBook.is_archived)
.select_from(Books)
.outerjoin(read_column, read_column.book == Books.id))
except (KeyError, AttributeError, IndexError):
log.error("Custom Column No.{} is not existing in calibre database".format(read_column))
# Skip linking read column and return None instead of read status
query = self.session.query(database, None, ub.ArchivedBook.is_archived)
query = query.outerjoin(ub.ArchivedBook, and_(Books.id == ub.ArchivedBook.book_id,
int(current_user.id) == ub.ArchivedBook.user_id))
else: else:
query = self.session.query(database) query = self.session.query(database)
off = int(int(pagesize) * (page - 1)) off = int(int(pagesize) * (page - 1))
@ -830,21 +833,23 @@ class CalibreDB:
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 + "%")))
if not config_read_column: query = self.generate_linked_query(config_read_column, Books)
'''if not config_read_column:
query = (self.session.query(Books, ub.ArchivedBook.is_archived, ub.ReadBook).select_from(Books) query = (self.session.query(Books, ub.ArchivedBook.is_archived, ub.ReadBook).select_from(Books)
.outerjoin(ub.ReadBook, and_(Books.id == ub.ReadBook.book_id, .outerjoin(ub.ReadBook, and_(Books.id == ub.ReadBook.book_id,
int(current_user.id) == ub.ReadBook.user_id))) int(current_user.id) == ub.ReadBook.user_id)))
else: else:
try: try:
read_column = cc_classes[config_read_column] read_column = cc_classes[config_read_column]
query = (self.session.query(Books, ub.ArchivedBook.is_archived, read_column.value).select_from(Books) query = (self.session.query(Books, ub.ArchivedBook.is_archived, read_column.value)
.select_from(Books)
.outerjoin(read_column, read_column.book == Books.id)) .outerjoin(read_column, read_column.book == Books.id))
except (KeyError, AttributeError, IndexError): except (KeyError, AttributeError, IndexError):
log.error("Custom Column No.{} is not existing in calibre database".format(config_read_column)) log.error("Custom Column No.{} is not existing in calibre database".format(config_read_column))
# Skip linking read column # Skip linking read column
query = self.session.query(Books, ub.ArchivedBook.is_archived, None) query = self.session.query(Books, ub.ArchivedBook.is_archived, None)
query = query.outerjoin(ub.ArchivedBook, and_(Books.id == ub.ArchivedBook.book_id, query = query.outerjoin(ub.ArchivedBook, and_(Books.id == ub.ArchivedBook.book_id,
int(current_user.id) == ub.ArchivedBook.user_id)) int(current_user.id) == ub.ArchivedBook.user_id))'''
if len(join) == 6: if len(join) == 6:
query = query.outerjoin(join[0], join[1]).outerjoin(join[2]).outerjoin(join[3], join[4]).outerjoin(join[5]) query = query.outerjoin(join[0], join[1]).outerjoin(join[2]).outerjoin(join[3], join[4]).outerjoin(join[5])

View File

@ -26,7 +26,8 @@ from functools import wraps
from flask import Blueprint, request, render_template, Response, g, make_response, abort from flask import Blueprint, request, render_template, Response, g, make_response, abort
from flask_login import current_user from flask_login import current_user
from sqlalchemy.sql.expression import func, text, or_, and_, any_, true from sqlalchemy.sql.expression import func, text, or_, and_, true
from sqlalchemy.exc import InvalidRequestError, OperationalError
from werkzeug.security import check_password_hash from werkzeug.security import check_password_hash
from . import constants, logger, config, db, calibre_db, ub, services, get_locale, isoLanguages from . import constants, logger, config, db, calibre_db, ub, services, get_locale, isoLanguages
from .helper import get_download_link, get_book_cover from .helper import get_download_link, get_book_cover
@ -108,7 +109,8 @@ def feed_letter_books(book_id):
entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), 0, entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), 0,
db.Books, db.Books,
letter, letter,
[db.Books.sort]) [db.Books.sort],
True, config.config_read_column)
return render_xml_template('feed.xml', entries=entries, pagination=pagination) return render_xml_template('feed.xml', entries=entries, pagination=pagination)
@ -118,15 +120,16 @@ def feed_letter_books(book_id):
def feed_new(): def feed_new():
off = request.args.get("offset") or 0 off = request.args.get("offset") or 0
entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), 0, entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), 0,
db.Books, True, [db.Books.timestamp.desc()]) db.Books, True, [db.Books.timestamp.desc()],
True, config.config_read_column)
return render_xml_template('feed.xml', entries=entries, pagination=pagination) return render_xml_template('feed.xml', entries=entries, pagination=pagination)
@opds.route("/opds/discover") @opds.route("/opds/discover")
@requires_basic_auth_if_no_ano @requires_basic_auth_if_no_ano
def feed_discover(): def feed_discover():
entries = calibre_db.session.query(db.Books).filter(calibre_db.common_filters()).order_by(func.random())\ query = calibre_db.generate_linked_query(config.config_read_column, db.Books)
.limit(config.config_books_per_page) entries = query.filter(calibre_db.common_filters()).order_by(func.random()).limit(config.config_books_per_page)
pagination = Pagination(1, config.config_books_per_page, int(config.config_books_per_page)) pagination = Pagination(1, config.config_books_per_page, int(config.config_books_per_page))
return render_xml_template('feed.xml', entries=entries, pagination=pagination) return render_xml_template('feed.xml', entries=entries, pagination=pagination)
@ -137,7 +140,8 @@ def feed_best_rated():
off = request.args.get("offset") or 0 off = request.args.get("offset") or 0
entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), 0, entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), 0,
db.Books, db.Books.ratings.any(db.Ratings.rating > 9), db.Books, db.Books.ratings.any(db.Ratings.rating > 9),
[db.Books.timestamp.desc()]) [db.Books.timestamp.desc()],
True, config.config_read_column)
return render_xml_template('feed.xml', entries=entries, pagination=pagination) return render_xml_template('feed.xml', entries=entries, pagination=pagination)
@ -150,11 +154,11 @@ def feed_hot():
hot_books = all_books.offset(off).limit(config.config_books_per_page) hot_books = all_books.offset(off).limit(config.config_books_per_page)
entries = list() entries = list()
for book in hot_books: for book in hot_books:
download_book = calibre_db.get_book(book.Downloads.book_id) query = calibre_db.generate_linked_query(config.config_read_column, db.Books)
download_book = query.filter(calibre_db.common_filters()).filter(
book.Downloads.book_id == db.Books.id).first()
if download_book: if download_book:
entries.append( entries.append(download_book)
calibre_db.get_filtered_book(book.Downloads.book_id)
)
else: else:
ub.delete_download(book.Downloads.book_id) ub.delete_download(book.Downloads.book_id)
num_books = entries.__len__() num_books = entries.__len__()
@ -270,7 +274,8 @@ def feed_series(book_id):
entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), 0, entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), 0,
db.Books, db.Books,
db.Books.series.any(db.Series.id == book_id), db.Books.series.any(db.Series.id == book_id),
[db.Books.series_index]) [db.Books.series_index],
True, config.config_read_column)
return render_xml_template('feed.xml', entries=entries, pagination=pagination) return render_xml_template('feed.xml', entries=entries, pagination=pagination)
@ -324,7 +329,8 @@ def feed_format(book_id):
entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), 0, entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), 0,
db.Books, db.Books,
db.Books.data.any(db.Data.format == book_id.upper()), db.Books.data.any(db.Data.format == book_id.upper()),
[db.Books.timestamp.desc()]) [db.Books.timestamp.desc()],
True, config.config_read_column)
return render_xml_template('feed.xml', entries=entries, pagination=pagination) return render_xml_template('feed.xml', entries=entries, pagination=pagination)
@ -351,7 +357,8 @@ def feed_languages(book_id):
entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), 0, entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), 0,
db.Books, db.Books,
db.Books.languages.any(db.Languages.id == book_id), db.Books.languages.any(db.Languages.id == book_id),
[db.Books.timestamp.desc()]) [db.Books.timestamp.desc()],
True, config.config_read_column)
return render_xml_template('feed.xml', entries=entries, pagination=pagination) return render_xml_template('feed.xml', entries=entries, pagination=pagination)
@ -381,13 +388,25 @@ def feed_shelf(book_id):
result = list() result = list()
# user is allowed to access shelf # user is allowed to access shelf
if shelf: if shelf:
books_in_shelf = ub.session.query(ub.BookShelf).filter(ub.BookShelf.shelf == book_id).order_by( result, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1),
ub.BookShelf.order.asc()).all() config.config_books_per_page,
for book in books_in_shelf: db.Books,
cur_book = calibre_db.get_book(book.book_id) ub.BookShelf.shelf == shelf.id,
result.append(cur_book) [ub.BookShelf.order.asc()],
pagination = Pagination((int(off) / (int(config.config_books_per_page)) + 1), config.config_books_per_page, True, config.config_read_column,
len(result)) ub.BookShelf, ub.BookShelf.book_id == db.Books.id)
# delete shelf entries where book is not existent anymore, can happen if book is deleted outside calibre-web
wrong_entries = calibre_db.session.query(ub.BookShelf) \
.join(db.Books, ub.BookShelf.book_id == db.Books.id, isouter=True) \
.filter(db.Books.id == None).all()
for entry in wrong_entries:
log.info('Not existing book {} in {} deleted'.format(entry.book_id, shelf))
try:
ub.session.query(ub.BookShelf).filter(ub.BookShelf.book_id == entry.book_id).delete()
ub.session.commit()
except (OperationalError, InvalidRequestError) as e:
ub.session.rollback()
log.error_or_exception("Settings Database error: {}".format(e))
return render_xml_template('feed.xml', entries=result, pagination=pagination) return render_xml_template('feed.xml', entries=result, pagination=pagination)
@ -451,8 +470,7 @@ def feed_search(term):
entries, __, ___ = calibre_db.get_search_results(term, config_read_column=config.config_read_column) entries, __, ___ = calibre_db.get_search_results(term, config_read_column=config.config_read_column)
entries_count = len(entries) if len(entries) > 0 else 1 entries_count = len(entries) if len(entries) > 0 else 1
pagination = Pagination(1, entries_count, entries_count) pagination = Pagination(1, entries_count, entries_count)
items = [entry[0] for entry in entries] return render_xml_template('feed.xml', searchterm=term, entries=entries, pagination=pagination)
return render_xml_template('feed.xml', searchterm=term, entries=items, pagination=pagination)
else: else:
return render_xml_template('feed.xml', searchterm="") return render_xml_template('feed.xml', searchterm="")
@ -493,14 +511,16 @@ def render_xml_dataset(data_table, book_id):
entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), 0, entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), 0,
db.Books, db.Books,
getattr(db.Books, data_table.__tablename__).any(data_table.id == book_id), getattr(db.Books, data_table.__tablename__).any(data_table.id == book_id),
[db.Books.timestamp.desc()]) [db.Books.timestamp.desc()],
True, config.config_read_column)
return render_xml_template('feed.xml', entries=entries, pagination=pagination) return render_xml_template('feed.xml', entries=entries, pagination=pagination)
def render_element_index(database_column, linked_table, folder): def render_element_index(database_column, linked_table, folder):
shift = 0 shift = 0
off = int(request.args.get("offset") or 0) off = int(request.args.get("offset") or 0)
entries = calibre_db.session.query(func.upper(func.substr(database_column, 1, 1)).label('id')) entries = calibre_db.session.query(func.upper(func.substr(database_column, 1, 1)).label('id'), None, None)
# query = calibre_db.generate_linked_query(config.config_read_column, db.Books)
if linked_table is not None: if linked_table is not None:
entries = entries.join(linked_table).join(db.Books) entries = entries.join(linked_table).join(db.Books)
entries = entries.filter(calibre_db.common_filters()).group_by(func.upper(func.substr(database_column, 1, 1))).all() entries = entries.filter(calibre_db.common_filters()).group_by(func.upper(func.substr(database_column, 1, 1))).all()

View File

@ -101,7 +101,7 @@ def get_sidebar_config(kwargs=None):
"show_text": _('Show Books List'), "config_show": content}) "show_text": _('Show Books List'), "config_show": content})
return sidebar, simple return sidebar, simple
def get_readbooks_ids(): '''def get_readbooks_ids():
if not config.config_read_column: if not config.config_read_column:
readBooks = ub.session.query(ub.ReadBook).filter(ub.ReadBook.user_id == int(current_user.id))\ readBooks = ub.session.query(ub.ReadBook).filter(ub.ReadBook.user_id == int(current_user.id))\
.filter(ub.ReadBook.read_status == ub.ReadBook.STATUS_FINISHED).all() .filter(ub.ReadBook.read_status == ub.ReadBook.STATUS_FINISHED).all()
@ -113,11 +113,11 @@ def get_readbooks_ids():
return frozenset([x.book for x in readBooks]) return frozenset([x.book for x in readBooks])
except (KeyError, AttributeError, IndexError): except (KeyError, AttributeError, IndexError):
log.error("Custom Column No.{} is not existing in calibre database".format(config.config_read_column)) log.error("Custom Column No.{} is not existing in calibre database".format(config.config_read_column))
return [] return []'''
# Returns the template for rendering and includes the instance name # Returns the template for rendering and includes the instance name
def render_title_template(*args, **kwargs): def render_title_template(*args, **kwargs):
sidebar, simple = get_sidebar_config(kwargs) sidebar, simple = get_sidebar_config(kwargs)
return render_template(instance=config.config_calibre_web_title, sidebar=sidebar, simple=simple, return render_template(instance=config.config_calibre_web_title, sidebar=sidebar, simple=simple,
accept=constants.EXTENSIONS_UPLOAD, read_book_ids=get_readbooks_ids(), accept=constants.EXTENSIONS_UPLOAD, # read_book_ids=get_readbooks_ids(),
*args, **kwargs) *args, **kwargs)

View File

@ -439,7 +439,7 @@ def render_show_shelf(shelf_type, shelf_id, page_no, sort_param):
db.Books, db.Books,
ub.BookShelf.shelf == shelf_id, ub.BookShelf.shelf == shelf_id,
[ub.BookShelf.order.asc()], [ub.BookShelf.order.asc()],
False, 0, True, config.config_read_column,
ub.BookShelf, ub.BookShelf.book_id == db.Books.id) ub.BookShelf, ub.BookShelf.book_id == db.Books.id)
# delete chelf entries where book is not existent anymore, can happen if book is deleted outside calibre-web # delete chelf entries where book is not existent anymore, can happen if book is deleted outside calibre-web
wrong_entries = calibre_db.session.query(ub.BookShelf) \ wrong_entries = calibre_db.session.query(ub.BookShelf) \

View File

@ -31,23 +31,22 @@
<a id="pub_old" data-toggle="tooltip" title="{{_('Sort according to publishing date, oldest first')}}" class="btn btn-primary{% if order == "pubold" %} active{% endif%}" href="{{url_for('web.books_list', data='author', book_id=id, sort_param='pubold')}}"><span class="glyphicon glyphicon-calendar"></span><span class="glyphicon glyphicon-sort-by-order-alt"></span></a> <a id="pub_old" data-toggle="tooltip" title="{{_('Sort according to publishing date, oldest first')}}" class="btn btn-primary{% if order == "pubold" %} active{% endif%}" href="{{url_for('web.books_list', data='author', book_id=id, sort_param='pubold')}}"><span class="glyphicon glyphicon-calendar"></span><span class="glyphicon glyphicon-sort-by-order-alt"></span></a>
</div> </div>
<div class="row display-flex"> <div class="row display-flex">
{% if entries[0] %}
{% for entry in entries %} {% for entry in entries %}
<div id="books" class="col-sm-3 col-lg-2 col-xs-6 book"> <div id="books" class="col-sm-3 col-lg-2 col-xs-6 book">
<div class="cover"> <div class="cover">
<a href="{{ url_for('web.show_book', book_id=entry.id) }}"> <a href="{{ url_for('web.show_book', book_id=entry.Books.id) }}" {% if simple==false %}data-toggle="modal" data-target="#bookDetailsModal" data-remote="false"{% endif %}>
<span class="img" title="{{entry.title}}"> <span class="img" title="{{entry.Books.title}}">
<img src="{{ url_for('web.get_cover', book_id=entry.id) }}" /> <img src="{{ url_for('web.get_cover', book_id=entry.Books.id) }}" />
{% if entry.id in read_book_ids %}<span class="badge read glyphicon glyphicon-ok"></span>{% endif %} {% if entry[2] == True %}<span class="badge read glyphicon glyphicon-ok"></span>{% endif %}
</span> </span>
</a> </a>
</div> </div>
<div class="meta"> <div class="meta">
<a href="{{ url_for('web.show_book', book_id=entry.id) }}"> <a href="{{ url_for('web.show_book', book_id=entry.Books.id) }}" {% if simple==false %}data-toggle="modal" data-target="#bookDetailsModal" data-remote="false"{% endif %}>
<p title="{{ entry.title }}" class="title">{{entry.title|shortentitle}}</p> <p title="{{ entry.Books.title }}" class="title">{{entry.Books.title|shortentitle}}</p>
</a> </a>
<p class="author"> <p class="author">
{% for author in entry.ordered_authors %} {% for author in entry.Books.authors %}
{% if loop.index > g.config_authors_max and g.config_authors_max != 0 %} {% if loop.index > g.config_authors_max and g.config_authors_max != 0 %}
{% if not loop.first %} {% if not loop.first %}
<span class="author-hidden-divider">&amp;</span> <span class="author-hidden-divider">&amp;</span>
@ -63,23 +62,23 @@
<a class="author-name" href="{{url_for('web.books_list', data='author', sort_param='stored', book_id=author.id) }}">{{author.name.replace('|',',')|shortentitle(30)}}</a> <a class="author-name" href="{{url_for('web.books_list', data='author', sort_param='stored', book_id=author.id) }}">{{author.name.replace('|',',')|shortentitle(30)}}</a>
{% endif %} {% endif %}
{% endfor %} {% endfor %}
{% for format in entry.data %} {% for format in entry.Books.data %}
{% if format.format|lower in g.constants.EXTENSIONS_AUDIO %} {% if format.format|lower in g.constants.EXTENSIONS_AUDIO %}
<span class="glyphicon glyphicon-music"></span> <span class="glyphicon glyphicon-music"></span>
{% endif %} {% endif %}
{% endfor %} {% endfor %}
</p> </p>
{% if entry.series.__len__() > 0 %} {% if entry.Books.series.__len__() > 0 %}
<p class="series"> <p class="series">
<a href="{{url_for('web.books_list', data='series', sort_param='stored', book_id=entry.series[0].id )}}"> <a href="{{url_for('web.books_list', data='series', sort_param='stored', book_id=entry.Books.series[0].id )}}">
{{entry.series[0].name}} {{entry.Books.series[0].name}}
</a> </a>
({{entry.series_index|formatseriesindex}}) ({{entry.Books.series_index|formatseriesindex}})
</p> </p>
{% endif %} {% endif %}
{% if entry.ratings.__len__() > 0 %} {% if entry.Books.ratings.__len__() > 0 %}
<div class="rating"> <div class="rating">
{% for number in range((entry.ratings[0].rating/2)|int(2)) %} {% for number in range((entry.Books.ratings[0].rating/2)|int(2)) %}
<span class="glyphicon glyphicon-star good"></span> <span class="glyphicon glyphicon-star good"></span>
{% if loop.last and loop.index < 5 %} {% if loop.last and loop.index < 5 %}
{% for numer in range(5 - loop.index) %} {% for numer in range(5 - loop.index) %}
@ -92,7 +91,6 @@
</div> </div>
</div> </div>
{% endfor %} {% endfor %}
{% endif %}
</div> </div>
</div> </div>
@ -110,7 +108,7 @@
<div class="meta"> <div class="meta">
<p title="{{ entry.title }}" class="title">{{entry.title|shortentitle}}</p> <p title="{{ entry.title }}" class="title">{{entry.title|shortentitle}}</p>
<p class="author"> <p class="author">
{% for author in entry.ordered_authors %} {% for author in entry.authors %}
{% if loop.index > g.config_authors_max and g.config_authors_max != 0 %} {% if loop.index > g.config_authors_max and g.config_authors_max != 0 %}
<a class="author-name author-hidden" href="https://www.goodreads.com/author/show/{{ author.gid }}" target="_blank" rel="noopener">{{author.name.replace('|',',')}}</a> <a class="author-name author-hidden" href="https://www.goodreads.com/author/show/{{ author.gid }}" target="_blank" rel="noopener">{{author.name.replace('|',',')}}</a>
{% if loop.last %} {% if loop.last %}

View File

@ -1,65 +0,0 @@
{% extends "layout.html" %}
{% block body %}
<div class="discover load-more">
<h2>{{title}}</h2>
<div class="row display-flex">
{% for entry in entries %}
<div class="col-sm-3 col-lg-2 col-xs-6 book">
<div class="cover">
{% if entry.has_cover is defined %}
<a href="{{ url_for('web.show_book', book_id=entry.id) }}" {% if simple==false %}data-toggle="modal" data-target="#bookDetailsModal" data-remote="false"{% endif %}>
<span class="img" title="{{entry.title}}">
<img src="{{ url_for('web.get_cover', book_id=entry.id) }}" alt="{{ entry.title }}" />
{% if entry.id in read_book_ids %}<span class="badge read glyphicon glyphicon-ok"></span>{% endif %}
</span>
</a>
{% endif %}
</div>
<div class="meta">
<a href="{{ url_for('web.show_book', book_id=entry.id) }}" {% if simple==false %}data-toggle="modal" data-target="#bookDetailsModal" data-remote="false"{% endif %}>
<p title="{{ entry.title }}" class="title">{{entry.title|shortentitle}}</p>
</a>
<p class="author">
{% for author in entry.ordered_authors %}
{% if loop.index > g.config_authors_max and g.config_authors_max != 0 %}
{% if not loop.first %}
<span class="author-hidden-divider">&amp;</span>
{% endif %}
<a class="author-name author-hidden" href="{{url_for('web.books_list', data='author', sort_param='stored', book_id=author.id) }}">{{author.name.replace('|',',')|shortentitle(30)}}</a>
{% if loop.last %}
<a href="#" class="author-expand" data-authors-max="{{g.config_authors_max}}" data-collapse-caption="({{_('reduce')}})">(...)</a>
{% endif %}
{% else %}
{% if not loop.first %}
<span>&amp;</span>
{% endif %}
<a class="author-name" href="{{url_for('web.books_list', data='author', sort_param='stored', book_id=author.id) }}">{{author.name.replace('|',',')|shortentitle(30)}}</a>
{% endif %}
{% endfor %}
</p>
{% if entry.series.__len__() > 0 %}
<p class="series">
<a href="{{url_for('web.books_list', data='series', sort_param='stored', book_id=entry.series[0].id )}}">
{{entry.series[0].name}}
</a>
({{entry.series_index|formatseriesindex}})
</p>
{% endif %}
{% if entry.ratings.__len__() > 0 %}
<div class="rating">
{% for number in range((entry.ratings[0].rating/2)|int(2)) %}
<span class="glyphicon glyphicon-star good"></span>
{% if loop.last and loop.index < 5 %}
{% for numer in range(5 - loop.index) %}
<span class="glyphicon glyphicon-star-empty"></span>
{% endfor %}
{% endif %}
{% endfor %}
</div>
{% endif %}
</div>
</div>
{% endfor %}
</div>
</div>
{% endblock %}

View File

@ -40,35 +40,35 @@
{% if entries and entries[0] %} {% if entries and entries[0] %}
{% for entry in entries %} {% for entry in entries %}
<entry> <entry>
<title>{{entry.title}}</title> <title>{{entry.Books.title}}</title>
<id>urn:uuid:{{entry.uuid}}</id> <id>urn:uuid:{{entry.Books.uuid}}</id>
<updated>{{entry.atom_timestamp}}</updated> <updated>{{entry.Books.atom_timestamp}}</updated>
{% if entry.authors.__len__() > 0 %} {% if entry.Books.authors.__len__() > 0 %}
<author> <author>
<name>{{entry.authors[0].name}}</name> <name>{{entry.Books.authors[0].name}}</name>
</author> </author>
{% endif %} {% endif %}
{% if entry.publishers.__len__() > 0 %} {% if entry.Books.publishers.__len__() > 0 %}
<publisher> <publisher>
<name>{{entry.publishers[0].name}}</name> <name>{{entry.Books.publishers[0].name}}</name>
</publisher> </publisher>
{% endif %} {% endif %}
{% for lang in entry.languages %} {% for lang in entry.Books.languages %}
<dcterms:language>{{lang.lang_code}}</dcterms:language> <dcterms:language>{{lang.lang_code}}</dcterms:language>
{% endfor %} {% endfor %}
{% for tag in entry.tags %} {% for tag in entry.Books.tags %}
<category scheme="http://www.bisg.org/standards/bisac_subject/index.html" <category scheme="http://www.bisg.org/standards/bisac_subject/index.html"
term="{{tag.name}}" term="{{tag.name}}"
label="{{tag.name}}"/> label="{{tag.name}}"/>
{% endfor %} {% endfor %}
{% if entry.comments[0] %}<summary>{{entry.comments[0].text|striptags}}</summary>{% endif %} {% if entry.Books.comments[0] %}<summary>{{entry.Books.comments[0].text|striptags}}</summary>{% endif %}
{% if entry.has_cover %} {% if entry.Books.has_cover %}
<link type="image/jpeg" href="{{url_for('opds.feed_get_cover', book_id=entry.id)}}" rel="http://opds-spec.org/image"/> <link type="image/jpeg" href="{{url_for('opds.feed_get_cover', book_id=entry.Books.id)}}" rel="http://opds-spec.org/image"/>
<link type="image/jpeg" href="{{url_for('opds.feed_get_cover', book_id=entry.id)}}" rel="http://opds-spec.org/image/thumbnail"/> <link type="image/jpeg" href="{{url_for('opds.feed_get_cover', book_id=entry.Books.id)}}" rel="http://opds-spec.org/image/thumbnail"/>
{% endif %} {% endif %}
{% for format in entry.data %} {% for format in entry.Books.data %}
<link rel="http://opds-spec.org/acquisition" href="{{ url_for('opds.opds_download_link', book_id=entry.id, book_format=format.format|lower)}}" <link rel="http://opds-spec.org/acquisition" href="{{ url_for('opds.opds_download_link', book_id=entry.Books.id, book_format=format.format|lower)}}"
length="{{format.uncompressed_size}}" mtime="{{entry.atom_timestamp}}" type="{{format.format|lower|mimetype}}"/> length="{{format.uncompressed_size}}" mtime="{{entry.Books.atom_timestamp}}" type="{{format.format|lower|mimetype}}"/>
{% endfor %} {% endfor %}
</entry> </entry>
{% endfor %} {% endfor %}

View File

@ -1,25 +1,25 @@
{% extends "layout.html" %} {% extends "layout.html" %}
{% block body %} {% block body %}
{% if g.user.show_detail_random() %} {% if g.user.show_detail_random() and page != "discover" %}
<div class="discover random-books"> <div class="discover random-books">
<h2 class="random-books">{{_('Discover (Random Books)')}}</h2> <h2 class="random-books">{{_('Discover (Random Books)')}}</h2>
<div class="row display-flex"> <div class="row display-flex">
{% for entry in random %} {% for entry in random %}
<div class="col-sm-3 col-lg-2 col-xs-6 book" id="books_rand"> <div class="col-sm-3 col-lg-2 col-xs-6 book" id="books_rand">
<div class="cover"> <div class="cover">
<a href="{{ url_for('web.show_book', book_id=entry.id) }}" {% if simple==false %}data-toggle="modal" data-target="#bookDetailsModal" data-remote="false"{% endif %}> <a href="{{ url_for('web.show_book', book_id=entry.Books.id) }}" {% if simple==false %}data-toggle="modal" data-target="#bookDetailsModal" data-remote="false"{% endif %}>
<span class="img" title="{{ entry.title }}"> <span class="img" title="{{ entry.Books.title }}">
<img src="{{ url_for('web.get_cover', book_id=entry.id) }}" alt="{{ entry.title }}" /> <img src="{{ url_for('web.get_cover', book_id=entry.Books.id) }}" alt="{{ entry.Books.title }}" />
{% if entry.id in read_book_ids %}<span class="badge read glyphicon glyphicon-ok"></span>{% endif %} {% if entry[2] == True %}<span class="badge read glyphicon glyphicon-ok"></span>{% endif %}
</span> </span>
</a> </a>
</div> </div>
<div class="meta"> <div class="meta">
<a href="{{ url_for('web.show_book', book_id=entry.id) }}" {% if simple==false %}data-toggle="modal" data-target="#bookDetailsModal" data-remote="false"{% endif %}> <a href="{{ url_for('web.show_book', book_id=entry.Books.id) }}" {% if simple==false %}data-toggle="modal" data-target="#bookDetailsModal" data-remote="false"{% endif %}>
<p title="{{entry.title}}" class="title">{{entry.title|shortentitle}}</p> <p title="{{entry.Books.title}}" class="title">{{entry.Books.title|shortentitle}}</p>
</a> </a>
<p class="author"> <p class="author">
{% for author in entry.ordered_authors %} {% for author in entry.Books.authors %}
{% if loop.index > g.config_authors_max and g.config_authors_max != 0 %} {% if loop.index > g.config_authors_max and g.config_authors_max != 0 %}
{% if not loop.first %} {% if not loop.first %}
<span class="author-hidden-divider">&amp;</span> <span class="author-hidden-divider">&amp;</span>
@ -36,17 +36,17 @@
{% endif %} {% endif %}
{% endfor %} {% endfor %}
</p> </p>
{% if entry.series.__len__() > 0 %} {% if entry.Books.series.__len__() > 0 %}
<p class="series"> <p class="series">
<a href="{{url_for('web.books_list', data='series', sort_param='stored', book_id=entry.series[0].id )}}"> <a href="{{url_for('web.books_list', data='series', sort_param='stored', book_id=entry.Books.series[0].id )}}">
{{entry.series[0].name}} {{entry.Books.series[0].name}}
</a> </a>
({{entry.series_index|formatseriesindex}}) ({{entry.Books.series_index|formatseriesindex}})
</p> </p>
{% endif %} {% endif %}
{% if entry.ratings.__len__() > 0 %} {% if entry.Books.ratings.__len__() > 0 %}
<div class="rating"> <div class="rating">
{% for number in range((entry.ratings[0].rating/2)|int(2)) %} {% for number in range((entry.Books.ratings[0].rating/2)|int(2)) %}
<span class="glyphicon glyphicon-star good"></span> <span class="glyphicon glyphicon-star good"></span>
{% if loop.last and loop.index < 5 %} {% if loop.last and loop.index < 5 %}
{% for numer in range(5 - loop.index) %} {% for numer in range(5 - loop.index) %}
@ -64,6 +64,7 @@
{% endif %} {% endif %}
<div class="discover load-more"> <div class="discover load-more">
<h2 class="{{title}}">{{title}}</h2> <h2 class="{{title}}">{{title}}</h2>
{% if page != 'discover' %}
<div class="filterheader hidden-xs"> <div class="filterheader hidden-xs">
{% if page == 'hot' %} {% if page == 'hot' %}
<a data-toggle="tooltip" title="{{_('Sort ascending according to download count')}}" id="hot_asc" class="btn btn-primary{% if order == "hotasc" %} active{% endif%}" href="{{url_for('web.books_list', data=page, book_id=id, sort_param='hotasc')}}"><span class="glyphicon glyphicon-sort-by-order"></span></a> <a data-toggle="tooltip" title="{{_('Sort ascending according to download count')}}" id="hot_asc" class="btn btn-primary{% if order == "hotasc" %} active{% endif%}" href="{{url_for('web.books_list', data=page, book_id=id, sort_param='hotasc')}}"><span class="glyphicon glyphicon-sort-by-order"></span></a>
@ -83,25 +84,25 @@
{% endif %} {% endif %}
{% endif %} {% endif %}
</div> </div>
{% endif %}
<div class="row display-flex"> <div class="row display-flex">
{% if entries[0] %} {% if entries[0] %}
{% for entry in entries %} {% for entry in entries %}
<div class="col-sm-3 col-lg-2 col-xs-6 book" id="books"> <div class="col-sm-3 col-lg-2 col-xs-6 book" id="books">
<div class="cover"> <div class="cover">
<a href="{{ url_for('web.show_book', book_id=entry.id) }}" {% if simple==false %}data-toggle="modal" data-target="#bookDetailsModal" data-remote="false"{% endif %}> <a href="{{ url_for('web.show_book', book_id=entry.Books.id) }}" {% if simple==false %}data-toggle="modal" data-target="#bookDetailsModal" data-remote="false"{% endif %}>
<span class="img" title="{{ entry.title }}"> <span class="img" title="{{ entry.Books.title }}">
<img src="{{ url_for('web.get_cover', book_id=entry.id) }}" alt="{{ entry.title }}"/> <img src="{{ url_for('web.get_cover', book_id=entry.Books.id) }}" alt="{{ entry.Books.title }}"/>
{% if entry.id in read_book_ids %}<span class="badge read glyphicon glyphicon-ok"></span>{% endif %} {% if entry[2] == True %}<span class="badge read glyphicon glyphicon-ok"></span>{% endif %}
</span> </span>
</a> </a>
</div> </div>
<div class="meta"> <div class="meta">
<a href="{{ url_for('web.show_book', book_id=entry.id) }}" {% if simple==false %}data-toggle="modal" data-target="#bookDetailsModal" data-remote="false"{% endif %}> <a href="{{ url_for('web.show_book', book_id=entry.Books.id) }}" {% if simple==false %}data-toggle="modal" data-target="#bookDetailsModal" data-remote="false"{% endif %}>
<p title="{{ entry.title }}" class="title">{{entry.title|shortentitle}}</p> <p title="{{ entry.Books.title }}" class="title">{{entry.Books.title|shortentitle}}</p>
</a> </a>
<p class="author"> <p class="author">
{% for author in entry.ordered_authors %} {% for author in entry.Books.authors %}
{% if loop.index > g.config_authors_max and g.config_authors_max != 0 %} {% if loop.index > g.config_authors_max and g.config_authors_max != 0 %}
{% if not loop.first %} {% if not loop.first %}
<span class="author-hidden-divider">&amp;</span> <span class="author-hidden-divider">&amp;</span>
@ -117,27 +118,27 @@
<a class="author-name" href="{{url_for('web.books_list', data='author', book_id=author.id, sort_param='stored') }}">{{author.name.replace('|',',')|shortentitle(30)}}</a> <a class="author-name" href="{{url_for('web.books_list', data='author', book_id=author.id, sort_param='stored') }}">{{author.name.replace('|',',')|shortentitle(30)}}</a>
{% endif %} {% endif %}
{% endfor %} {% endfor %}
{% for format in entry.data %} {% for format in entry.Books.data %}
{% if format.format|lower in g.constants.EXTENSIONS_AUDIO %} {% if format.format|lower in g.constants.EXTENSIONS_AUDIO %}
<span class="glyphicon glyphicon-music"></span> <span class="glyphicon glyphicon-music"></span>
{% endif %} {% endif %}
{%endfor%} {%endfor%}
</p> </p>
{% if entry.series.__len__() > 0 %} {% if entry.Books.series.__len__() > 0 %}
<p class="series"> <p class="series">
{% if page != "series" %} {% if page != "series" %}
<a href="{{url_for('web.books_list', data='series', sort_param='stored', book_id=entry.series[0].id )}}"> <a href="{{url_for('web.books_list', data='series', sort_param='stored', book_id=entry.Books.series[0].id )}}">
{{entry.series[0].name}} {{entry.Books.series[0].name}}
</a> </a>
{% else %} {% else %}
<span>{{entry.series[0].name}}</span> <span>{{entry.Books.series[0].name}}</span>
{% endif %} {% endif %}
({{entry.series_index|formatseriesindex}}) ({{entry.Books.series_index|formatseriesindex}})
</p> </p>
{% endif %} {% endif %}
{% if entry.ratings.__len__() > 0 %} {% if entry.Books.ratings.__len__() > 0 %}
<div class="rating"> <div class="rating">
{% for number in range((entry.ratings[0].rating/2)|int(2)) %} {% for number in range((entry.Books.ratings[0].rating/2)|int(2)) %}
<span class="glyphicon glyphicon-star good"></span> <span class="glyphicon glyphicon-star good"></span>
{% if loop.last and loop.index < 5 %} {% if loop.last and loop.index < 5 %}
{% for numer in range(5 - loop.index) %} {% for numer in range(5 - loop.index) %}

View File

@ -46,7 +46,7 @@
<a href="{{ url_for('web.show_book', book_id=entry.Books.id) }}" {% if simple==false %}data-toggle="modal" data-target="#bookDetailsModal" data-remote="false"{% endif %}> <a href="{{ url_for('web.show_book', book_id=entry.Books.id) }}" {% if simple==false %}data-toggle="modal" data-target="#bookDetailsModal" data-remote="false"{% endif %}>
<span class="img" title="{{entry.Books.title}}" > <span class="img" title="{{entry.Books.title}}" >
<img src="{{ url_for('web.get_cover', book_id=entry.Books.id) }}" alt="{{ entry.Books.title }}" /> <img src="{{ url_for('web.get_cover', book_id=entry.Books.id) }}" alt="{{ entry.Books.title }}" />
{% if entry.Books.id in read_book_ids %}<span class="badge read glyphicon glyphicon-ok"></span>{% endif %} {% if entry[2] == True %}<span class="badge read glyphicon glyphicon-ok"></span>{% endif %}
</span> </span>
</a> </a>
{% endif %} {% endif %}

View File

@ -32,19 +32,19 @@
{% for entry in entries %} {% for entry in entries %}
<div class="col-sm-3 col-lg-2 col-xs-6 book"> <div class="col-sm-3 col-lg-2 col-xs-6 book">
<div class="cover"> <div class="cover">
<a href="{{ url_for('web.show_book', book_id=entry.id) }}" {% if simple==false %}data-toggle="modal" data-target="#bookDetailsModal" data-remote="false"{% endif %}> <a href="{{ url_for('web.show_book', book_id=entry.Books.id) }}" {% if simple==false %}data-toggle="modal" data-target="#bookDetailsModal" data-remote="false"{% endif %}>
<span class="img" title="{{entry.title}}" > <span class="img" title="{{entry.Books.title}}" >
<img src="{{ url_for('web.get_cover', book_id=entry.id) }}" alt="{{ entry.title }}" /> <img src="{{ url_for('web.get_cover', book_id=entry.Books.id) }}" alt="{{ entry.Books.title }}" />
{% if entry.id in read_book_ids %}<span class="badge read glyphicon glyphicon-ok"></span>{% endif %} {% if entry[2] == True %}<span class="badge read glyphicon glyphicon-ok"></span>{% endif %}
</span> </span>
</a> </a>
</div> </div>
<div class="meta"> <div class="meta">
<a href="{{ url_for('web.show_book', book_id=entry.id) }}" {% if simple==false %}data-toggle="modal" data-target="#bookDetailsModal" data-remote="false"{% endif %}> <a href="{{ url_for('web.show_book', book_id=entry.Books.id) }}" {% if simple==false %}data-toggle="modal" data-target="#bookDetailsModal" data-remote="false"{% endif %}>
<p title="{{entry.title}}" class="title">{{entry.title|shortentitle}}</p> <p title="{{entry.Books.title}}" class="title">{{entry.Books.title|shortentitle}}</p>
</a> </a>
<p class="author"> <p class="author">
{% for author in entry.ordered_authors %} {% for author in entry.Books.authors %}
{% if loop.index > g.config_authors_max and g.config_authors_max != 0 %} {% if loop.index > g.config_authors_max and g.config_authors_max != 0 %}
{% if not loop.first %} {% if not loop.first %}
<span class="author-hidden-divider">&amp;</span> <span class="author-hidden-divider">&amp;</span>
@ -61,17 +61,17 @@
{% endif %} {% endif %}
{% endfor %} {% endfor %}
</p> </p>
{% if entry.series.__len__() > 0 %} {% if entry.Books.series.__len__() > 0 %}
<p class="series"> <p class="series">
<a href="{{url_for('web.books_list', data='series', sort_param='stored', book_id=entry.series[0].id )}}"> <a href="{{url_for('web.books_list', data='series', sort_param='stored', book_id=entry.Books.series[0].id )}}">
{{entry.series[0].name}} {{entry.Books.series[0].name}}
</a> </a>
({{entry.series_index|formatseriesindex}}) ({{entry.Books.series_index|formatseriesindex}})
</p> </p>
{% endif %} {% endif %}
{% if entry.ratings.__len__() > 0 %} {% if entry.Books.ratings.__len__() > 0 %}
<div class="rating"> <div class="rating">
{% for number in range((entry.ratings[0].rating/2)|int(2)) %} {% for number in range((entry.Books.ratings[0].rating/2)|int(2)) %}
<span class="glyphicon glyphicon-star good"></span> <span class="glyphicon glyphicon-star good"></span>
{% if loop.last and loop.index < 5 %} {% if loop.last and loop.index < 5 %}
{% for numer in range(5 - loop.index) %} {% for numer in range(5 - loop.index) %}

View File

@ -35,31 +35,31 @@
<div class="col-sm-3 col-lg-2 col-xs-6 book"> <div class="col-sm-3 col-lg-2 col-xs-6 book">
<div class="meta"> <div class="meta">
<p title="{{entry.title}}" class="title">{{entry.title|shortentitle}}</p> <p title="{{entry.Books.title}}" class="title">{{entry.Books.title|shortentitle}}</p>
<p class="author"> <p class="author">
{% for author in entry.ordered_authors %} {% for author in entry.Books.authors %}
<a href="{{url_for('web.books_list', data='author', sort_param='stored', book_id=author.id) }}">{{author.name.replace('|',',')}}</a> <a href="{{url_for('web.books_list', data='author', sort_param='stored', book_id=author.id) }}">{{author.name.replace('|',',')}}</a>
{% if not loop.last %} {% if not loop.last %}
&amp; &amp;
{% endif %} {% endif %}
{% endfor %} {% endfor %}
</p> </p>
{% if entry.series.__len__() > 0 %} {% if entry.Books.series.__len__() > 0 %}
<p class="series"> <p class="series">
<a href="{{url_for('web.books_list', data='series', sort_param='stored', book_id=entry.series[0].id )}}"> <a href="{{url_for('web.books_list', data='series', sort_param='stored', book_id=entry.Books.series[0].id )}}">
{{entry.series[0].name}} {{entry.Books.series[0].name}}
</a> </a>
({{entry.series_index}}) ({{entry.Books.series_index}})
</p> </p>
{% endif %} {% endif %}
</div> </div>
<div class="btn-group" role="group" aria-label="Download, send to Kindle, reading"> <div class="btn-group" role="group" aria-label="Download, send to Kindle, reading">
{% if g.user.role_download() %} {% if g.user.role_download() %}
{% if entry.data|length %} {% if entry.Books.data|length %}
<div class="btn-group" role="group"> <div class="btn-group" role="group">
{% for format in entry.data %} {% for format in entry.Books.data %}
<a href="{{ url_for('web.download_link', book_id=entry.id, book_format=format.format|lower, anyname=entry.id|string+'.'+format.format|lower) }}" id="btnGroupDrop{{entry.id}}{{format.format|lower}}" class="btn btn-primary" role="button"> <a href="{{ url_for('web.download_link', book_id=entry.Books.id, book_format=format.format|lower, anyname=entry.Books.id|string+'.'+format.format|lower) }}" id="btnGroupDrop{{entry.Books.id}}{{format.format|lower}}" class="btn btn-primary" role="button">
<span class="glyphicon glyphicon-download"></span>{{format.format}} ({{ format.uncompressed_size|filesizeformat }}) <span class="glyphicon glyphicon-download"></span>{{format.format}} ({{ format.uncompressed_size|filesizeformat }})
</a> </a>
{% endfor %} {% endfor %}

View File

@ -85,7 +85,10 @@ except ImportError:
def add_security_headers(resp): def add_security_headers(resp):
csp = "default-src 'self'" csp = "default-src 'self'"
csp += ''.join([' ' + host for host in config.config_trustedhosts.strip().split(',')]) csp += ''.join([' ' + host for host in config.config_trustedhosts.strip().split(',')])
csp += " 'unsafe-inline' 'unsafe-eval'; font-src 'self' data:; img-src 'self' data:" csp += " 'unsafe-inline' 'unsafe-eval'; font-src 'self' data:; img-src 'self' "
if request.path.startswith("/author/") and config.config_use_goodreads:
csp += "images.gr-assets.com i.gr-assets.com s.gr-assets.com"
csp += " data:"
resp.headers['Content-Security-Policy'] = csp resp.headers['Content-Security-Policy'] = csp
if request.endpoint == "edit-book.show_edit_book" or config.config_use_google_drive: if request.endpoint == "edit-book.show_edit_book" or config.config_use_google_drive:
resp.headers['Content-Security-Policy'] += " *" resp.headers['Content-Security-Policy'] += " *"
@ -350,7 +353,7 @@ def render_books_list(data, sort_param, book_id, page):
if data == "rated": if data == "rated":
return render_rated_books(page, book_id, order=order) return render_rated_books(page, book_id, order=order)
elif data == "discover": elif data == "discover":
return render_discover_books(page, book_id) return render_discover_books(book_id)
elif data == "unread": elif data == "unread":
return render_read_books(page, False, order=order) return render_read_books(page, False, order=order)
elif data == "read": elif data == "read":
@ -386,7 +389,7 @@ def render_books_list(data, sort_param, book_id, page):
else: else:
website = data or "newest" website = data or "newest"
entries, random, pagination = calibre_db.fill_indexpage(page, 0, db.Books, True, order[0], entries, random, pagination = calibre_db.fill_indexpage(page, 0, db.Books, True, order[0],
False, 0, True, config.config_read_column,
db.books_series_link, db.books_series_link,
db.Books.id == db.books_series_link.c.book, db.Books.id == db.books_series_link.c.book,
db.Series) db.Series)
@ -400,7 +403,7 @@ def render_rated_books(page, book_id, order):
db.Books, db.Books,
db.Books.ratings.any(db.Ratings.rating > 9), db.Books.ratings.any(db.Ratings.rating > 9),
order[0], order[0],
False, 0, True, config.config_read_column,
db.books_series_link, db.books_series_link,
db.Books.id == db.books_series_link.c.book, db.Books.id == db.books_series_link.c.book,
db.Series) db.Series)
@ -411,11 +414,13 @@ def render_rated_books(page, book_id, order):
abort(404) abort(404)
def render_discover_books(page, book_id): def render_discover_books(book_id):
if current_user.check_visibility(constants.SIDEBAR_RANDOM): if current_user.check_visibility(constants.SIDEBAR_RANDOM):
entries, __, pagination = calibre_db.fill_indexpage(page, 0, db.Books, True, [func.randomblob(2)]) entries, __, ___ = calibre_db.fill_indexpage(1, 0, db.Books, True, [func.randomblob(2)],
join_archive_read=True,
config_read_column=config.config_read_column)
pagination = Pagination(1, config.config_books_per_page, config.config_books_per_page) pagination = Pagination(1, config.config_books_per_page, config.config_books_per_page)
return render_title_template('discover.html', entries=entries, pagination=pagination, id=book_id, return render_title_template('index.html', random=false(), entries=entries, pagination=pagination, id=book_id,
title=_(u"Discover (Random Books)"), page="discover") title=_(u"Discover (Random Books)"), page="discover")
else: else:
abort(404) abort(404)
@ -429,18 +434,22 @@ def render_hot_books(page, order):
# order[0][0].compare(func.count(ub.Downloads.book_id).asc())): # order[0][0].compare(func.count(ub.Downloads.book_id).asc())):
order = [func.count(ub.Downloads.book_id).desc()], 'hotdesc' order = [func.count(ub.Downloads.book_id).desc()], 'hotdesc'
if current_user.show_detail_random(): if current_user.show_detail_random():
random = calibre_db.session.query(db.Books).filter(calibre_db.common_filters()) \ random_query = calibre_db.generate_linked_query(config.config_read_column, db.Books)
.order_by(func.random()).limit(config.config_random_books) random = (random_query.filter(calibre_db.common_filters())
.order_by(func.random())
.limit(config.config_random_books).all())
else: else:
random = false() random = false()
off = int(int(config.config_books_per_page) * (page - 1)) off = int(int(config.config_books_per_page) * (page - 1))
all_books = ub.session.query(ub.Downloads, func.count(ub.Downloads.book_id)) \ all_books = ub.session.query(ub.Downloads, func.count(ub.Downloads.book_id)) \
.order_by(*order[0]).group_by(ub.Downloads.book_id) .order_by(*order[0]).group_by(ub.Downloads.book_id)
hot_books = all_books.offset(off).limit(config.config_books_per_page) hot_books = all_books.offset(off).limit(config.config_books_per_page)
entries = list() entries = list()
for book in hot_books: for book in hot_books:
download_book = calibre_db.session.query(db.Books).filter(calibre_db.common_filters()).filter( query = calibre_db.generate_linked_query(config.config_read_column, db.Books)
db.Books.id == book.Downloads.book_id).first() download_book = query.filter(calibre_db.common_filters()).filter(
book.Downloads.book_id == db.Books.id).first()
if download_book: if download_book:
entries.append(download_book) entries.append(download_book)
else: else:
@ -459,26 +468,20 @@ def render_downloaded_books(page, order, user_id):
else: else:
user_id = current_user.id user_id = current_user.id
if current_user.check_visibility(constants.SIDEBAR_DOWNLOAD): if current_user.check_visibility(constants.SIDEBAR_DOWNLOAD):
if current_user.show_detail_random(): entries, random, pagination = calibre_db.fill_indexpage(page,
random = calibre_db.session.query(db.Books).filter(calibre_db.common_filters()) \
.order_by(func.random()).limit(config.config_random_books)
else:
random = false()
entries, __, pagination = calibre_db.fill_indexpage(page,
0, 0,
db.Books, db.Books,
ub.Downloads.user_id == user_id, ub.Downloads.user_id == user_id,
order[0], order[0],
False, 0, True, config.config_read_column,
db.books_series_link, db.books_series_link,
db.Books.id == db.books_series_link.c.book, db.Books.id == db.books_series_link.c.book,
db.Series, db.Series,
ub.Downloads, db.Books.id == ub.Downloads.book_id) ub.Downloads, db.Books.id == ub.Downloads.book_id)
for book in entries: for book in entries:
if not calibre_db.session.query(db.Books).\ if not (calibre_db.session.query(db.Books).filter(calibre_db.common_filters())
filter(calibre_db.common_filters()).filter(db.Books.id == book.id).first(): .filter(db.Books.id == book.Books.id).first()):
ub.delete_download(book.id) ub.delete_download(book.Books.id)
user = ub.session.query(ub.User).filter(ub.User.id == user_id).first() user = ub.session.query(ub.User).filter(ub.User.id == user_id).first()
return render_title_template('index.html', return render_title_template('index.html',
random=random, random=random,
@ -497,9 +500,9 @@ def render_author_books(page, author_id, order):
db.Books, db.Books,
db.Books.authors.any(db.Authors.id == author_id), db.Books.authors.any(db.Authors.id == author_id),
[order[0][0], db.Series.name, db.Books.series_index], [order[0][0], db.Series.name, db.Books.series_index],
False, 0, True, config.config_read_column,
db.books_series_link, db.books_series_link,
db.Books.id == db.books_series_link.c.book, db.books_series_link.c.book == db.Books.id,
db.Series) db.Series)
if entries is None or not len(entries): if entries is None or not len(entries):
flash(_(u"Oops! Selected book title is unavailable. File does not exist or is not accessible"), flash(_(u"Oops! Selected book title is unavailable. File does not exist or is not accessible"),
@ -515,7 +518,8 @@ def render_author_books(page, author_id, order):
other_books = [] other_books = []
if services.goodreads_support and config.config_use_goodreads: if services.goodreads_support and config.config_use_goodreads:
author_info = services.goodreads_support.get_author_info(author_name) author_info = services.goodreads_support.get_author_info(author_name)
other_books = services.goodreads_support.get_other_books(author_info, entries) book_entries = [entry.Books for entry in entries]
other_books = services.goodreads_support.get_other_books(author_info, book_entries)
return render_title_template('author.html', entries=entries, pagination=pagination, id=author_id, return render_title_template('author.html', entries=entries, pagination=pagination, id=author_id,
title=_(u"Author: %(name)s", name=author_name), author=author_info, title=_(u"Author: %(name)s", name=author_name), author=author_info,
other_books=other_books, page="author", order=order[1]) other_books=other_books, page="author", order=order[1])
@ -528,7 +532,7 @@ def render_publisher_books(page, book_id, order):
db.Books, db.Books,
db.Books.publishers.any(db.Publishers.id == book_id), db.Books.publishers.any(db.Publishers.id == book_id),
[db.Series.name, order[0][0], db.Books.series_index], [db.Series.name, order[0][0], db.Books.series_index],
False, 0, True, config.config_read_column,
db.books_series_link, db.books_series_link,
db.Books.id == db.books_series_link.c.book, db.Books.id == db.books_series_link.c.book,
db.Series) db.Series)
@ -546,7 +550,8 @@ def render_series_books(page, book_id, order):
entries, random, pagination = calibre_db.fill_indexpage(page, 0, entries, random, pagination = calibre_db.fill_indexpage(page, 0,
db.Books, db.Books,
db.Books.series.any(db.Series.id == book_id), db.Books.series.any(db.Series.id == book_id),
[order[0][0]]) [order[0][0]],
True, config.config_read_column)
return render_title_template('index.html', random=random, pagination=pagination, entries=entries, id=book_id, return render_title_template('index.html', random=random, pagination=pagination, entries=entries, id=book_id,
title=_(u"Series: %(serie)s", serie=name.name), page="series", order=order[1]) title=_(u"Series: %(serie)s", serie=name.name), page="series", order=order[1])
else: else:
@ -558,7 +563,8 @@ def render_ratings_books(page, book_id, order):
entries, random, pagination = calibre_db.fill_indexpage(page, 0, entries, random, pagination = calibre_db.fill_indexpage(page, 0,
db.Books, db.Books,
db.Books.ratings.any(db.Ratings.id == book_id), db.Books.ratings.any(db.Ratings.id == book_id),
[order[0][0]]) [order[0][0]],
True, config.config_read_column)
if name and name.rating <= 10: if name and name.rating <= 10:
return render_title_template('index.html', random=random, pagination=pagination, entries=entries, id=book_id, return render_title_template('index.html', random=random, pagination=pagination, entries=entries, id=book_id,
title=_(u"Rating: %(rating)s stars", rating=int(name.rating / 2)), title=_(u"Rating: %(rating)s stars", rating=int(name.rating / 2)),
@ -574,7 +580,8 @@ def render_formats_books(page, book_id, order):
entries, random, pagination = calibre_db.fill_indexpage(page, 0, entries, random, pagination = calibre_db.fill_indexpage(page, 0,
db.Books, db.Books,
db.Books.data.any(db.Data.format == book_id.upper()), db.Books.data.any(db.Data.format == book_id.upper()),
[order[0][0]]) [order[0][0]],
True, config.config_read_column)
return render_title_template('index.html', random=random, pagination=pagination, entries=entries, id=book_id, return render_title_template('index.html', random=random, pagination=pagination, entries=entries, id=book_id,
title=_(u"File format: %(format)s", format=name.format), title=_(u"File format: %(format)s", format=name.format),
page="formats", page="formats",
@ -590,7 +597,7 @@ def render_category_books(page, book_id, order):
db.Books, db.Books,
db.Books.tags.any(db.Tags.id == book_id), db.Books.tags.any(db.Tags.id == book_id),
[order[0][0], db.Series.name, db.Books.series_index], [order[0][0], db.Series.name, db.Books.series_index],
False, 0, True, config.config_read_column,
db.books_series_link, db.books_series_link,
db.Books.id == db.books_series_link.c.book, db.Books.id == db.books_series_link.c.book,
db.Series) db.Series)
@ -609,7 +616,8 @@ def render_language_books(page, name, order):
entries, random, pagination = calibre_db.fill_indexpage(page, 0, entries, random, pagination = calibre_db.fill_indexpage(page, 0,
db.Books, db.Books,
db.Books.languages.any(db.Languages.lang_code == name), db.Books.languages.any(db.Languages.lang_code == name),
[order[0][0]]) [order[0][0]],
True, config.config_read_column)
return render_title_template('index.html', random=random, entries=entries, pagination=pagination, id=name, return render_title_template('index.html', random=random, entries=entries, pagination=pagination, id=name,
title=_(u"Language: %(name)s", name=lang_name), page="language", order=order[1]) title=_(u"Language: %(name)s", name=lang_name), page="language", order=order[1])
@ -622,30 +630,12 @@ def render_read_books(page, are_read, as_xml=False, order=None):
ub.ReadBook.read_status == ub.ReadBook.STATUS_FINISHED) ub.ReadBook.read_status == ub.ReadBook.STATUS_FINISHED)
else: else:
db_filter = coalesce(ub.ReadBook.read_status, 0) != ub.ReadBook.STATUS_FINISHED db_filter = coalesce(ub.ReadBook.read_status, 0) != ub.ReadBook.STATUS_FINISHED
entries, random, pagination = calibre_db.fill_indexpage(page, 0,
db.Books,
db_filter,
sort_param,
False, 0,
db.books_series_link,
db.Books.id == db.books_series_link.c.book,
db.Series,
ub.ReadBook, db.Books.id == ub.ReadBook.book_id)
else: else:
try: try:
if are_read: if are_read:
db_filter = db.cc_classes[config.config_read_column].value == True db_filter = db.cc_classes[config.config_read_column].value == True
else: else:
db_filter = coalesce(db.cc_classes[config.config_read_column].value, False) != True db_filter = coalesce(db.cc_classes[config.config_read_column].value, False) != True
entries, random, pagination = calibre_db.fill_indexpage(page, 0,
db.Books,
db_filter,
sort_param,
False, 0,
db.books_series_link,
db.Books.id == db.books_series_link.c.book,
db.Series,
db.cc_classes[config.config_read_column])
except (KeyError, AttributeError, IndexError): except (KeyError, AttributeError, IndexError):
log.error("Custom Column No.{} is not existing in calibre database".format(config.config_read_column)) log.error("Custom Column No.{} is not existing in calibre database".format(config.config_read_column))
if not as_xml: if not as_xml:
@ -655,6 +645,15 @@ def render_read_books(page, are_read, as_xml=False, order=None):
return redirect(url_for("web.index")) return redirect(url_for("web.index"))
return [] # ToDo: Handle error Case for opds return [] # ToDo: Handle error Case for opds
entries, random, pagination = calibre_db.fill_indexpage(page, 0,
db.Books,
db_filter,
sort_param,
True, config.config_read_column,
db.books_series_link,
db.Books.id == db.books_series_link.c.book,
db.Series)
if as_xml: if as_xml:
return entries, pagination return entries, pagination
else: else:
@ -683,7 +682,7 @@ def render_archived_books(page, sort_param):
archived_filter, archived_filter,
order, order,
True, True,
False, 0) True, config.config_read_column)
name = _(u'Archived Books') + ' (' + str(len(archived_book_ids)) + ')' name = _(u'Archived Books') + ' (' + str(len(archived_book_ids)) + ')'
page_name = "archived" page_name = "archived"
@ -723,7 +722,7 @@ def render_prepare_search_form(cc):
def render_search_results(term, offset=None, order=None, limit=None): def render_search_results(term, offset=None, order=None, limit=None):
join = db.books_series_link, db.Books.id == db.books_series_link.c.book, db.Series join = db.books_series_link, db.books_series_link.c.book == db.Books.id, db.Series
entries, result_count, pagination = calibre_db.get_search_results(term, entries, result_count, pagination = calibre_db.get_search_results(term,
offset, offset,
order, order,
@ -813,27 +812,8 @@ def list_books():
books = calibre_db.search_query(search_param, config.config_read_column).all() books = calibre_db.search_query(search_param, config.config_read_column).all()
filtered_count = len(books) filtered_count = len(books)
else: else:
if not config.config_read_column: query = calibre_db.generate_linked_query(config.config_read_column, db.Books)
books = (calibre_db.session.query(db.Books, ub.ReadBook.read_status, ub.ArchivedBook.is_archived) books = query.filter(calibre_db.common_filters(allow_show_archived=True)).all()
.select_from(db.Books)
.outerjoin(ub.ReadBook,
and_(ub.ReadBook.user_id == int(current_user.id),
ub.ReadBook.book_id == db.Books.id)))
else:
read_column = ""
try:
read_column = db.cc_classes[config.config_read_column]
books = (calibre_db.session.query(db.Books, read_column.value, ub.ArchivedBook.is_archived)
.select_from(db.Books)
.outerjoin(read_column, read_column.book == db.Books.id))
except (KeyError, AttributeError, IndexError):
log.error(
"Custom Column No.{} is not existing in calibre database".format(config.config_read_column))
# Skip linking read column and return None instead of read status
books = calibre_db.session.query(db.Books, None, ub.ArchivedBook.is_archived)
books = (books.outerjoin(ub.ArchivedBook, and_(db.Books.id == ub.ArchivedBook.book_id,
int(current_user.id) == ub.ArchivedBook.user_id))
.filter(calibre_db.common_filters(allow_show_archived=True)).all())
entries = calibre_db.get_checkbox_sorted(books, state, off, limit, order, True) entries = calibre_db.get_checkbox_sorted(books, state, off, limit, order, True)
elif search_param: elif search_param:
entries, filtered_count, __ = calibre_db.get_search_results(search_param, entries, filtered_count, __ = calibre_db.get_search_results(search_param,
@ -856,8 +836,8 @@ def list_books():
result = list() result = list()
for entry in entries: for entry in entries:
val = entry[0] val = entry[0]
val.read_status = entry[1] == ub.ReadBook.STATUS_FINISHED val.is_archived = entry[1] is True
val.is_archived = entry[2] is True val.read_status = entry[2] == ub.ReadBook.STATUS_FINISHED
for lang_index in range(0, len(val.languages)): for lang_index in range(0, len(val.languages)):
val.languages[lang_index].language_name = isoLanguages.get_language_name(get_locale(), val.languages[ val.languages[lang_index].language_name = isoLanguages.get_language_name(get_locale(), val.languages[
lang_index].lang_code) lang_index].lang_code)
@ -1254,24 +1234,25 @@ def render_adv_search_results(term, offset=None, order=None, limit=None):
cc = get_cc_columns(filter_config_custom_read=True) cc = get_cc_columns(filter_config_custom_read=True)
calibre_db.session.connection().connection.connection.create_function("lower", 1, db.lcase) calibre_db.session.connection().connection.connection.create_function("lower", 1, db.lcase)
if not config.config_read_column: query = calibre_db.generate_linked_query(config.config_read_column, db.Books)
'''if not config.config_read_column:
query = (calibre_db.session.query(db.Books, ub.ArchivedBook.is_archived, ub.ReadBook).select_from(db.Books) query = (calibre_db.session.query(db.Books, ub.ArchivedBook.is_archived, ub.ReadBook).select_from(db.Books)
.outerjoin(ub.ReadBook, and_(db.Books.id == ub.ReadBook.book_id, .outerjoin(ub.ReadBook, and_(db.Books.id == ub.ReadBook.book_id,
int(current_user.id) == ub.ReadBook.user_id))) int(current_user.id) == ub.ReadBook.user_id)))
else: else:
try: try:
read_column = cc[config.config_read_column] read_column = db.cc_classes[config.config_read_column]
query = (calibre_db.session.query(db.Books, ub.ArchivedBook.is_archived, read_column.value) query = (calibre_db.session.query(db.Books, ub.ArchivedBook.is_archived, read_column.value)
.select_from(db.Books) .select_from(db.Books)
.outerjoin(read_column, read_column.book == db.Books.id)) .outerjoin(read_column, read_column.book == db.Books.id))
except (KeyError, AttributeError, IndexError): except (KeyError, AttributeError, IndexError):
log.error("Custom Column No.{} is not existing in calibre database".format(config.config_read_column)) log.error("Custom Column No.{} is not existing in calibre database".format(config.config_read_column))
# Skip linking read column # Skip linking read column
query = calibre_db.session.query(db.Books, ub.ArchivedBook.is_archived, None) query = calibre_db.session.query(db.Books, ub.ArchivedBook.is_archived, None)
query = query.outerjoin(ub.ArchivedBook, and_(db.Books.id == ub.ArchivedBook.book_id, query = query.outerjoin(ub.ArchivedBook, and_(db.Books.id == ub.ArchivedBook.book_id,
int(current_user.id) == ub.ArchivedBook.user_id)) int(current_user.id) == ub.ArchivedBook.user_id))'''
q = query.outerjoin(db.books_series_link, db.Books.id == db.books_series_link.c.book) \ q = query.outerjoin(db.books_series_link, db.books_series_link.c.book == db.Books.id) \
.outerjoin(db.Series) \ .outerjoin(db.Series) \
.filter(calibre_db.common_filters(True)) .filter(calibre_db.common_filters(True))

File diff suppressed because it is too large Load Diff