From 7455711b60e32c8934ae6fe8ad5720478b4c9740 Mon Sep 17 00:00:00 2001 From: spl0k Date: Mon, 29 Jan 2018 21:37:19 +0100 Subject: [PATCH] Frontend as blueprint Ref #76 --- supysonic/frontend/__init__.py | 24 ++++---- supysonic/frontend/folder.py | 30 +++++----- supysonic/frontend/playlist.py | 28 ++++----- supysonic/frontend/user.py | 73 ++++++++++++------------ supysonic/templates/addfolder.html | 4 +- supysonic/templates/adduser.html | 4 +- supysonic/templates/change_mail.html | 6 +- supysonic/templates/change_pass.html | 6 +- supysonic/templates/change_username.html | 4 +- supysonic/templates/folders.html | 12 ++-- supysonic/templates/home.html | 4 +- supysonic/templates/importusers.html | 30 ---------- supysonic/templates/layout.html | 14 ++--- supysonic/templates/playlist.html | 4 +- supysonic/templates/playlists.html | 10 ++-- supysonic/templates/profile.html | 24 ++++---- supysonic/templates/users.html | 10 ++-- supysonic/web.py | 3 +- 18 files changed, 131 insertions(+), 159 deletions(-) delete mode 100644 supysonic/templates/importusers.html diff --git a/supysonic/frontend/__init__.py b/supysonic/frontend/__init__.py index f4845eb..d9b060d 100644 --- a/supysonic/frontend/__init__.py +++ b/supysonic/frontend/__init__.py @@ -4,26 +4,23 @@ # This file is part of Supysonic. # Supysonic is a Python implementation of the Subsonic server API. # -# Copyright (C) 2013-2017 Alban 'spl0k' Féron +# Copyright (C) 2013-2018 Alban 'spl0k' Féron # 2017 Óscar García Amor # # Distributed under terms of the GNU AGPLv3 license. -from flask import session, request, redirect, url_for, current_app as app +from flask import redirect, request, session, url_for +from flask import Blueprint from functools import wraps from pony.orm import db_session from ..db import Artist, Album, Track from ..managers.user import UserManager -@app.before_request +frontend = Blueprint('frontend', __name__) + +@frontend.before_request def login_check(): - if request.path.startswith('/rest/'): - return - - if request.path.startswith('/static/'): - return - request.user = None should_login = True if session.get('userid'): @@ -34,11 +31,11 @@ def login_check(): request.user = user should_login = False - if should_login and request.endpoint != 'login': + if should_login and request.endpoint != 'frontend.login': flash('Please login') - return redirect(url_for('login', returnUrl = request.script_root + request.url[len(request.url_root)-1:])) + return redirect(url_for('frontend.login', returnUrl = request.script_root + request.url[len(request.url_root)-1:])) -@app.route('/') +@frontend.route('/') @db_session def index(): stats = { @@ -52,10 +49,11 @@ 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 redirect(url_for('frontend.index')) return f(*args, **kwargs) return decorated_func from .user import * from .folder import * from .playlist import * + diff --git a/supysonic/frontend/folder.py b/supysonic/frontend/folder.py index 51d4cd1..cbc2423 100644 --- a/supysonic/frontend/folder.py +++ b/supysonic/frontend/folder.py @@ -3,7 +3,7 @@ # This file is part of Supysonic. # # Supysonic is a Python implementation of the Subsonic server API. -# Copyright (C) 2013-2017 Alban 'spl0k' Féron +# Copyright (C) 2013-2018 Alban 'spl0k' Féron # # 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 @@ -21,7 +21,7 @@ import os.path import uuid -from flask import request, flash, render_template, redirect, url_for, current_app as app +from flask import current_app, flash, redirect, render_template, request, url_for from pony.orm import db_session from ..db import Folder @@ -29,20 +29,20 @@ from ..managers.user import UserManager from ..managers.folder import FolderManager from ..scanner import Scanner -from . import admin_only +from . import admin_only, frontend -@app.route('/folder') +@frontend.route('/folder') @admin_only @db_session def folder_index(): return render_template('folders.html', folders = Folder.select(lambda f: f.root)) -@app.route('/folder/add') +@frontend.route('/folder/add') @admin_only def add_folder_form(): return render_template('addfolder.html') -@app.route('/folder/add', methods = [ 'POST' ]) +@frontend.route('/folder/add', methods = [ 'POST' ]) @admin_only def add_folder_post(): error = False @@ -63,16 +63,16 @@ def add_folder_post(): flash("Folder '%s' created. You should now run a scan" % name) - return redirect(url_for('folder_index')) + return redirect(url_for('frontend.folder_index')) -@app.route('/folder/del/') +@frontend.route('/folder/del/') @admin_only def del_folder(id): try: idid = uuid.UUID(id) except ValueError: flash('Invalid folder id') - return redirect(url_for('folder_index')) + return redirect(url_for('frontend.folder_index')) ret = FolderManager.delete(idid) if ret != FolderManager.SUCCESS: @@ -80,14 +80,14 @@ def del_folder(id): else: flash('Deleted folder') - return redirect(url_for('folder_index')) + return redirect(url_for('frontend.folder_index')) -@app.route('/folder/scan') -@app.route('/folder/scan/') +@frontend.route('/folder/scan') +@frontend.route('/folder/scan/') @admin_only @db_session def scan_folder(id = None): - extensions = app.config['BASE']['scanner_extensions'] + extensions = current_app.config['BASE']['scanner_extensions'] if extensions: extensions = extensions.split(' ') @@ -100,7 +100,7 @@ def scan_folder(id = None): status, folder = FolderManager.get(id) if status != FolderManager.SUCCESS: flash(FolderManager.error_str(status)) - return redirect(url_for('folder_index')) + return redirect(url_for('frontend.folder_index')) scanner.scan(folder) scanner.finish() @@ -112,5 +112,5 @@ def scan_folder(id = None): flash('Errors in:') for err in stats.errors: flash('- ' + err) - return redirect(url_for('folder_index')) + return redirect(url_for('frontend.folder_index')) diff --git a/supysonic/frontend/playlist.py b/supysonic/frontend/playlist.py index 1e7d8ad..98366c2 100644 --- a/supysonic/frontend/playlist.py +++ b/supysonic/frontend/playlist.py @@ -3,7 +3,7 @@ # This file is part of Supysonic. # # Supysonic is a Python implementation of the Subsonic server API. -# Copyright (C) 2013-2017 Alban 'spl0k' Féron +# Copyright (C) 2013-2018 Alban 'spl0k' Féron # # 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 @@ -20,51 +20,53 @@ import uuid -from flask import request, flash, render_template, redirect, url_for, current_app as app +from flask import flash, redirect, render_template, request, url_for from pony.orm import db_session from pony.orm import ObjectNotFound from ..db import Playlist from ..managers.user import UserManager -@app.route('/playlist') +from . import frontend + +@frontend.route('/playlist') @db_session def playlist_index(): return render_template('playlists.html', mine = Playlist.select(lambda p: p.user == request.user), others = Playlist.select(lambda p: p.user != request.user and p.public)) -@app.route('/playlist/') +@frontend.route('/playlist/') @db_session def playlist_details(uid): try: uid = uuid.UUID(uid) except ValueError: flash('Invalid playlist id') - return redirect(url_for('playlist_index')) + return redirect(url_for('frontend.playlist_index')) try: playlist = Playlist[uid] except ObjectNotFound: flash('Unknown playlist') - return redirect(url_for('playlist_index')) + return redirect(url_for('frontend.playlist_index')) return render_template('playlist.html', playlist = playlist) -@app.route('/playlist/', methods = [ 'POST' ]) +@frontend.route('/playlist/', methods = [ 'POST' ]) @db_session def playlist_update(uid): try: uid = uuid.UUID(uid) except ValueError: flash('Invalid playlist id') - return redirect(url_for('playlist_index')) + return redirect(url_for('frontend.playlist_index')) try: playlist = Playlist[uid] except ObjectNotFound: flash('Unknown playlist') - return redirect(url_for('playlist_index')) + return redirect(url_for('frontend.playlist_index')) if playlist.user.id != request.user.id: flash("You're not allowed to edit this playlist") @@ -77,20 +79,20 @@ def playlist_update(uid): return playlist_details(str(uid)) -@app.route('/playlist/del/') +@frontend.route('/playlist/del/') @db_session def playlist_delete(uid): try: uid = uuid.UUID(uid) except ValueError: flash('Invalid playlist id') - return redirect(url_for('playlist_index')) + return redirect(url_for('frontend.playlist_index')) try: playlist = Playlist[uid] except ObjectNotFound: flash('Unknown playlist') - return redirect(url_for('playlist_index')) + return redirect(url_for('frontend.playlist_index')) if playlist.user.id != request.user.id: flash("You're not allowed to delete this playlist") @@ -98,5 +100,5 @@ def playlist_delete(uid): playlist.delete() flash('Playlist deleted') - return redirect(url_for('playlist_index')) + return redirect(url_for('frontend.playlist_index')) diff --git a/supysonic/frontend/user.py b/supysonic/frontend/user.py index e9ba9ae..3a8cd2b 100644 --- a/supysonic/frontend/user.py +++ b/supysonic/frontend/user.py @@ -18,7 +18,8 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -from flask import request, session, flash, render_template, redirect, url_for, current_app as app +from flask import flash, redirect, render_template, request, session, url_for +from flask import current_app from functools import wraps from pony.orm import db_session @@ -27,7 +28,7 @@ from ..lastfm import LastFm from ..managers.user import UserManager from ..py23 import dict -from . import admin_only +from . import admin_only, frontend def me_or_uuid(f, arg = 'uid'): @db_session @@ -41,12 +42,12 @@ def me_or_uuid(f, arg = 'uid'): if uid == 'me': user = User[request.user.id] # Refetch user from previous transaction elif not request.user.admin: - return redirect(url_for('index')) + return redirect(url_for('frontend.index')) else: code, user = UserManager.get(uid) if code != UserManager.SUCCESS: flash(UserManager.error_str(code)) - return redirect(url_for('index')) + return redirect(url_for('frontend.index')) if kwargs: kwargs['user'] = user @@ -57,18 +58,18 @@ def me_or_uuid(f, arg = 'uid'): return decorated_func -@app.route('/user') +@frontend.route('/user') @admin_only @db_session def user_index(): return render_template('users.html', users = User.select()) -@app.route('/user/') +@frontend.route('/user/') @me_or_uuid def user_profile(uid, user): - return render_template('profile.html', user = user, has_lastfm = app.config['LASTFM']['api_key'] != None, clients = user.clients) + return render_template('profile.html', user = user, has_lastfm = current_app.config['LASTFM']['api_key'] != None, clients = user.clients) -@app.route('/user/', methods = [ 'POST' ]) +@frontend.route('/user/', methods = [ 'POST' ]) @me_or_uuid def update_clients(uid, user): clients_opts = dict() @@ -86,7 +87,7 @@ def update_clients(uid, user): clients_opts[client] = dict([ (opt, value) ]) else: clients_opts[client][opt] = value - app.logger.debug(clients_opts) + current_app.logger.debug(clients_opts) for client, opts in clients_opts.items(): prefs = user.clients.select(lambda c: c.client_name == client).first() @@ -103,23 +104,23 @@ def update_clients(uid, user): flash('Clients preferences updated.') return user_profile(uid, user) -@app.route('/user//changeusername') +@frontend.route('/user//changeusername') @admin_only def change_username_form(uid): code, user = UserManager.get(uid) if code != UserManager.SUCCESS: flash(UserManager.error_str(code)) - return redirect(url_for('index')) + return redirect(url_for('frontend.index')) return render_template('change_username.html', user = user) -@app.route('/user//changeusername', methods = [ 'POST' ]) +@frontend.route('/user//changeusername', methods = [ 'POST' ]) @admin_only @db_session def change_username_post(uid): code, user = UserManager.get(uid) if code != UserManager.SUCCESS: - return redirect(url_for('index')) + return redirect(url_for('frontend.index')) username = request.form.get('user') if username in ('', None): @@ -141,27 +142,27 @@ def change_username_post(uid): else: flash("No changes for '%s'." % username) - return redirect(url_for('user_profile', uid = uid)) + return redirect(url_for('frontend.user_profile', uid = uid)) -@app.route('/user//changemail') +@frontend.route('/user//changemail') @me_or_uuid def change_mail_form(uid, user): return render_template('change_mail.html', user = user) -@app.route('/user//changemail', methods = [ 'POST' ]) +@frontend.route('/user//changemail', methods = [ 'POST' ]) @me_or_uuid def change_mail_post(uid, user): mail = request.form.get('mail', '') # No validation, lol. user.mail = mail - return redirect(url_for('user_profile', uid = uid)) + return redirect(url_for('frontend.user_profile', uid = uid)) -@app.route('/user//changepass') +@frontend.route('/user//changepass') @me_or_uuid def change_password_form(uid, user): return render_template('change_pass.html', user = user) -@app.route('/user//changepass', methods = [ 'POST' ]) +@frontend.route('/user//changepass', methods = [ 'POST' ]) @me_or_uuid def change_password_post(uid, user): error = False @@ -190,16 +191,16 @@ def change_password_post(uid, user): flash(UserManager.error_str(status)) else: flash('Password changed') - return redirect(url_for('user_profile', uid = uid)) + return redirect(url_for('frontend.user_profile', uid = uid)) return change_password_form(uid, user) -@app.route('/user/add') +@frontend.route('/user/add') @admin_only def add_user_form(): return render_template('adduser.html') -@app.route('/user/add', methods = [ 'POST' ]) +@frontend.route('/user/add', methods = [ 'POST' ]) @admin_only def add_user_post(): error = False @@ -222,13 +223,13 @@ def add_user_post(): status = UserManager.add(name, passwd, mail, admin) if status == UserManager.SUCCESS: flash("User '%s' successfully added" % name) - return redirect(url_for('user_index')) + return redirect(url_for('frontend.user_index')) else: flash(UserManager.error_str(status)) return add_user_form() -@app.route('/user/del/') +@frontend.route('/user/del/') @admin_only def del_user(uid): status = UserManager.delete(uid) @@ -237,33 +238,33 @@ def del_user(uid): else: flash(UserManager.error_str(status)) - return redirect(url_for('user_index')) + return redirect(url_for('frontend.user_index')) -@app.route('/user//lastfm/link') +@frontend.route('/user//lastfm/link') @me_or_uuid def lastfm_reg(uid, user): token = request.args.get('token') if token in ('', None): flash('Missing LastFM auth token') - return redirect(url_for('user_profile', uid = uid)) + return redirect(url_for('frontend.user_profile', uid = uid)) - lfm = LastFm(app.config['LASTFM'], user, app.logger) + lfm = LastFm(current_app.config['LASTFM'], user, current_app.logger) status, error = lfm.link_account(token) flash(error if not status else 'Successfully linked LastFM account') - return redirect(url_for('user_profile', uid = uid)) + return redirect(url_for('frontend.user_profile', uid = uid)) -@app.route('/user//lastfm/unlink') +@frontend.route('/user//lastfm/unlink') @me_or_uuid def lastfm_unreg(uid, user): - lfm = LastFm(app.config['LASTFM'], user, app.logger) + lfm = LastFm(current_app.config['LASTFM'], user, current_app.logger) lfm.unlink_account() flash('Unlinked LastFM account') - return redirect(url_for('user_profile', uid = uid)) + return redirect(url_for('frontend.user_profile', uid = uid)) -@app.route('/user/login', methods = [ 'GET', 'POST']) +@frontend.route('/user/login', methods = [ 'GET', 'POST']) def login(): - return_url = request.args.get('returnUrl') or url_for('index') + return_url = request.args.get('returnUrl') or url_for('frontend.index') if request.user: flash('Already logged in') return redirect(return_url) @@ -291,9 +292,9 @@ def login(): return render_template('login.html') -@app.route('/user/logout') +@frontend.route('/user/logout') def logout(): session.clear() flash('Logged out!') - return redirect(url_for('login')) + return redirect(url_for('frontend.login')) diff --git a/supysonic/templates/addfolder.html b/supysonic/templates/addfolder.html index 094a869..3829080 100644 --- a/supysonic/templates/addfolder.html +++ b/supysonic/templates/addfolder.html @@ -2,14 +2,14 @@ This file is part of Supysonic. Supysonic is a Python implementation of the Subsonic server API. - Copyright (C) 2013-2017 Alban 'spl0k' Féron + Copyright (C) 2013-2018 Alban 'spl0k' Féron 2017 Óscar García Amor Distributed under terms of the GNU AGPLv3 license. -#} {% extends "layout.html" %} {% block navbar_folders %} -
  • Folders Folders (current)
  • {% endblock %} {% block body %} diff --git a/supysonic/templates/adduser.html b/supysonic/templates/adduser.html index a7d6170..07d4448 100644 --- a/supysonic/templates/adduser.html +++ b/supysonic/templates/adduser.html @@ -2,14 +2,14 @@ This file is part of Supysonic. Supysonic is a Python implementation of the Subsonic server API. - Copyright (C) 2013-2017 Alban 'spl0k' Féron + Copyright (C) 2013-2018 Alban 'spl0k' Féron 2017 Óscar García Amor Distributed under terms of the GNU AGPLv3 license. -#} {% extends "layout.html" %} {% block navbar_users %} -
  • Users Users (current)
  • {% endblock %} {% block body %} diff --git a/supysonic/templates/change_mail.html b/supysonic/templates/change_mail.html index ec7eb51..4f6b017 100644 --- a/supysonic/templates/change_mail.html +++ b/supysonic/templates/change_mail.html @@ -2,7 +2,7 @@ This file is part of Supysonic. Supysonic is a Python implementation of the Subsonic server API. - Copyright (C) 2013-2017 Alban 'spl0k' Féron + Copyright (C) 2013-2018 Alban 'spl0k' Féron 2017 Óscar García Amor Distributed under terms of the GNU AGPLv3 license. @@ -10,7 +10,7 @@ {% extends "layout.html" %} {% block navbar_users %} {% if request.user.id != user.id %} -
  • Users Users (current)
  • {% else %} {{ super() }} @@ -18,7 +18,7 @@ {% endblock %} {% block navbar_profile %} {% if request.user.id == user.id %} -
  • {{ request.user.name }} (current)
  • +
  • {{ request.user.name }} (current)
  • {% else %} {{ super() }} {% endif %} diff --git a/supysonic/templates/change_pass.html b/supysonic/templates/change_pass.html index 2633cf2..1ce3e93 100644 --- a/supysonic/templates/change_pass.html +++ b/supysonic/templates/change_pass.html @@ -2,7 +2,7 @@ This file is part of Supysonic. Supysonic is a Python implementation of the Subsonic server API. - Copyright (C) 2013-2017 Alban 'spl0k' Féron + Copyright (C) 2013-2018 Alban 'spl0k' Féron 2017 Óscar García Amor Distributed under terms of the GNU AGPLv3 license. @@ -10,7 +10,7 @@ {% extends "layout.html" %} {% block navbar_users %} {% if request.user.id != user.id %} -
  • Users Users (current)
  • {% else %} {{ super() }} @@ -18,7 +18,7 @@ {% endblock %} {% block navbar_profile %} {% if request.user.id == user.id %} -
  • {{ request.user.name }} (current)
  • +
  • {{ request.user.name }} (current)
  • {% else %} {{ super() }} {% endif %} diff --git a/supysonic/templates/change_username.html b/supysonic/templates/change_username.html index 16d2fe2..3bc664d 100644 --- a/supysonic/templates/change_username.html +++ b/supysonic/templates/change_username.html @@ -2,14 +2,14 @@ This file is part of Supysonic. Supysonic is a Python implementation of the Subsonic server API. - Copyright (C) 2013-2017 Alban 'spl0k' Féron + Copyright (C) 2013-2018 Alban 'spl0k' Féron 2017 Óscar García Amor Distributed under terms of the GNU AGPLv3 license. -#} {% extends "layout.html" %} {% block navbar_users %} -
  • Users Users (current)
  • {% endblock %} {% block body %} diff --git a/supysonic/templates/folders.html b/supysonic/templates/folders.html index 386e1e8..799f4f9 100644 --- a/supysonic/templates/folders.html +++ b/supysonic/templates/folders.html @@ -2,14 +2,14 @@ This file is part of Supysonic. Supysonic is a Python implementation of the Subsonic server API. - Copyright (C) 2013-2017 Alban 'spl0k' Féron + Copyright (C) 2013-2018 Alban 'spl0k' Féron 2017 Óscar García Amor Distributed under terms of the GNU AGPLv3 license. -#} {% extends "layout.html" %} {% block navbar_folders %} -
  • Folders Folders (current)
  • {% endblock %} {% block body %} @@ -24,17 +24,17 @@ {% for folder in folders %} {{ folder.name }}{{ folder.path }} - - + {% endfor %} @@ -59,18 +59,18 @@
    {% if request.user.id == user.id %} - Unlink + Unlink {% else %} - Unlink + Unlink {% endif %}
    {% else %}
    {% if request.user.id == user.id %} - Link + Link {% else %} - Link + Link {% endif %}
    {% endif %} @@ -83,10 +83,10 @@ {% if request.user.id == user.id %} -Change password +Change password {% else %} -Change username or admin status -Change password +Change username or admin status +Change password {% endif %} {% if clients.count() %}