mirror of
https://github.com/janeczku/calibre-web
synced 2024-11-24 10:37:23 +00:00
Anonymous account now can also be configured like normal user (e.g. download permissions, change visibility of sidebar) (#35)
Search now working for calibre-companion (#79), download not working yet metadata view 80% finished
This commit is contained in:
parent
beca61a596
commit
0107c52b66
@ -13,9 +13,9 @@
|
||||
<th>{{_('Upload')}}</th>
|
||||
<th>{{_('Edit')}}</th>
|
||||
<th>{{_('Passwd')}}</th>
|
||||
|
||||
</tr>
|
||||
{% for user in content %}
|
||||
{% if not user.role_anonymous() or config.ANON_BROWSE %}
|
||||
<tr>
|
||||
<td><a href="{{url_for('edit_user', user_id=user.id)}}">{{user.nickname}}</a></td>
|
||||
<td>{{user.email}}</td>
|
||||
@ -26,7 +26,8 @@
|
||||
<td>{% if user.role_upload() %}<span class="glyphicon glyphicon-ok"></span>{% else %}<span class="glyphicon glyphicon-remove"></span>{% endif %}</td>
|
||||
<td>{% if user.role_edit() %}<span class="glyphicon glyphicon-ok"></span>{% else %}<span class="glyphicon glyphicon-remove"></span>{% endif %}</td>
|
||||
<td>{% if user.role_passwd() %}<span class="glyphicon glyphicon-ok"></span>{% else %}<span class="glyphicon glyphicon-remove"></span>{% endif %}</td>
|
||||
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</table>
|
||||
<div class="btn btn-default"><a href="{{url_for('new_user')}}">{{_('Add new user')}}</a></div>
|
||||
|
@ -104,11 +104,11 @@
|
||||
{{entry.comments[0].text|safe}}
|
||||
{% endif %}
|
||||
|
||||
{% if g.user.is_authenticated %}
|
||||
|
||||
<div class="more-stuff">
|
||||
<div class="btn-toolbar" role="toolbar">
|
||||
|
||||
<div class="btn-group" role="group" aria-label="Download, send to Kindle, reading">
|
||||
{% if g.user.role_download() %}
|
||||
<div class="btn-group" role="group">
|
||||
<button id="btnGroupDrop1" type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<span class="glyphicon glyphicon-download"></span> {{_('Download')}}
|
||||
@ -120,6 +120,9 @@
|
||||
{%endfor%}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if g.user.is_authenticated %}
|
||||
{% if g.user.kindle_mail %}
|
||||
<a href="{{url_for('send_to_kindle', book_id=entry.id)}}" id="sendbtn" class="btn btn-primary" role="button"><span class="glyphicon glyphicon-send"></span> {{_('Send to Kindle')}}</a>
|
||||
{% endif %}
|
||||
@ -136,10 +139,11 @@
|
||||
{%endfor%}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</br>
|
||||
|
||||
{% if g.user.is_authenticated %}
|
||||
{% if g.user.shelf.all() or g.public_shelfes %}
|
||||
<div class="btn-toolbar" role="toolbar">
|
||||
<div class="btn-group" role="group" aria-label="Add to shelves">
|
||||
@ -180,7 +184,7 @@
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% endif %}
|
||||
{% if g.user.role_edit() %}
|
||||
<div class="btn-toolbar" role="toolbar">
|
||||
<div class="btn-group" role="group" aria-label="Edit/Delete book">
|
||||
@ -189,7 +193,7 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -10,12 +10,11 @@
|
||||
<link rel="up"
|
||||
href="{{url_for('feed_index')}}"
|
||||
type="application/atom+xml;profile=opds-catalog;type=feed;kind=navigation"/>
|
||||
{% if pagination.has_prev %}
|
||||
<link rel="first"
|
||||
href="{{request.script_root + request.path}}"
|
||||
type="application/atom+xml;profile=opds-catalog;type=feed;kind=navigation"/>
|
||||
<link rel="last"
|
||||
href="{{request.script_root + request.path}}?offset={{ pagination.last_offset }}"
|
||||
type="application/atom+xml;profile=opds-catalog;type=feed;kind=navigation"/>
|
||||
{% endif %}
|
||||
{% if pagination.has_next %}
|
||||
<link rel="next"
|
||||
title="{{_('Next')}}"
|
||||
|
@ -146,10 +146,11 @@
|
||||
<li><a href="{{url_for('show_shelf', shelf_id=shelf.id)}}"><span class="glyphicon glyphicon-list"></span> {{shelf.name}}</a></li>
|
||||
{% endfor %}
|
||||
{% if not g.user.is_anonymous() %}
|
||||
<li class="create-shelf"><a href="{{url_for('create_shelf')}}">{{_('Create a Shelf')}}</a></li>
|
||||
<li id="nav_createshelf" class="create-shelf"><a href="{{url_for('create_shelf')}}">{{_('Create a Shelf')}}</a></li>
|
||||
<li id="nav_about"><a href="{{url_for('stats')}}"><span class="glyphicon glyphicon-info-sign"></span> {{_('About')}}</a></li>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<li><a href="{{url_for('stats')}}"><span class="glyphicon glyphicon-info-sign"></span> {{_('About')}}</a></li>
|
||||
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
|
@ -13,7 +13,7 @@
|
||||
<label for="email">{{_('Email address')}}</label>
|
||||
<input type="email" class="form-control" name="email" id="email" value="{{ content.email if content.email != None }}" autocomplete="off" required>
|
||||
</div>
|
||||
{% if g.user and g.user.role_passwd() or g.user.role_admin()%}
|
||||
{% if ( g.user and g.user.role_passwd() or g.user.role_admin() ) and not content.role_anonymous() %}
|
||||
<div class="form-group">
|
||||
<label for="password">{{_('Password')}}</label>
|
||||
<input type="password" class="form-control" name="password" id="password" value="" autocomplete="off">
|
||||
@ -23,6 +23,7 @@
|
||||
<label for="kindle_mail">{{_('Kindle E-Mail')}}</label>
|
||||
<input type="email" class="form-control" name="kindle_mail" id="kindle_mail" value="{{ content.kindle_mail if content.kindle_mail != None }}">
|
||||
</div>
|
||||
{% if not content.role_anonymous() %}
|
||||
<div class="form-group">
|
||||
<label for="locale">{{_('Language')}}</label>
|
||||
<select name="locale" id="locale" class="form-control">
|
||||
@ -31,6 +32,7 @@
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="form-group">
|
||||
<label for="default_language">{{_('Show books with language')}}</label>
|
||||
<select name="default_language" id="default_language" class="form-control">
|
||||
@ -62,11 +64,12 @@
|
||||
</div>
|
||||
|
||||
{% if g.user and g.user.role_admin() and not profile %}
|
||||
|
||||
{% if not content.role_anonymous() %}
|
||||
<div class="form-group">
|
||||
<input type="checkbox" name="admin_role" id="admin_role" {% if content.role_admin() %}checked{% endif %}>
|
||||
<label for="admin_role">{{_('Admin user')}}</label>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="form-group">
|
||||
<input type="checkbox" name="download_role" id="download_role" {% if content.role_download() %}checked{% endif %}>
|
||||
<label for="download_role">{{_('Allow Downloads')}}</label>
|
||||
@ -79,19 +82,21 @@
|
||||
<input type="checkbox" name="edit_role" id="edit_role" {% if content.role_edit() %}checked{% endif %}>
|
||||
<label for="edit_role">{{_('Allow Edit')}}</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="checkbox" name="passwd_role" id="passwd_role" {% if content.role_passwd() %}checked{% endif %}>
|
||||
<label for="passwd_role">{{_('Allow Changing Password')}}</label>
|
||||
</div>
|
||||
{% if not content.role_anonymous() %}
|
||||
<div class="form-group">
|
||||
<input type="checkbox" name="passwd_role" id="passwd_role" {% if content.role_passwd() %}checked{% endif %}>
|
||||
<label for="passwd_role">{{_('Allow Changing Password')}}</label>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if g.user and g.user.role_admin() and not profile and not new_user %}
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" name="delete"> {{_('Delete this user')}}
|
||||
</label>
|
||||
</div>
|
||||
{% if g.user and g.user.role_admin() and not profile and not new_user and not content.role_anonymous() %}
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" name="delete"> {{_('Delete this user')}}
|
||||
</label>
|
||||
</div>
|
||||
{% endif %}
|
||||
<button type="submit" class="btn btn-default">{{_('Submit')}}</button>
|
||||
<button type="submit" id="submit" class="btn btn-default">{{_('Submit')}}</button>
|
||||
{% if not profile %}
|
||||
<a href="{{ url_for('admin') }}" class="btn btn-default">{{_('Back')}}</a>
|
||||
{% endif %}
|
||||
|
93
cps/ub.py
93
cps/ub.py
@ -5,9 +5,12 @@ from sqlalchemy import *
|
||||
from sqlalchemy import exc
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from sqlalchemy.orm import *
|
||||
from flask_login import AnonymousUserMixin
|
||||
import os
|
||||
import config
|
||||
import traceback
|
||||
from werkzeug.security import generate_password_hash
|
||||
from flask_babel import gettext as _
|
||||
|
||||
dbpath = os.path.join(config.APP_DB_ROOT, "app.db")
|
||||
engine = create_engine('sqlite:///{0}'.format(dbpath), echo=False)
|
||||
@ -19,28 +22,11 @@ ROLE_DOWNLOAD = 2
|
||||
ROLE_UPLOAD = 4
|
||||
ROLE_EDIT = 8
|
||||
ROLE_PASSWD = 16
|
||||
ROLE_ANONYMOUS = 32
|
||||
DEFAULT_PASS = "admin123"
|
||||
|
||||
|
||||
class User(Base):
|
||||
__tablename__ = 'user'
|
||||
|
||||
id = Column(Integer, primary_key=True)
|
||||
nickname = Column(String(64), unique=True)
|
||||
email = Column(String(120), unique=True, default="")
|
||||
role = Column(SmallInteger, default=ROLE_USER)
|
||||
password = Column(String)
|
||||
kindle_mail = Column(String(120), default="")
|
||||
shelf = relationship('Shelf', backref='user', lazy='dynamic')
|
||||
downloads = relationship('Downloads', backref='user', lazy='dynamic')
|
||||
locale = Column(String(2), default="en")
|
||||
random_books = Column(Integer, default=1)
|
||||
language_books = Column(Integer, default=1)
|
||||
series_books = Column(Integer, default=1)
|
||||
category_books = Column(Integer, default=1)
|
||||
hot_books = Column(Integer, default=1)
|
||||
default_language = Column(String(3), default="all")
|
||||
|
||||
class UserBase():
|
||||
def is_authenticated(self):
|
||||
return True
|
||||
|
||||
@ -74,6 +60,12 @@ class User(Base):
|
||||
else:
|
||||
return False
|
||||
|
||||
def role_anonymous(self):
|
||||
if self.role is not None:
|
||||
return True if self.role & ROLE_ANONYMOUS == ROLE_ANONYMOUS else False
|
||||
else:
|
||||
return False
|
||||
|
||||
def is_active(self):
|
||||
return True
|
||||
|
||||
@ -105,6 +97,52 @@ class User(Base):
|
||||
return '<User %r>' % self.nickname
|
||||
|
||||
|
||||
class User(UserBase,Base):
|
||||
__tablename__ = 'user'
|
||||
|
||||
id = Column(Integer, primary_key=True)
|
||||
nickname = Column(String(64), unique=True)
|
||||
email = Column(String(120), unique=True, default="")
|
||||
role = Column(SmallInteger, default=ROLE_USER)
|
||||
password = Column(String)
|
||||
kindle_mail = Column(String(120), default="")
|
||||
shelf = relationship('Shelf', backref='user', lazy='dynamic')
|
||||
downloads = relationship('Downloads', backref='user', lazy='dynamic')
|
||||
locale = Column(String(2), default="en")
|
||||
random_books = Column(Integer, default=1)
|
||||
language_books = Column(Integer, default=1)
|
||||
series_books = Column(Integer, default=1)
|
||||
category_books = Column(Integer, default=1)
|
||||
hot_books = Column(Integer, default=1)
|
||||
default_language = Column(String(3), default="all")
|
||||
|
||||
|
||||
class Anonymous(AnonymousUserMixin,UserBase):
|
||||
def __init__(self):
|
||||
self.loadSettings()
|
||||
|
||||
def loadSettings(self):
|
||||
data=session.query(User).filter(User.role.op('&')(ROLE_ANONYMOUS) == ROLE_ANONYMOUS).first()
|
||||
self.nickname = data.nickname
|
||||
self.role = data.role
|
||||
self.random_books = data.random_books
|
||||
self.default_language = data.default_language
|
||||
self.language_books = data.language_books
|
||||
self.series_books = data.series_books
|
||||
self.category_books = data.category_books
|
||||
self.hot_books = data.hot_books
|
||||
self.default_language = data.default_language
|
||||
|
||||
def role_admin(self):
|
||||
return False
|
||||
|
||||
def is_active(self):
|
||||
return False
|
||||
|
||||
def is_anonymous(self):
|
||||
return config.ANON_BROWSE
|
||||
|
||||
|
||||
class Shelf(Base):
|
||||
__tablename__ = 'shelf'
|
||||
|
||||
@ -155,6 +193,8 @@ class Settings(Base):
|
||||
|
||||
|
||||
def migrate_Database():
|
||||
if session.query(User).filter(User.role.op('&')(ROLE_ANONYMOUS) == ROLE_ANONYMOUS).first() is None:
|
||||
create_anonymous_user()
|
||||
try:
|
||||
session.query(exists().where(User.random_books)).scalar()
|
||||
session.commit()
|
||||
@ -213,6 +253,20 @@ def get_mail_settings():
|
||||
|
||||
return data
|
||||
|
||||
def create_anonymous_user():
|
||||
user = User()
|
||||
user.nickname = _("Guest")
|
||||
user.email='no@email'
|
||||
user.role = ROLE_ANONYMOUS
|
||||
user.password = generate_password_hash('1')
|
||||
|
||||
session.add(user)
|
||||
try:
|
||||
session.commit()
|
||||
except:
|
||||
session.rollback()
|
||||
pass
|
||||
|
||||
|
||||
def create_admin_user():
|
||||
user = User()
|
||||
@ -236,6 +290,7 @@ if not os.path.exists(dbpath):
|
||||
Base.metadata.create_all(engine)
|
||||
create_default_config()
|
||||
create_admin_user()
|
||||
create_anonymous_user()
|
||||
except Exception:
|
||||
pass
|
||||
else:
|
||||
|
80
cps/web.py
80
cps/web.py
@ -14,7 +14,7 @@ from sqlalchemy.sql.expression import func
|
||||
from sqlalchemy.sql.expression import false
|
||||
from sqlalchemy.exc import IntegrityError
|
||||
from math import ceil
|
||||
from flask_login import LoginManager, login_user, logout_user, login_required, current_user, AnonymousUserMixin
|
||||
from flask_login import LoginManager, login_user, logout_user, login_required, current_user
|
||||
from flask_principal import Principal, Identity, AnonymousIdentity, identity_changed
|
||||
from flask_babel import Babel
|
||||
from flask_babel import gettext as _
|
||||
@ -115,49 +115,10 @@ global global_queue
|
||||
global_queue = None
|
||||
|
||||
|
||||
class Anonymous(AnonymousUserMixin):
|
||||
def __init__(self):
|
||||
self.nickname = 'Guest'
|
||||
self.role = -1
|
||||
|
||||
def role_admin(self):
|
||||
return False
|
||||
|
||||
def role_download(self):
|
||||
return False
|
||||
|
||||
def role_upload(self):
|
||||
return False
|
||||
|
||||
def role_edit(self):
|
||||
return False
|
||||
|
||||
def filter_language(self):
|
||||
return 'all'
|
||||
|
||||
def show_random_books(self):
|
||||
return True
|
||||
|
||||
def show_hot_books(self):
|
||||
return True
|
||||
|
||||
def show_series(self):
|
||||
return True
|
||||
|
||||
def show_category(self):
|
||||
return True
|
||||
|
||||
def show_language(self):
|
||||
return True
|
||||
|
||||
def is_anonymous(self):
|
||||
return config.ANON_BROWSE
|
||||
|
||||
|
||||
lm = LoginManager(app)
|
||||
lm.init_app(app)
|
||||
lm.login_view = 'login'
|
||||
lm.anonymous_user = Anonymous
|
||||
lm.anonymous_user = ub.Anonymous
|
||||
|
||||
app.secret_key = 'A0Zr98j/3yX R~XHH!jmN]LWX/,?RT'
|
||||
|
||||
@ -237,9 +198,9 @@ def requires_basic_auth_if_no_ano(f):
|
||||
# simple pagination for the feed
|
||||
class Pagination(object):
|
||||
def __init__(self, page, per_page, total_count):
|
||||
self.page = page
|
||||
self.per_page = per_page
|
||||
self.total_count = total_count
|
||||
self.page = int(page)
|
||||
self.per_page = int(per_page)
|
||||
self.total_count = int(total_count)
|
||||
|
||||
@property
|
||||
def next_offset(self):
|
||||
@ -247,7 +208,7 @@ class Pagination(object):
|
||||
|
||||
@property
|
||||
def previous_offset(self):
|
||||
return int((self.page-1) * self.per_page)
|
||||
return int((self.page-2) * self.per_page)
|
||||
|
||||
@property
|
||||
def last_offset(self):
|
||||
@ -453,11 +414,17 @@ def feed_osd():
|
||||
response.headers["Content-Type"] = "application/xml"
|
||||
return response
|
||||
|
||||
@app.route("/opds/search/<query>")
|
||||
def feed_cc_search(query):
|
||||
return feed_search(query.strip())
|
||||
|
||||
|
||||
@app.route("/opds/search", methods=["GET"])
|
||||
@requires_basic_auth_if_no_ano
|
||||
def feed_search():
|
||||
term = request.args.get("query").strip()
|
||||
def feed_normal_search():
|
||||
return feed_search(request.args.get("query").strip())
|
||||
|
||||
def feed_search(term):
|
||||
if current_user.filter_language() != "all":
|
||||
filter = db.Books.languages.any(db.Languages.lang_code == current_user.filter_language())
|
||||
else:
|
||||
@ -466,8 +433,8 @@ def feed_search():
|
||||
entries = db.session.query(db.Books).filter(db.or_(db.Books.tags.any(db.Tags.name.like("%" + term + "%")),
|
||||
db.Books.authors.any(db.Authors.name.like("%" + term + "%")),
|
||||
db.Books.title.like("%" + term + "%"))).filter(filter).all()
|
||||
|
||||
xml = render_template('feed.xml', searchterm=term, entries=entries)
|
||||
pagination = Pagination( 1,len(entries),len(entries))
|
||||
xml = render_template('feed.xml', searchterm=term, entries=entries, pagination=pagination)
|
||||
else:
|
||||
xml = render_template('feed.xml', searchterm="")
|
||||
response = make_response(xml)
|
||||
@ -1145,13 +1112,14 @@ def read_book(book_id, format):
|
||||
|
||||
|
||||
@app.route("/download/<int:book_id>/<format>")
|
||||
@login_required
|
||||
@login_required_if_no_ano
|
||||
@download_required
|
||||
def get_download_link(book_id, format):
|
||||
format = format.split(".")[0]
|
||||
book = db.session.query(db.Books).filter(db.Books.id == book_id).first()
|
||||
data = db.session.query(db.Data).filter(db.Data.book == book.id).filter(db.Data.format == format.upper()).first()
|
||||
helper.update_download(book_id, int(current_user.id))
|
||||
if current_user.is_authenticated: # collect downloaded books only for registered user and not for anonymous user
|
||||
helper.update_download(book_id, int(current_user.id))
|
||||
author = helper.get_normalized_author(book.author_sort)
|
||||
file_name = book.title
|
||||
if len(author) > 0:
|
||||
@ -1392,7 +1360,7 @@ def show_shelf(shelf_id):
|
||||
|
||||
|
||||
@app.route("/shelf/order/<int:shelf_id>", methods=["GET", "POST"])
|
||||
@login_required_if_no_ano
|
||||
@login_required
|
||||
def order_shelf(shelf_id):
|
||||
if request.method == "POST":
|
||||
to_save = request.form.to_dict()
|
||||
@ -1619,7 +1587,7 @@ def edit_user(user_id):
|
||||
flash(_(u"User '%(nick)s' deleted", nick=content.nickname), category="success")
|
||||
return redirect(url_for('admin'))
|
||||
else:
|
||||
if to_save["password"]:
|
||||
if "password" in to_save and to_save["password"]:
|
||||
content.password = generate_password_hash(to_save["password"])
|
||||
|
||||
if "admin_role" in to_save and not content.role_admin():
|
||||
@ -1663,7 +1631,7 @@ def edit_user(user_id):
|
||||
content.hot_books = 1
|
||||
if "default_language" in to_save:
|
||||
content.default_language = to_save["default_language"]
|
||||
if to_save["locale"]:
|
||||
if "locale" in to_save and to_save["locale"]:
|
||||
content.locale = to_save["locale"]
|
||||
if to_save["email"] and to_save["email"] != content.email:
|
||||
content.email = to_save["email"]
|
||||
@ -1680,7 +1648,7 @@ def edit_user(user_id):
|
||||
|
||||
|
||||
@app.route("/admin/book/<int:book_id>", methods=['GET', 'POST'])
|
||||
@login_required
|
||||
@login_required_if_no_ano
|
||||
@edit_required
|
||||
def edit_book(book_id):
|
||||
# create the function for sorting...
|
||||
@ -1889,7 +1857,7 @@ def edit_book(book_id):
|
||||
|
||||
|
||||
@app.route("/upload", methods=["GET", "POST"])
|
||||
@login_required
|
||||
@login_required_if_no_ano
|
||||
@upload_required
|
||||
def upload():
|
||||
if not config.UPLOADING:
|
||||
|
Loading…
Reference in New Issue
Block a user