mirror of
https://github.com/spl0k/supysonic.git
synced 2024-12-22 17:06:17 +00:00
Web UI: reworked how data is passed to templates
Logged user is pushed in request data No more 'username' session key Small fixes along the way
This commit is contained in:
parent
9a58d067ab
commit
b998bb0684
@ -9,12 +9,11 @@
|
|||||||
#
|
#
|
||||||
# Distributed under terms of the GNU AGPLv3 license.
|
# Distributed under terms of the GNU AGPLv3 license.
|
||||||
|
|
||||||
from flask import session
|
from flask import session, request, redirect, url_for
|
||||||
from supysonic.web import app, store
|
from supysonic.web import app, store
|
||||||
from supysonic.db import Artist, Album, Track
|
from supysonic.db import Artist, Album, Track
|
||||||
from supysonic.managers.user import UserManager
|
from supysonic.managers.user import UserManager
|
||||||
|
from functools import wraps
|
||||||
app.add_template_filter(str)
|
|
||||||
|
|
||||||
@app.before_request
|
@app.before_request
|
||||||
def login_check():
|
def login_check():
|
||||||
@ -24,18 +23,17 @@ def login_check():
|
|||||||
if request.path.startswith('/static/'):
|
if request.path.startswith('/static/'):
|
||||||
return
|
return
|
||||||
|
|
||||||
if request.endpoint != 'login':
|
request.user = None
|
||||||
|
should_login = True
|
||||||
|
if session.get('userid'):
|
||||||
|
code, user = UserManager.get(store, session.get('userid'))
|
||||||
|
if code != UserManager.SUCCESS:
|
||||||
|
session.clear()
|
||||||
|
else:
|
||||||
|
request.user = user
|
||||||
should_login = False
|
should_login = False
|
||||||
if not session.get('userid'):
|
|
||||||
should_login = True
|
|
||||||
elif UserManager.get(store, session.get('userid'))[0] != UserManager.SUCCESS:
|
|
||||||
session.clear()
|
|
||||||
should_login = True
|
|
||||||
elif UserManager.get(store, session.get('userid'))[1].name != session.get('username'):
|
|
||||||
session.clear()
|
|
||||||
should_login = True
|
|
||||||
|
|
||||||
if should_login:
|
if should_login and request.endpoint != 'login':
|
||||||
flash('Please login')
|
flash('Please login')
|
||||||
return redirect(url_for('login', returnUrl = request.script_root + request.url[len(request.url_root)-1:]))
|
return redirect(url_for('login', returnUrl = request.script_root + request.url[len(request.url_root)-1:]))
|
||||||
|
|
||||||
@ -48,6 +46,14 @@ def index():
|
|||||||
}
|
}
|
||||||
return render_template('home.html', stats = stats, admin = UserManager.get(store, session.get('userid'))[1].admin)
|
return render_template('home.html', stats = stats, admin = UserManager.get(store, session.get('userid'))[1].admin)
|
||||||
|
|
||||||
|
def admin_only(f):
|
||||||
|
@wraps(f)
|
||||||
|
def decorated_func(*args, **kwargs):
|
||||||
|
if not request.user or not request.user.admin:
|
||||||
|
return redirect(url_for('index'))
|
||||||
|
return f(*args, **kwargs)
|
||||||
|
return decorated_func
|
||||||
|
|
||||||
from .user import *
|
from .user import *
|
||||||
from .folder import *
|
from .folder import *
|
||||||
from .playlist import *
|
from .playlist import *
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
# This file is part of Supysonic.
|
# This file is part of Supysonic.
|
||||||
#
|
#
|
||||||
# Supysonic is a Python implementation of the Subsonic server API.
|
# Supysonic is a Python implementation of the Subsonic server API.
|
||||||
# Copyright (C) 2013 Alban 'spl0k' Féron
|
# Copyright (C) 2013-2017 Alban 'spl0k' Féron
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU Affero General Public License as published by
|
# it under the terms of the GNU Affero General Public License as published by
|
||||||
@ -18,7 +18,7 @@
|
|||||||
# You should have received a copy of the GNU Affero General Public License
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from flask import request, flash, render_template, redirect, url_for, session
|
from flask import request, flash, render_template, redirect, url_for
|
||||||
import os.path
|
import os.path
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
@ -28,23 +28,21 @@ from supysonic.scanner import Scanner
|
|||||||
from supysonic.managers.user import UserManager
|
from supysonic.managers.user import UserManager
|
||||||
from supysonic.managers.folder import FolderManager
|
from supysonic.managers.folder import FolderManager
|
||||||
|
|
||||||
@app.before_request
|
from . import admin_only
|
||||||
def check_admin():
|
|
||||||
if not request.path.startswith('/folder'):
|
|
||||||
return
|
|
||||||
|
|
||||||
if not UserManager.get(store, session.get('userid'))[1].admin:
|
|
||||||
return redirect(url_for('index'))
|
|
||||||
|
|
||||||
@app.route('/folder')
|
@app.route('/folder')
|
||||||
|
@admin_only
|
||||||
def folder_index():
|
def folder_index():
|
||||||
return render_template('folders.html', folders = store.find(Folder, Folder.root == True), admin = UserManager.get(store, session.get('userid'))[1].admin)
|
return render_template('folders.html', folders = store.find(Folder, Folder.root == True))
|
||||||
|
|
||||||
@app.route('/folder/add', methods = [ 'GET', 'POST' ])
|
@app.route('/folder/add')
|
||||||
def add_folder():
|
@admin_only
|
||||||
if request.method == 'GET':
|
def add_folder_form():
|
||||||
return render_template('addfolder.html', admin = UserManager.get(store, session.get('userid'))[1].admin)
|
return render_template('addfolder.html')
|
||||||
|
|
||||||
|
@app.route('/folder/add', methods = [ 'POST' ])
|
||||||
|
@admin_only
|
||||||
|
def add_folder_post():
|
||||||
error = False
|
error = False
|
||||||
(name, path) = map(request.form.get, [ 'name', 'path' ])
|
(name, path) = map(request.form.get, [ 'name', 'path' ])
|
||||||
if name in (None, ''):
|
if name in (None, ''):
|
||||||
@ -54,18 +52,19 @@ def add_folder():
|
|||||||
flash('The path is required.')
|
flash('The path is required.')
|
||||||
error = True
|
error = True
|
||||||
if error:
|
if error:
|
||||||
return render_template('addfolder.html', admin = UserManager.get(store, session.get('userid'))[1].admin)
|
return render_template('addfolder.html')
|
||||||
|
|
||||||
ret = FolderManager.add(store, name, path)
|
ret = FolderManager.add(store, name, path)
|
||||||
if ret != FolderManager.SUCCESS:
|
if ret != FolderManager.SUCCESS:
|
||||||
flash(FolderManager.error_str(ret))
|
flash(FolderManager.error_str(ret))
|
||||||
return render_template('addfolder.html', admin = UserManager.get(store, session.get('userid'))[1].admin)
|
return render_template('addfolder.html')
|
||||||
|
|
||||||
flash("Folder '%s' created. You should now run a scan" % name)
|
flash("Folder '%s' created. You should now run a scan" % name)
|
||||||
|
|
||||||
return redirect(url_for('folder_index'))
|
return redirect(url_for('folder_index'))
|
||||||
|
|
||||||
@app.route('/folder/del/<id>')
|
@app.route('/folder/del/<id>')
|
||||||
|
@admin_only
|
||||||
def del_folder(id):
|
def del_folder(id):
|
||||||
try:
|
try:
|
||||||
idid = uuid.UUID(id)
|
idid = uuid.UUID(id)
|
||||||
@ -83,6 +82,7 @@ def del_folder(id):
|
|||||||
|
|
||||||
@app.route('/folder/scan')
|
@app.route('/folder/scan')
|
||||||
@app.route('/folder/scan/<id>')
|
@app.route('/folder/scan/<id>')
|
||||||
|
@admin_only
|
||||||
def scan_folder(id = None):
|
def scan_folder(id = None):
|
||||||
scanner = Scanner(store)
|
scanner = Scanner(store)
|
||||||
if id is None:
|
if id is None:
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
# This file is part of Supysonic.
|
# This file is part of Supysonic.
|
||||||
#
|
#
|
||||||
# Supysonic is a Python implementation of the Subsonic server API.
|
# Supysonic is a Python implementation of the Subsonic server API.
|
||||||
# Copyright (C) 2013 Alban 'spl0k' Féron
|
# Copyright (C) 2013-2017 Alban 'spl0k' Féron
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU Affero General Public License as published by
|
# it under the terms of the GNU Affero General Public License as published by
|
||||||
@ -18,7 +18,7 @@
|
|||||||
# You should have received a copy of the GNU Affero General Public License
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from flask import request, session, flash, render_template, redirect, url_for
|
from flask import request, flash, render_template, redirect, url_for
|
||||||
import uuid
|
import uuid
|
||||||
from supysonic.web import app, store
|
from supysonic.web import app, store
|
||||||
from supysonic.db import Playlist
|
from supysonic.db import Playlist
|
||||||
@ -26,9 +26,9 @@ from supysonic.managers.user import UserManager
|
|||||||
|
|
||||||
@app.route('/playlist')
|
@app.route('/playlist')
|
||||||
def playlist_index():
|
def playlist_index():
|
||||||
return render_template('playlists.html', mine = store.find(Playlist, Playlist.user_id == uuid.UUID(session.get('userid'))),
|
return render_template('playlists.html',
|
||||||
others = store.find(Playlist, Playlist.user_id != uuid.UUID(session.get('userid')), Playlist.public == True),
|
mine = store.find(Playlist, Playlist.user_id == request.user.id),
|
||||||
admin = UserManager.get(store, session.get('userid'))[1].admin)
|
others = store.find(Playlist, Playlist.user_id != request.user.id, Playlist.public == True))
|
||||||
|
|
||||||
@app.route('/playlist/<uid>')
|
@app.route('/playlist/<uid>')
|
||||||
def playlist_details(uid):
|
def playlist_details(uid):
|
||||||
@ -43,7 +43,7 @@ def playlist_details(uid):
|
|||||||
flash('Unknown playlist')
|
flash('Unknown playlist')
|
||||||
return redirect(url_for('playlist_index'))
|
return redirect(url_for('playlist_index'))
|
||||||
|
|
||||||
return render_template('playlist.html', playlist = playlist, admin = UserManager.get(store, session.get('userid'))[1].admin)
|
return render_template('playlist.html', playlist = playlist)
|
||||||
|
|
||||||
@app.route('/playlist/<uid>', methods = [ 'POST' ])
|
@app.route('/playlist/<uid>', methods = [ 'POST' ])
|
||||||
def playlist_update(uid):
|
def playlist_update(uid):
|
||||||
@ -58,7 +58,7 @@ def playlist_update(uid):
|
|||||||
flash('Unknown playlist')
|
flash('Unknown playlist')
|
||||||
return redirect(url_for('playlist_index'))
|
return redirect(url_for('playlist_index'))
|
||||||
|
|
||||||
if str(playlist.user_id) != session.get('userid'):
|
if playlist.user_id != request.user.id:
|
||||||
flash("You're not allowed to edit this playlist")
|
flash("You're not allowed to edit this playlist")
|
||||||
elif not request.form.get('name'):
|
elif not request.form.get('name'):
|
||||||
flash('Missing playlist name')
|
flash('Missing playlist name')
|
||||||
@ -81,7 +81,7 @@ def playlist_delete(uid):
|
|||||||
playlist = store.get(Playlist, uid)
|
playlist = store.get(Playlist, uid)
|
||||||
if not playlist:
|
if not playlist:
|
||||||
flash('Unknown playlist')
|
flash('Unknown playlist')
|
||||||
elif str(playlist.user_id) != session.get('userid'):
|
elif playlist.user_id != request.user.id:
|
||||||
flash("You're not allowed to delete this playlist")
|
flash("You're not allowed to delete this playlist")
|
||||||
else:
|
else:
|
||||||
store.remove(playlist)
|
store.remove(playlist)
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
# This file is part of Supysonic.
|
# This file is part of Supysonic.
|
||||||
#
|
#
|
||||||
# Supysonic is a Python implementation of the Subsonic server API.
|
# Supysonic is a Python implementation of the Subsonic server API.
|
||||||
# Copyright (C) 2013 Alban 'spl0k' Féron
|
# Copyright (C) 2013-2017 Alban 'spl0k' Féron
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU Affero General Public License as published by
|
# it under the terms of the GNU Affero General Public License as published by
|
||||||
@ -27,28 +27,27 @@ import uuid, csv
|
|||||||
from supysonic import config
|
from supysonic import config
|
||||||
from supysonic.lastfm import LastFm
|
from supysonic.lastfm import LastFm
|
||||||
|
|
||||||
@app.before_request
|
from . import admin_only
|
||||||
def check_admin():
|
|
||||||
if not request.path.startswith('/user'):
|
|
||||||
return
|
|
||||||
|
|
||||||
if request.endpoint in ('user_index', 'add_user', 'del_user', 'export_users', 'import_users', 'do_user_import') and not UserManager.get(store, session.get('userid'))[1].admin:
|
|
||||||
return redirect(url_for('index'))
|
|
||||||
|
|
||||||
@app.route('/user')
|
@app.route('/user')
|
||||||
|
@admin_only
|
||||||
def user_index():
|
def user_index():
|
||||||
return render_template('users.html', users = store.find(User), admin = UserManager.get(store, session.get('userid'))[1].admin)
|
return render_template('users.html', users = store.find(User))
|
||||||
|
|
||||||
@app.route('/user/<uid>')
|
@app.route('/user/<uid>')
|
||||||
def user_profile(uid):
|
def user_profile(uid):
|
||||||
if uid == 'me':
|
if uid == 'me':
|
||||||
prefs = store.find(ClientPrefs, ClientPrefs.user_id == uuid.UUID(session.get('userid')))
|
user = request.user
|
||||||
return render_template('profile.html', user = UserManager.get(store, session.get('userid'))[1], api_key = config.get('lastfm', 'api_key'), clients = prefs, admin = UserManager.get(store, session.get('userid'))[1].admin)
|
elif not request.user.admin:
|
||||||
else:
|
|
||||||
if not UserManager.get(store, session.get('userid'))[1].admin or not UserManager.get(store, uid)[0] is UserManager.SUCCESS:
|
|
||||||
return redirect(url_for('index'))
|
return redirect(url_for('index'))
|
||||||
prefs = store.find(ClientPrefs, ClientPrefs.user_id == uuid.UUID(uid))
|
else:
|
||||||
return render_template('profile.html', user = UserManager.get(store, uid)[1], api_key = config.get('lastfm', 'api_key'), clients = prefs, admin = UserManager.get(store, session.get('userid'))[1].admin)
|
code, user = UserManager.get(store, uid)
|
||||||
|
if code != UserManager.SUCCESS:
|
||||||
|
flash(UserManager.error_str(code))
|
||||||
|
return redirect(url_for('index'))
|
||||||
|
|
||||||
|
prefs = store.find(ClientPrefs, ClientPrefs.user_id == user.id)
|
||||||
|
return render_template('profile.html', user = user, has_lastfm = config.get('lastfm', 'api_key') != None, clients = prefs)
|
||||||
|
|
||||||
@app.route('/user/<uid>', methods = [ 'POST' ])
|
@app.route('/user/<uid>', methods = [ 'POST' ])
|
||||||
def update_clients(uid):
|
def update_clients(uid):
|
||||||
@ -58,9 +57,9 @@ def update_clients(uid):
|
|||||||
app.logger.debug(clients_opts)
|
app.logger.debug(clients_opts)
|
||||||
|
|
||||||
if uid == 'me':
|
if uid == 'me':
|
||||||
userid = uuid.UUID(session.get('userid'))
|
userid = request.user.id
|
||||||
else:
|
else:
|
||||||
if not UserManager.get(store, session.get('userid'))[1].admin or not UserManager.get(store, uid)[0] is UserManager.SUCCESS:
|
if not request.user.admin or not UserManager.get(store, uid)[0] is UserManager.SUCCESS:
|
||||||
return redirect(url_for('index'))
|
return redirect(url_for('index'))
|
||||||
userid = uuid.UUID(uid)
|
userid = uuid.UUID(uid)
|
||||||
|
|
||||||
@ -78,20 +77,22 @@ def update_clients(uid):
|
|||||||
return user_profile(uid)
|
return user_profile(uid)
|
||||||
|
|
||||||
@app.route('/user/<uid>/changeusername', methods = [ 'GET', 'POST' ])
|
@app.route('/user/<uid>/changeusername', methods = [ 'GET', 'POST' ])
|
||||||
|
@admin_only
|
||||||
def change_username(uid):
|
def change_username(uid):
|
||||||
if not UserManager.get(store, session.get('userid'))[1].admin or not UserManager.get(store, uid)[0] is UserManager.SUCCESS:
|
code, user = UserManager.get(store, uid)
|
||||||
|
if code != UserManager.SUCCESS:
|
||||||
return redirect(url_for('index'))
|
return redirect(url_for('index'))
|
||||||
user = UserManager.get(store, uid)[1]
|
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
username = request.form.get('user')
|
username = request.form.get('user')
|
||||||
if username in ('', None):
|
if username in ('', None):
|
||||||
flash('The username is required')
|
flash('The username is required')
|
||||||
return render_template('change_username.html', user = user, admin = UserManager.get(store, session.get('userid'))[1].admin)
|
return render_template('change_username.html', user = user)
|
||||||
if request.form.get('admin') is None:
|
if request.form.get('admin') is None:
|
||||||
admin = False
|
admin = False
|
||||||
else:
|
else:
|
||||||
admin = True
|
admin = True
|
||||||
changed = False
|
|
||||||
if user.name != username or user.admin != admin:
|
if user.name != username or user.admin != admin:
|
||||||
user.name = username
|
user.name = username
|
||||||
user.admin = admin
|
user.admin = admin
|
||||||
@ -102,16 +103,19 @@ def change_username(uid):
|
|||||||
flash("No changes for '%s'." % username)
|
flash("No changes for '%s'." % username)
|
||||||
return redirect(url_for('user_profile', uid = uid))
|
return redirect(url_for('user_profile', uid = uid))
|
||||||
|
|
||||||
return render_template('change_username.html', user = user, admin = UserManager.get(store, session.get('userid'))[1].admin)
|
return render_template('change_username.html', user = user)
|
||||||
|
|
||||||
@app.route('/user/<uid>/changemail', methods = [ 'GET', 'POST' ])
|
@app.route('/user/<uid>/changemail', methods = [ 'GET', 'POST' ])
|
||||||
def change_mail(uid):
|
def change_mail(uid):
|
||||||
if uid == 'me':
|
if uid == 'me':
|
||||||
user = UserManager.get(store, session.get('userid'))[1]
|
user = request.user
|
||||||
else:
|
elif not request.user.admin:
|
||||||
if not UserManager.get(store, session.get('userid'))[1].admin or not UserManager.get(store, uid)[0] is UserManager.SUCCESS:
|
|
||||||
return redirect(url_for('index'))
|
return redirect(url_for('index'))
|
||||||
user = UserManager.get(store, uid)[1]
|
else:
|
||||||
|
code, user = UserManager.get(store, uid)
|
||||||
|
if code != UserManager.SUCCESS:
|
||||||
|
return redirect(url_for('index'))
|
||||||
|
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
mail = request.form.get('mail')
|
mail = request.form.get('mail')
|
||||||
# No validation, lol.
|
# No validation, lol.
|
||||||
@ -119,25 +123,29 @@ def change_mail(uid):
|
|||||||
store.commit()
|
store.commit()
|
||||||
return redirect(url_for('user_profile', uid = uid))
|
return redirect(url_for('user_profile', uid = uid))
|
||||||
|
|
||||||
return render_template('change_mail.html', user = user, admin = UserManager.get(store, session.get('userid'))[1].admin)
|
return render_template('change_mail.html', user = user)
|
||||||
|
|
||||||
@app.route('/user/<uid>/changepass', methods = [ 'GET', 'POST' ])
|
@app.route('/user/<uid>/changepass', methods = [ 'GET', 'POST' ])
|
||||||
def change_password(uid):
|
def change_password(uid):
|
||||||
if uid == 'me':
|
if uid == 'me':
|
||||||
user = UserManager.get(store, session.get('userid'))[1].name
|
user = request.user
|
||||||
else:
|
elif not request.user.admin:
|
||||||
if not UserManager.get(store, session.get('userid'))[1].admin or not UserManager.get(store, uid)[0] is UserManager.SUCCESS:
|
|
||||||
return redirect(url_for('index'))
|
return redirect(url_for('index'))
|
||||||
user = UserManager.get(store, uid)[1].name
|
else:
|
||||||
|
code, user = UserManager.get(store, uid)
|
||||||
|
if code != UserManager.SUCCESS:
|
||||||
|
return redirect(url_for('index'))
|
||||||
|
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
error = False
|
error = False
|
||||||
if uid == 'me' or uid == session.get('userid'):
|
if uid == 'me' or uid == str(request.user.id):
|
||||||
current, new, confirm = map(request.form.get, [ 'current', 'new', 'confirm' ])
|
current, new, confirm = map(request.form.get, [ 'current', 'new', 'confirm' ])
|
||||||
if current in ('', None):
|
if current in ('', None):
|
||||||
flash('The current password is required')
|
flash('The current password is required')
|
||||||
error = True
|
error = True
|
||||||
else:
|
else:
|
||||||
new, confirm = map(request.form.get, [ 'new', 'confirm' ])
|
new, confirm = map(request.form.get, [ 'new', 'confirm' ])
|
||||||
|
|
||||||
if new in ('', None):
|
if new in ('', None):
|
||||||
flash('The new password is required')
|
flash('The new password is required')
|
||||||
error = True
|
error = True
|
||||||
@ -146,22 +154,24 @@ def change_password(uid):
|
|||||||
error = True
|
error = True
|
||||||
|
|
||||||
if not error:
|
if not error:
|
||||||
if uid == 'me' or uid == session.get('userid'):
|
if uid == 'me' or uid == str(request.user.id):
|
||||||
status = UserManager.change_password(store, session.get('userid'), current, new)
|
status = UserManager.change_password(store, user.id, current, new)
|
||||||
else:
|
else:
|
||||||
status = UserManager.change_password2(store, UserManager.get(store, uid)[1].name, new)
|
status = UserManager.change_password2(store, user.name, new)
|
||||||
|
|
||||||
if status != UserManager.SUCCESS:
|
if status != UserManager.SUCCESS:
|
||||||
flash(UserManager.error_str(status))
|
flash(UserManager.error_str(status))
|
||||||
else:
|
else:
|
||||||
flash('Password changed')
|
flash('Password changed')
|
||||||
return redirect(url_for('user_profile', uid = uid))
|
return redirect(url_for('user_profile', uid = uid))
|
||||||
|
|
||||||
return render_template('change_pass.html', user = user, admin = UserManager.get(store, session.get('userid'))[1].admin)
|
return render_template('change_pass.html', user = user)
|
||||||
|
|
||||||
@app.route('/user/add', methods = [ 'GET', 'POST' ])
|
@app.route('/user/add', methods = [ 'GET', 'POST' ])
|
||||||
|
@admin_only
|
||||||
def add_user():
|
def add_user():
|
||||||
if request.method == 'GET':
|
if request.method == 'GET':
|
||||||
return render_template('adduser.html', admin = UserManager.get(store, session.get('userid'))[1].admin)
|
return render_template('adduser.html')
|
||||||
|
|
||||||
error = False
|
error = False
|
||||||
(name, passwd, passwd_confirm, mail, admin) = map(request.form.get, [ 'user', 'passwd', 'passwd_confirm', 'mail', 'admin' ])
|
(name, passwd, passwd_confirm, mail, admin) = map(request.form.get, [ 'user', 'passwd', 'passwd_confirm', 'mail', 'admin' ])
|
||||||
@ -188,9 +198,10 @@ def add_user():
|
|||||||
else:
|
else:
|
||||||
flash(UserManager.error_str(status))
|
flash(UserManager.error_str(status))
|
||||||
|
|
||||||
return render_template('adduser.html', admin = UserManager.get(store, session.get('userid'))[1].admin)
|
return render_template('adduser.html')
|
||||||
|
|
||||||
@app.route('/user/del/<uid>')
|
@app.route('/user/del/<uid>')
|
||||||
|
@admin_only
|
||||||
def del_user(uid):
|
def del_user(uid):
|
||||||
status = UserManager.delete(store, uid)
|
status = UserManager.delete(store, uid)
|
||||||
if status == UserManager.SUCCESS:
|
if status == UserManager.SUCCESS:
|
||||||
@ -201,6 +212,7 @@ def del_user(uid):
|
|||||||
return redirect(url_for('user_index'))
|
return redirect(url_for('user_index'))
|
||||||
|
|
||||||
@app.route('/user/export')
|
@app.route('/user/export')
|
||||||
|
@admin_only
|
||||||
def export_users():
|
def export_users():
|
||||||
resp = make_response('\n'.join([ '%s,%s,%s,%s,"%s",%s,%s,%s' % (u.id, u.name, u.mail, u.password, u.salt, u.admin, u.lastfm_session, u.lastfm_status)
|
resp = make_response('\n'.join([ '%s,%s,%s,%s,"%s",%s,%s,%s' % (u.id, u.name, u.mail, u.password, u.salt, u.admin, u.lastfm_session, u.lastfm_status)
|
||||||
for u in store.find(User) ]))
|
for u in store.find(User) ]))
|
||||||
@ -209,13 +221,15 @@ def export_users():
|
|||||||
return resp
|
return resp
|
||||||
|
|
||||||
@app.route('/user/import')
|
@app.route('/user/import')
|
||||||
|
@admin_only
|
||||||
def import_users():
|
def import_users():
|
||||||
return render_template('importusers.html', admin = UserManager.get(store, session.get('userid'))[1].admin)
|
return render_template('importusers.html')
|
||||||
|
|
||||||
@app.route('/user/import', methods = [ 'POST' ])
|
@app.route('/user/import', methods = [ 'POST' ])
|
||||||
|
@admin_only
|
||||||
def do_user_import():
|
def do_user_import():
|
||||||
if not request.files['file']:
|
if not request.files['file']:
|
||||||
return render_template('importusers.html', admin = UserManager.get(store, session.get('userid'))[1].admin)
|
return render_template('importusers.html')
|
||||||
|
|
||||||
users = []
|
users = []
|
||||||
reader = csv.reader(request.files['file'])
|
reader = csv.reader(request.files['file'])
|
||||||
@ -251,11 +265,15 @@ def lastfm_reg(uid):
|
|||||||
return redirect(url_for('user_profile', uid = uid))
|
return redirect(url_for('user_profile', uid = uid))
|
||||||
|
|
||||||
if uid == 'me':
|
if uid == 'me':
|
||||||
lfm = LastFm(UserManager.get(store, session.get('userid'))[1], app.logger)
|
user = request.user
|
||||||
else:
|
elif not request.user.admin:
|
||||||
if not UserManager.get(store, session.get('userid'))[1].admin or not UserManager.get(store, uid)[0] is UserManager.SUCCESS:
|
|
||||||
return redirect(url_for('index'))
|
return redirect(url_for('index'))
|
||||||
lfm = LastFm(UserManager.get(store, uid)[1], app.logger)
|
else:
|
||||||
|
code, user = UserManager.get(store, uid)
|
||||||
|
if code != UserManager.SUCCESS:
|
||||||
|
return redirect(url_for('index'))
|
||||||
|
|
||||||
|
lfm = LastFm(user, app.logger)
|
||||||
status, error = lfm.link_account(token)
|
status, error = lfm.link_account(token)
|
||||||
store.commit()
|
store.commit()
|
||||||
flash(error if not status else 'Successfully linked LastFM account')
|
flash(error if not status else 'Successfully linked LastFM account')
|
||||||
@ -265,11 +283,15 @@ def lastfm_reg(uid):
|
|||||||
@app.route('/user/<uid>/lastfm/unlink')
|
@app.route('/user/<uid>/lastfm/unlink')
|
||||||
def lastfm_unreg(uid):
|
def lastfm_unreg(uid):
|
||||||
if uid == 'me':
|
if uid == 'me':
|
||||||
lfm = LastFm(UserManager.get(store, session.get('userid'))[1], app.logger)
|
user = request.user
|
||||||
else:
|
elif not request.user.admin:
|
||||||
if not UserManager.get(store, session.get('userid'))[1].admin or not UserManager.get(store, uid)[0] is UserManager.SUCCESS:
|
|
||||||
return redirect(url_for('index'))
|
return redirect(url_for('index'))
|
||||||
lfm = LastFm(UserManager.get(store, uid)[1], app.logger)
|
else:
|
||||||
|
code, user = UserManager.get(store, uid)
|
||||||
|
if code != UserManager.SUCCESS:
|
||||||
|
return redirect(url_for('index'))
|
||||||
|
|
||||||
|
lfm = LastFm(user, app.logger)
|
||||||
lfm.unlink_account()
|
lfm.unlink_account()
|
||||||
store.commit()
|
store.commit()
|
||||||
flash('Unliked LastFM account')
|
flash('Unliked LastFM account')
|
||||||
@ -278,7 +300,7 @@ def lastfm_unreg(uid):
|
|||||||
@app.route('/user/login', methods = [ 'GET', 'POST'])
|
@app.route('/user/login', methods = [ 'GET', 'POST'])
|
||||||
def login():
|
def login():
|
||||||
return_url = request.args.get('returnUrl') or url_for('index')
|
return_url = request.args.get('returnUrl') or url_for('index')
|
||||||
if session.get('userid'):
|
if request.user:
|
||||||
flash('Already logged in')
|
flash('Already logged in')
|
||||||
return redirect(return_url)
|
return redirect(return_url)
|
||||||
|
|
||||||
@ -298,7 +320,6 @@ def login():
|
|||||||
status, user = UserManager.try_auth(store, name, password)
|
status, user = UserManager.try_auth(store, name, password)
|
||||||
if status == UserManager.SUCCESS:
|
if status == UserManager.SUCCESS:
|
||||||
session['userid'] = str(user.id)
|
session['userid'] = str(user.id)
|
||||||
session['username'] = user.name
|
|
||||||
flash('Logged in!')
|
flash('Logged in!')
|
||||||
return redirect(return_url)
|
return redirect(return_url)
|
||||||
else:
|
else:
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
-#}
|
-#}
|
||||||
{% extends "layout.html" %}
|
{% extends "layout.html" %}
|
||||||
{% block navbar_users %}
|
{% block navbar_users %}
|
||||||
{% if session.username != user.name %}
|
{% if request.user.id != user.id %}
|
||||||
<li class="active"><a href="{{ url_for('user_index') }}">Users <span
|
<li class="active"><a href="{{ url_for('user_index') }}">Users <span
|
||||||
class="sr-only">(current)</span></a></li>
|
class="sr-only">(current)</span></a></li>
|
||||||
{% else %}
|
{% else %}
|
||||||
@ -17,8 +17,8 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block navbar_profile %}
|
{% block navbar_profile %}
|
||||||
{% if session.username == user.name %}
|
{% if request.user.id == user.id %}
|
||||||
<li class="active"><a href="{{ url_for('user_profile', uid = 'me') }}">{{ session.username }} <span class="sr-only">(current)</span></a></li>
|
<li class="active"><a href="{{ url_for('user_profile', uid = 'me') }}">{{ request.user.name }} <span class="sr-only">(current)</span></a></li>
|
||||||
{% else %}
|
{% else %}
|
||||||
{{ super() }}
|
{{ super() }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
-#}
|
-#}
|
||||||
{% extends "layout.html" %}
|
{% extends "layout.html" %}
|
||||||
{% block navbar_users %}
|
{% block navbar_users %}
|
||||||
{% if session.username != user %}
|
{% if request.user.id != user.id %}
|
||||||
<li class="active"><a href="{{ url_for('user_index') }}">Users <span
|
<li class="active"><a href="{{ url_for('user_index') }}">Users <span
|
||||||
class="sr-only">(current)</span></a></li>
|
class="sr-only">(current)</span></a></li>
|
||||||
{% else %}
|
{% else %}
|
||||||
@ -17,8 +17,8 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block navbar_profile %}
|
{% block navbar_profile %}
|
||||||
{% if session.username == user %}
|
{% if request.user.id == user.id %}
|
||||||
<li class="active"><a href="{{ url_for('user_profile', uid = 'me') }}">{{ session.username }} <span class="sr-only">(current)</span></a></li>
|
<li class="active"><a href="{{ url_for('user_profile', uid = 'me') }}">{{ request.user.name }} <span class="sr-only">(current)</span></a></li>
|
||||||
{% else %}
|
{% else %}
|
||||||
{{ super() }}
|
{{ super() }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -28,7 +28,7 @@
|
|||||||
<h2>{{ user }}</h2>
|
<h2>{{ user }}</h2>
|
||||||
</div>
|
</div>
|
||||||
<form method="post">
|
<form method="post">
|
||||||
{% if session.username == user %}
|
{% if request.user.id == user.id %}
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="sr-only" for="current">Current password</label>
|
<label class="sr-only" for="current">Current password</label>
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
|
@ -33,7 +33,7 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<div class="btn-toolbar" role="toolbar">
|
<div class="btn-toolbar" role="toolbar">
|
||||||
<a href="{{ url_for('add_folder') }}" class="btn btn-default">Add</a>
|
<a href="{{ url_for('add_folder_form') }}" class="btn btn-default">Add</a>
|
||||||
<a href="{{ url_for('scan_folder') }}" class="btn btn-default">Scan all</a>
|
<a href="{{ url_for('scan_folder') }}" class="btn btn-default">Scan all</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal fade" id="confirm-delete" tabindex="-1" role="dialog">
|
<div class="modal fade" id="confirm-delete" tabindex="-1" role="dialog">
|
||||||
|
@ -56,7 +56,7 @@
|
|||||||
{% block navbar_playlists %}
|
{% block navbar_playlists %}
|
||||||
<li><a href="{{ url_for('playlist_index') }}">Playlists</a></li>
|
<li><a href="{{ url_for('playlist_index') }}">Playlists</a></li>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% if admin %}
|
{% if request.user.admin %}
|
||||||
{% block navbar_users %}
|
{% block navbar_users %}
|
||||||
<li><a href="{{ url_for('user_index') }}">Users</a></li>
|
<li><a href="{{ url_for('user_index') }}">Users</a></li>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@ -65,7 +65,7 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% block navbar_profile %}
|
{% block navbar_profile %}
|
||||||
<li><a href="{{ url_for('user_profile', uid = 'me')}}">{{ session.username }}</a></li>
|
<li><a href="{{ url_for('user_profile', uid = 'me')}}">{{ request.user.name }}</a></li>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
</ul>
|
</ul>
|
||||||
<ul class="nav navbar-nav navbar-right">
|
<ul class="nav navbar-nav navbar-right">
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
<div class="page-header first-header">
|
<div class="page-header first-header">
|
||||||
<h2>Playlist "{{ playlist.name }}"</h2>
|
<h2>Playlist "{{ playlist.name }}"</h2>
|
||||||
</div>
|
</div>
|
||||||
{% if playlist.user_id|str == session.get('userid') %}
|
{% if playlist.user_id == request.user.id %}
|
||||||
<h3>Edit</h3>
|
<h3>Edit</h3>
|
||||||
<form method="post">
|
<form method="post">
|
||||||
<table id="playlist" class="table">
|
<table id="playlist" class="table">
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
-#}
|
-#}
|
||||||
{% extends "layout.html" %}
|
{% extends "layout.html" %}
|
||||||
{% block navbar_users %}
|
{% block navbar_users %}
|
||||||
{% if session.username != user.name %}
|
{% if request.user.id != user.id %}
|
||||||
<li class="active"><a href="{{ url_for('user_index') }}">Users <span
|
<li class="active"><a href="{{ url_for('user_index') }}">Users <span
|
||||||
class="sr-only">(current)</span></a></li>
|
class="sr-only">(current)</span></a></li>
|
||||||
{% else %}
|
{% else %}
|
||||||
@ -17,8 +17,8 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block navbar_profile %}
|
{% block navbar_profile %}
|
||||||
{% if session.username == user.name %}
|
{% if request.user.id == user.id %}
|
||||||
<li class="active"><a href="{{ url_for('user_profile', uid = 'me') }}">{{ session.username }} <span class="sr-only">(current)</span></a></li>
|
<li class="active"><a href="{{ url_for('user_profile', uid = 'me') }}">{{ request.user.name }} <span class="sr-only">(current)</span></a></li>
|
||||||
{% else %}
|
{% else %}
|
||||||
{{ super() }}
|
{{ super() }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -27,7 +27,7 @@
|
|||||||
<div class="page-header first-header">
|
<div class="page-header first-header">
|
||||||
<h2>{{ user.name }}{% if user.admin %} <small><span class="glyphicon
|
<h2>{{ user.name }}{% if user.admin %} <small><span class="glyphicon
|
||||||
glyphicon-certificate" data-toggle="tooltip" data-placement="right"
|
glyphicon-certificate" data-toggle="tooltip" data-placement="right"
|
||||||
title="{% if session.username == user.name %}You're an admin!{% else %}The user is an admin!{% endif %}"></span></small>{% endif %}</h2>
|
title="{% if request.user.id == user.id %}You're an admin!{% else %}The user is an admin!{% endif %}"></span></small>{% endif %}</h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
@ -38,7 +38,7 @@
|
|||||||
<div class="input-group-addon">User eMail</div>
|
<div class="input-group-addon">User eMail</div>
|
||||||
<input type="text" class="form-control" id="email" placeholder="{{ user.mail }}" readonly>
|
<input type="text" class="form-control" id="email" placeholder="{{ user.mail }}" readonly>
|
||||||
<div class="input-group-btn">
|
<div class="input-group-btn">
|
||||||
{% if session.username == user.name %}
|
{% if request.user.id == user.id %}
|
||||||
<a href="{{ url_for('change_mail', uid = 'me') }}" class="btn btn-default">Change eMail</a>
|
<a href="{{ url_for('change_mail', uid = 'me') }}" class="btn btn-default">Change eMail</a>
|
||||||
{% else %}
|
{% else %}
|
||||||
<a href="{{ url_for('change_mail', uid = user.id) }}" class="btn btn-default">Change eMail</a>
|
<a href="{{ url_for('change_mail', uid = user.id) }}" class="btn btn-default">Change eMail</a>
|
||||||
@ -54,11 +54,11 @@
|
|||||||
<label class="sr-only" for="lastfm">LastFM status</label>
|
<label class="sr-only" for="lastfm">LastFM status</label>
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<div class="input-group-addon">LastFM status</div>
|
<div class="input-group-addon">LastFM status</div>
|
||||||
{% if api_key %}
|
{% if has_lastfm %}
|
||||||
{% if user.lastfm_session %}
|
{% if user.lastfm_session %}
|
||||||
<input type="text" class="form-control" id="lastfm" placeholder="{% if user.lastfm_status %}Linked{% else %}Invalid session{% endif %}" readonly>
|
<input type="text" class="form-control" id="lastfm" placeholder="{% if user.lastfm_status %}Linked{% else %}Invalid session{% endif %}" readonly>
|
||||||
<div class="input-group-btn">
|
<div class="input-group-btn">
|
||||||
{% if session.username == user.name %}
|
{% if request.user.id == user.id %}
|
||||||
<a href="{{ url_for('lastfm_unreg', uid = 'me') }}" class="btn btn-default">Unlink</a>
|
<a href="{{ url_for('lastfm_unreg', uid = 'me') }}" class="btn btn-default">Unlink</a>
|
||||||
{% else %}
|
{% else %}
|
||||||
<a href="{{ url_for('lastfm_unreg', uid = user.id) }}" class="btn btn-default">Unlink</a>
|
<a href="{{ url_for('lastfm_unreg', uid = user.id) }}" class="btn btn-default">Unlink</a>
|
||||||
@ -67,7 +67,7 @@
|
|||||||
{% else %}
|
{% else %}
|
||||||
<input type="text" class="form-control" id="lastfm" placeholder="Unlinked" readonly>
|
<input type="text" class="form-control" id="lastfm" placeholder="Unlinked" readonly>
|
||||||
<div class="input-group-btn">
|
<div class="input-group-btn">
|
||||||
{% if session.username == user.name %}
|
{% if request.user.id == user.id %}
|
||||||
<a href="http://www.last.fm/api/auth/?api_key={{ api_key }}&cb={{ request.url_root[:-(request.script_root|length+1)] + url_for('lastfm_reg', uid = 'me') }}" class="btn btn-default">Link</a>
|
<a href="http://www.last.fm/api/auth/?api_key={{ api_key }}&cb={{ request.url_root[:-(request.script_root|length+1)] + url_for('lastfm_reg', uid = 'me') }}" class="btn btn-default">Link</a>
|
||||||
{% else %}
|
{% else %}
|
||||||
<a href="http://www.last.fm/api/auth/?api_key={{ api_key }}&cb={{ request.url_root[:-(request.script_root|length+1)] + url_for('lastfm_reg', uid = user.id) }}" class="btn btn-default">Link</a>
|
<a href="http://www.last.fm/api/auth/?api_key={{ api_key }}&cb={{ request.url_root[:-(request.script_root|length+1)] + url_for('lastfm_reg', uid = user.id) }}" class="btn btn-default">Link</a>
|
||||||
@ -82,7 +82,7 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% if session.username == user.name %}
|
{% if request.user.id == user.id %}
|
||||||
<a href="{{ url_for('change_password', uid = 'me') }}" class="btn btn-default">Change password</a></li>
|
<a href="{{ url_for('change_password', uid = 'me') }}" class="btn btn-default">Change password</a></li>
|
||||||
{% else %}
|
{% else %}
|
||||||
<a href="{{ url_for('change_username', uid = user.id) }}" class="btn btn-default">Change username or admin status</a></li>
|
<a href="{{ url_for('change_username', uid = user.id) }}" class="btn btn-default">Change username or admin status</a></li>
|
||||||
|
@ -23,10 +23,10 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
{% for user in users %}
|
{% for user in users %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{% if session.username == user.name %}{{ user.name }}{% else %}
|
<td>{% if request.user.id == user.id %}{{ user.name }}{% else %}
|
||||||
<a href="{{ url_for('user_profile', uid = user.id) }}">{{ user.name }}</a>{% endif %}</td>
|
<a href="{{ url_for('user_profile', uid = user.id) }}">{{ user.name }}</a>{% endif %}</td>
|
||||||
<td>{{ user.mail }}</td><td>{{ user.admin }}</td><td>{{ user.last_play_date }}</td><td>
|
<td>{{ user.mail }}</td><td>{{ user.admin }}</td><td>{{ user.last_play_date }}</td><td>
|
||||||
{% if session.username != user.name %}<button class="btn btn-danger btn-xs" data-href="{{ url_for('del_user', uid = user.id) }}" data-toggle="modal" data-target="#confirm-delete" aria-label="Delete user">
|
{% if request.user.id != user.id %}<button class="btn btn-danger btn-xs" data-href="{{ url_for('del_user', uid = user.id) }}" data-toggle="modal" data-target="#confirm-delete" aria-label="Delete user">
|
||||||
<span class="glyphicon glyphicon-remove-circle" aria-hidden="true" data-toggle="tooltip" data-placement="top" title="Delete user"></span></button>{% endif %}</td></tr>
|
<span class="glyphicon glyphicon-remove-circle" aria-hidden="true" data-toggle="tooltip" data-placement="top" title="Delete user"></span></button>{% endif %}</td></tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
@ -33,7 +33,7 @@ class FolderTestCase(FrontendTestBase):
|
|||||||
self._login('bob', 'B0b')
|
self._login('bob', 'B0b')
|
||||||
rv = self.client.get('/folder/add', follow_redirects = True)
|
rv = self.client.get('/folder/add', follow_redirects = True)
|
||||||
self.assertIn('There\'s nothing much to see', rv.data)
|
self.assertIn('There\'s nothing much to see', rv.data)
|
||||||
self.assertNotIn('Add folder', rv.data)
|
self.assertNotIn('Add Folder', rv.data)
|
||||||
self._logout()
|
self._logout()
|
||||||
|
|
||||||
self._login('alice', 'Alic3')
|
self._login('alice', 'Alic3')
|
||||||
|
@ -54,7 +54,6 @@ class LoginTestCase(FrontendTestBase):
|
|||||||
# Root with valid session
|
# Root with valid session
|
||||||
with self.client.session_transaction() as sess:
|
with self.client.session_transaction() as sess:
|
||||||
sess['userid'] = self.store.find(User, User.name == 'alice').one().id
|
sess['userid'] = self.store.find(User, User.name == 'alice').one().id
|
||||||
sess['username'] = 'alice'
|
|
||||||
rv = self.client.get('/', follow_redirects=True)
|
rv = self.client.get('/', follow_redirects=True)
|
||||||
self.assertIn('alice', rv.data)
|
self.assertIn('alice', rv.data)
|
||||||
self.assertIn('Log out', rv.data)
|
self.assertIn('Log out', rv.data)
|
||||||
@ -64,13 +63,6 @@ class LoginTestCase(FrontendTestBase):
|
|||||||
# Root with a no-valid session
|
# Root with a no-valid session
|
||||||
with self.client.session_transaction() as sess:
|
with self.client.session_transaction() as sess:
|
||||||
sess['userid'] = uuid.uuid4()
|
sess['userid'] = uuid.uuid4()
|
||||||
sess['username'] = 'alice'
|
|
||||||
rv = self.client.get('/', follow_redirects=True)
|
|
||||||
self.assertIn('Please login', rv.data)
|
|
||||||
# Root with a no-valid user
|
|
||||||
with self.client.session_transaction() as sess:
|
|
||||||
sess['userid'] = self.store.find(User, User.name == 'alice').one().id
|
|
||||||
sess['username'] = 'nonexistent'
|
|
||||||
rv = self.client.get('/', follow_redirects=True)
|
rv = self.client.get('/', follow_redirects=True)
|
||||||
self.assertIn('Please login', rv.data)
|
self.assertIn('Please login', rv.data)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user