Commit 0107c52b authored by OzzieIsaacs's avatar OzzieIsaacs

Anonymous account now can also be configured like normal user (e.g. download...

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
parent beca61a5
......@@ -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 %}
......
......@@ -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:
......
......@@ -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:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment