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

Update shelfs handling, bugfix changed updater

This commit is contained in:
Ozzieisaacs 2020-04-02 18:23:24 +02:00
parent 0c27ff11b9
commit bab14a1fbf
9 changed files with 103 additions and 57 deletions

View File

@ -25,7 +25,7 @@ import sys
import datetime import datetime
from functools import wraps from functools import wraps
from flask import Blueprint, request, render_template, Response, g, make_response 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_ from sqlalchemy.sql.expression import func, text, or_, and_
from werkzeug.security import check_password_hash from werkzeug.security import check_password_hash
@ -165,7 +165,10 @@ def feed_author(book_id):
off = request.args.get("offset") or 0 off = request.args.get("offset") or 0
entries, __, pagination = fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), entries, __, pagination = fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1),
db.Books, db.Books.authors.any(db.Authors.id == book_id), [db.Books.timestamp.desc()]) db.Books, db.Books.authors.any(db.Authors.id == book_id), [db.Books.timestamp.desc()])
return render_xml_template('feed.xml', entries=entries, pagination=pagination) if len(entries):
return render_xml_template('feed.xml', entries=entries, pagination=pagination)
else:
return abort(404)
@opds.route("/opds/publisher") @opds.route("/opds/publisher")
@ -186,7 +189,10 @@ def feed_publisher(book_id):
entries, __, pagination = fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), entries, __, pagination = fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1),
db.Books, db.Books.publishers.any(db.Publishers.id == book_id), db.Books, db.Books.publishers.any(db.Publishers.id == book_id),
[db.Books.timestamp.desc()]) [db.Books.timestamp.desc()])
return render_xml_template('feed.xml', entries=entries, pagination=pagination) if len(entries):
return render_xml_template('feed.xml', entries=entries, pagination=pagination)
else:
return abort(404)
@opds.route("/opds/category") @opds.route("/opds/category")
@ -206,7 +212,10 @@ def feed_category(book_id):
off = request.args.get("offset") or 0 off = request.args.get("offset") or 0
entries, __, pagination = fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), entries, __, pagination = fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1),
db.Books, db.Books.tags.any(db.Tags.id == book_id), [db.Books.timestamp.desc()]) db.Books, db.Books.tags.any(db.Tags.id == book_id), [db.Books.timestamp.desc()])
return render_xml_template('feed.xml', entries=entries, pagination=pagination) if len(entries):
return render_xml_template('feed.xml', entries=entries, pagination=pagination)
else:
return abort(404)
@opds.route("/opds/series") @opds.route("/opds/series")
@ -226,7 +235,10 @@ def feed_series(book_id):
off = request.args.get("offset") or 0 off = request.args.get("offset") or 0
entries, __, pagination = fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), entries, __, pagination = fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1),
db.Books, db.Books.series.any(db.Series.id == book_id), [db.Books.series_index]) db.Books, db.Books.series.any(db.Series.id == book_id), [db.Books.series_index])
return render_xml_template('feed.xml', entries=entries, pagination=pagination) if len(entries):
return render_xml_template('feed.xml', entries=entries, pagination=pagination)
else:
return abort(404)
@opds.route("/opds/ratings") @opds.route("/opds/ratings")
@requires_basic_auth_if_no_ano @requires_basic_auth_if_no_ano
@ -250,7 +262,11 @@ def feed_ratings(book_id):
off = request.args.get("offset") or 0 off = request.args.get("offset") or 0
entries, __, pagination = fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), entries, __, pagination = fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1),
db.Books, db.Books.ratings.any(db.Ratings.id == book_id),[db.Books.timestamp.desc()]) db.Books, db.Books.ratings.any(db.Ratings.id == book_id),[db.Books.timestamp.desc()])
return render_xml_template('feed.xml', entries=entries, pagination=pagination) if len(entries):
return render_xml_template('feed.xml', entries=entries, pagination=pagination)
else:
return abort(404)
@opds.route("/opds/formats") @opds.route("/opds/formats")
@ -274,7 +290,11 @@ def feed_format(book_id):
off = request.args.get("offset") or 0 off = request.args.get("offset") or 0
entries, __, pagination = fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), entries, __, pagination = fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1),
db.Books, db.Books.data.any(db.Data.format == book_id.upper()), [db.Books.timestamp.desc()]) db.Books, db.Books.data.any(db.Data.format == book_id.upper()), [db.Books.timestamp.desc()])
return render_xml_template('feed.xml', entries=entries, pagination=pagination) if len(entries):
return render_xml_template('feed.xml', entries=entries, pagination=pagination)
else:
return abort(404)
@opds.route("/opds/language") @opds.route("/opds/language")
@opds.route("/opds/language/") @opds.route("/opds/language/")
@ -305,20 +325,23 @@ def feed_languages(book_id):
off = request.args.get("offset") or 0 off = request.args.get("offset") or 0
entries, __, pagination = fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), entries, __, pagination = fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1),
db.Books, db.Books.languages.any(db.Languages.id == book_id), [db.Books.timestamp.desc()]) db.Books, db.Books.languages.any(db.Languages.id == book_id), [db.Books.timestamp.desc()])
return render_xml_template('feed.xml', entries=entries, pagination=pagination) if len(entries):
return render_xml_template('feed.xml', entries=entries, pagination=pagination)
@opds.route("/opds/shelfindex", defaults={'public': 0})
@opds.route("/opds/shelfindex/<string:public>")
@requires_basic_auth_if_no_ano
def feed_shelfindex(public):
off = request.args.get("offset") or 0
if public != 0:
shelf = g.public_shelfes
number = len(shelf)
else: else:
shelf = g.user.shelf return abort(404)
number = shelf.count()
@opds.route("/opds/shelfindex") #, defaults={'public': 0})
# @opds.route("/opds/shelfindex/<string:public>")
@requires_basic_auth_if_no_ano
def feed_shelfindex():
off = request.args.get("offset") or 0
# if public != 0:
shelf = g.shelves_access
number = len(shelf)
#else:
#shelf = g.user.shelf
# number = shelf.count()
pagination = Pagination((int(off) / (int(config.config_books_per_page)) + 1), config.config_books_per_page, pagination = Pagination((int(off) / (int(config.config_books_per_page)) + 1), config.config_books_per_page,
number) number)
return render_xml_template('feed.xml', listelements=shelf, folder='opds.feed_shelf', pagination=pagination) return render_xml_template('feed.xml', listelements=shelf, folder='opds.feed_shelf', pagination=pagination)
@ -346,6 +369,8 @@ def feed_shelf(book_id):
pagination = Pagination((int(off) / (int(config.config_books_per_page)) + 1), config.config_books_per_page, pagination = Pagination((int(off) / (int(config.config_books_per_page)) + 1), config.config_books_per_page,
len(result)) len(result))
return render_xml_template('feed.xml', entries=result, pagination=pagination) return render_xml_template('feed.xml', entries=result, pagination=pagination)
else:
return abort(404)
@opds.route("/opds/download/<book_id>/<book_format>/") @opds.route("/opds/download/<book_id>/<book_format>/")

View File

@ -24,7 +24,7 @@ import signal
import socket import socket
try: try:
from gevent.pyewsgi import WSGIServer from gevent.pywsgi import WSGIServer
from gevent.pool import Pool from gevent.pool import Pool
from gevent import __version__ as _version from gevent import __version__ as _version
VERSION = 'Gevent ' + _version VERSION = 'Gevent ' + _version
@ -171,6 +171,7 @@ class WebServer(object):
except Exception as ex: except Exception as ex:
log.error("Error starting server: %s", ex) log.error("Error starting server: %s", ex)
print("Error starting server: %s" % ex) print("Error starting server: %s" % ex)
self.stop()
return False return False
finally: finally:
self.wsgiserver = None self.wsgiserver = None

View File

@ -204,12 +204,24 @@ def create_shelf():
shelf.is_public = 1 shelf.is_public = 1
shelf.name = to_save["title"] shelf.name = to_save["title"]
shelf.user_id = int(current_user.id) shelf.user_id = int(current_user.id)
existing_shelf = ub.session.query(ub.Shelf).filter(
or_((ub.Shelf.name == to_save["title"]) & (ub.Shelf.is_public == 1), is_shelf_name_unique = False
(ub.Shelf.name == to_save["title"]) & (ub.Shelf.user_id == int(current_user.id)))).first() if shelf.is_public == 1:
if existing_shelf: is_shelf_name_unique = ub.session.query(ub.Shelf) \
flash(_(u"A shelf with the name '%(title)s' already exists.", title=to_save["title"]), category="error") .filter((ub.Shelf.name == to_save["title"]) & (ub.Shelf.is_public == 1)) \
.first() is None
if not is_shelf_name_unique:
flash(_(u"A public shelf with the name '%(title)s' already exists.", title=to_save["title"]), category="error")
else: else:
is_shelf_name_unique = ub.session.query(ub.Shelf) \
.filter((ub.Shelf.name == to_save["title"]) & (ub.Shelf.is_public == 0) & (ub.Shelf.user_id == int(current_user.id))) \
.first() is None
if not is_shelf_name_unique:
flash(_(u"A private shelf with the name '%(title)s' already exists.", title=to_save["title"]), category="error")
if is_shelf_name_unique:
try: try:
ub.session.add(shelf) ub.session.add(shelf)
ub.session.commit() ub.session.commit()
@ -227,13 +239,26 @@ def edit_shelf(shelf_id):
shelf = ub.session.query(ub.Shelf).filter(ub.Shelf.id == shelf_id).first() shelf = ub.session.query(ub.Shelf).filter(ub.Shelf.id == shelf_id).first()
if request.method == "POST": if request.method == "POST":
to_save = request.form.to_dict() to_save = request.form.to_dict()
existing_shelf = ub.session.query(ub.Shelf).filter(
or_((ub.Shelf.name == to_save["title"]) & (ub.Shelf.is_public == 1), is_shelf_name_unique = False
(ub.Shelf.name == to_save["title"]) & (ub.Shelf.user_id == int(current_user.id)))).filter( if shelf.is_public == 1:
ub.Shelf.id != shelf_id).first() is_shelf_name_unique = ub.session.query(ub.Shelf) \
if existing_shelf: .filter((ub.Shelf.name == to_save["title"]) & (ub.Shelf.is_public == 1)) \
flash(_(u"A shelf with the name '%(title)s' already exists.", title=to_save["title"]), category="error") .filter(ub.Shelf.id != shelf_id) \
.first() is None
if not is_shelf_name_unique:
flash(_(u"A public shelf with the name '%(title)s' already exists.", title=to_save["title"]), category="error")
else: else:
is_shelf_name_unique = ub.session.query(ub.Shelf) \
.filter((ub.Shelf.name == to_save["title"]) & (ub.Shelf.is_public == 0) & (ub.Shelf.user_id == int(current_user.id))) \
.filter(ub.Shelf.id != shelf_id) \
.first() is None
if not is_shelf_name_unique:
flash(_(u"A private shelf with the name '%(title)s' already exists.", title=to_save["title"]), category="error")
if is_shelf_name_unique:
shelf.name = to_save["title"] shelf.name = to_save["title"]
if "is_public" in to_save: if "is_public" in to_save:
shelf.is_public = 1 shelf.is_public = 1

View File

@ -217,7 +217,7 @@
<div class="more-stuff"> <div class="more-stuff">
{% if g.user.is_authenticated %} {% if g.user.is_authenticated %}
{% if g.user.shelf.all() or g.public_shelfes %} {% if g.user.shelf.all() or g.shelves_access %}
<div id="shelf-actions" class="btn-toolbar" role="toolbar"> <div id="shelf-actions" class="btn-toolbar" role="toolbar">
<div class="btn-group" role="group" aria-label="Add to shelves"> <div class="btn-group" role="group" aria-label="Add to shelves">
<button id="add-to-shelf" type="button" class="btn btn-primary btn-sm dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> <button id="add-to-shelf" type="button" class="btn btn-primary btn-sm dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
@ -237,7 +237,7 @@
</li> </li>
{% endif %} {% endif %}
{%endfor%} {%endfor%}
{% for shelf in g.public_shelfes %} {% for shelf in g.shelves_access %}
{% if not shelf.id in books_shelfs %} {% if not shelf.id in books_shelfs %}
<li> <li>
<a href="{{ url_for('shelf.add_to_shelf', book_id=entry.id, shelf_id=shelf.id) }}" <a href="{{ url_for('shelf.add_to_shelf', book_id=entry.id, shelf_id=shelf.id) }}"
@ -263,7 +263,7 @@
</a> </a>
{% endif %} {% endif %}
{%endfor%} {%endfor%}
{% for shelf in g.public_shelfes %} {% for shelf in g.shelves_access %}
{% if shelf.id in books_shelfs %} {% if shelf.id in books_shelfs %}
<a href="{{ url_for('shelf.remove_from_shelf', book_id=entry.id, shelf_id=shelf.id) }}" <a href="{{ url_for('shelf.remove_from_shelf', book_id=entry.id, shelf_id=shelf.id) }}"
data-add-href="{{ url_for('shelf.add_to_shelf', book_id=entry.id, shelf_id=shelf.id) }}" data-add-href="{{ url_for('shelf.add_to_shelf', book_id=entry.id, shelf_id=shelf.id) }}"

View File

@ -75,7 +75,11 @@
{% endif %} {% endif %}
{% for entry in listelements %} {% for entry in listelements %}
<entry> <entry>
{% if entry.__class__.__name__ == 'Shelf' and entry.is_public == 1 %}
<title>{{entry.name}} {{_('(Public)')}}</title>
{% else %}
<title>{{entry.name}}</title> <title>{{entry.name}}</title>
{% endif %}
<id>{{ url_for(folder, book_id=entry.id) }}</id> <id>{{ url_for(folder, book_id=entry.id) }}</id>
<link rel="subsection" type="application/atom+xml;profile=opds-catalog" href="{{url_for(folder, book_id=entry.id)}}"/> <link rel="subsection" type="application/atom+xml;profile=opds-catalog" href="{{url_for(folder, book_id=entry.id)}}"/>
</entry> </entry>

View File

@ -108,19 +108,10 @@
<content type="text">{{_('Books ordered by file formats')}}</content> <content type="text">{{_('Books ordered by file formats')}}</content>
</entry> </entry>
<entry> <entry>
<title>{{_('Public Shelves')}}</title> <title>{{_('Shelves')}}</title>
<link href="{{url_for('opds.feed_shelfindex', public='public')}}" type="application/atom+xml;profile=opds-catalog"/> <link href="{{url_for('opds.feed_shelfindex')}}" type="application/atom+xml;profile=opds-catalog"/>
<id>{{url_for('opds.feed_shelfindex', public="public")}}</id>
<updated>{{ current_time }}</updated>
<content type="text">{{_('Books organized in public shelfs, visible to everyone')}}</content>
</entry>
{% if not current_user.is_anonymous %}
<entry>
<title>{{_('Your Shelves')}}</title>
<link href="{{url_for('opds.feed_shelfindex')}}" type="application/atom+xml;profile=opds-catalog"/>
<id>{{url_for('opds.feed_shelfindex')}}</id> <id>{{url_for('opds.feed_shelfindex')}}</id>
<updated>{{ current_time }}</updated> <updated>{{ current_time }}</updated>
<content type="text">{{_("User's own shelfs, only visible to the current user himself")}}</content> <content type="text">{{_('Books organized in shelves')}}</content>
</entry> </entry>
{% endif %}
</feed> </feed>

View File

@ -133,14 +133,14 @@
{% endif %} {% endif %}
{% endfor %} {% endfor %}
{% if g.user.is_authenticated or g.allow_anonymous %} {% if g.user.is_authenticated or g.allow_anonymous %}
<li class="nav-head hidden-xs public-shelves">{{_('Public Shelves')}}</li> <li class="nav-head hidden-xs public-shelves">{{_('Shelves')}}</li>
{% for shelf in g.public_shelfes %} {% for shelf in g.shelves_access %}
<li><a href="{{url_for('shelf.show_shelf', shelf_id=shelf.id)}}"><span class="glyphicon glyphicon-list public_shelf"></span> {{shelf.name|shortentitle(40)}}</a></li> <li><a href="{{url_for('shelf.show_shelf', shelf_id=shelf.id)}}"><span class="glyphicon glyphicon-list shelf"></span>{{shelf.name|shortentitle(40)}}{% if shelf.is_public == 1 %} {{_('(Public)')}}{% endif %}</a></li>
{% endfor %} {% endfor %}
<li class="nav-head hidden-xs your-shelves">{{_('Your Shelves')}}</li> <!--li class="nav-head hidden-xs your-shelves">{{_('Your Shelves')}}</li>
{% for shelf in g.user.shelf %} {% for shelf in g.user.shelf %}
<li><a href="{{url_for('shelf.show_shelf', shelf_id=shelf.id)}}"><span class="glyphicon glyphicon-list private_shelf"></span> {{shelf.name|shortentitle(40)}}</a></li> <li><a href="{{url_for('shelf.show_shelf', shelf_id=shelf.id)}}"><span class="glyphicon glyphicon-list private_shelf"></span>{{shelf.name|shortentitle(40)}}{% if shelf.is_public == 1 %} {{_('(Public)')}}{% endif %}</a></li>
{% endfor %} {% endfor %}-->
{% if not g.user.is_anonymous %} {% if not g.user.is_anonymous %}
<li id="nav_createshelf" class="create-shelf"><a href="{{url_for('shelf.create_shelf')}}">{{_('Create a Shelf')}}</a></li> <li id="nav_createshelf" class="create-shelf"><a href="{{url_for('shelf.create_shelf')}}">{{_('Create a Shelf')}}</a></li>
<li id="nav_about" {% if page == 'stat' %}class="active"{% endif %}><a href="{{url_for('about.stats')}}"><span class="glyphicon glyphicon-info-sign"></span> {{_('About')}}</a></li> <li id="nav_about" {% if page == 'stat' %}class="active"{% endif %}><a href="{{url_for('about.stats')}}"><span class="glyphicon glyphicon-info-sign"></span> {{_('About')}}</a></li>

View File

@ -7,7 +7,7 @@
{% else %} {% else %}
<h2>{{entries|length}} {{_('Results for:')}} {{searchterm}}</h2> <h2>{{entries|length}} {{_('Results for:')}} {{searchterm}}</h2>
{% if g.user.is_authenticated %} {% if g.user.is_authenticated %}
{% if g.user.shelf.all() or g.public_shelfes %} {% if g.user.shelf.all() or g.shelves_access %}
<div id="shelf-actions" class="btn-toolbar" role="toolbar"> <div id="shelf-actions" class="btn-toolbar" role="toolbar">
<div class="btn-group" role="group" aria-label="Add to shelves"> <div class="btn-group" role="group" aria-label="Add to shelves">
<button id="add-to-shelf" type="button" class="btn btn-primary btn-sm dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> <button id="add-to-shelf" type="button" class="btn btn-primary btn-sm dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
@ -20,7 +20,7 @@
<li><a href="{{ url_for('shelf.search_to_shelf', shelf_id=shelf.id) }}"> {{shelf.name}}</a></li> <li><a href="{{ url_for('shelf.search_to_shelf', shelf_id=shelf.id) }}"> {{shelf.name}}</a></li>
{% endif %} {% endif %}
{% endfor %} {% endfor %}
{% for shelf in g.public_shelfes %} {% for shelf in g.shelves_access %}
<li><a href="{{ url_for('shelf.search_to_shelf', shelf_id=shelf.id) }}">{{shelf.name}}</a></li> <li><a href="{{ url_for('shelf.search_to_shelf', shelf_id=shelf.id) }}">{{shelf.name}}</a></li>
{% endfor %} {% endfor %}
</ul> </ul>

View File

@ -37,7 +37,7 @@ from flask import render_template, request, redirect, send_from_directory, make_
from flask_babel import gettext as _ from flask_babel import gettext as _
from flask_login import login_user, logout_user, login_required, current_user from flask_login import login_user, logout_user, login_required, current_user
from sqlalchemy.exc import IntegrityError from sqlalchemy.exc import IntegrityError
from sqlalchemy.sql.expression import text, func, true, false, not_, and_, exists from sqlalchemy.sql.expression import text, func, true, false, not_, and_, exists, or_
from werkzeug.exceptions import default_exceptions from werkzeug.exceptions import default_exceptions
from werkzeug.datastructures import Headers from werkzeug.datastructures import Headers
from werkzeug.security import generate_password_hash, check_password_hash from werkzeug.security import generate_password_hash, check_password_hash
@ -268,7 +268,7 @@ def before_request():
g.allow_upload = config.config_uploading g.allow_upload = config.config_uploading
g.current_theme = config.config_theme g.current_theme = config.config_theme
g.config_authors_max = config.config_authors_max g.config_authors_max = config.config_authors_max
g.public_shelfes = ub.session.query(ub.Shelf).filter(ub.Shelf.is_public == 1).order_by(ub.Shelf.name).all() g.shelves_access = ub.session.query(ub.Shelf).filter(or_(ub.Shelf.is_public == 1, ub.Shelf.user_id == current_user.id)).order_by(ub.Shelf.name).all()
if not config.db_configured and request.endpoint not in ('admin.basic_configuration', 'login') and '/static/' not in request.path: if not config.db_configured and request.endpoint not in ('admin.basic_configuration', 'login') and '/static/' not in request.path:
return redirect(url_for('admin.basic_configuration')) return redirect(url_for('admin.basic_configuration'))