calendar-social/calsocial/account.py

235 lines
6.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Calendar.social
# Copyright (C) 2018 Gergely Polonkai
#
# 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
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
"""Main module for the Calendar.social app
"""
from flask import Blueprint, abort, current_app, flash, redirect, render_template, session, url_for
from flask_security import current_user, login_required
from sqlalchemy.orm.exc import NoResultFound
from calsocial.utils import RoutedMixin
class AccountBlueprint(Blueprint, RoutedMixin):
"""Blueprint for account management
"""
def __init__(self):
Blueprint.__init__(self, 'account', __name__)
self.app = None
RoutedMixin.register_routes(self)
def init_app(self, app, url_prefix=None):
"""Initialise the blueprint, registering it with ``app``.
"""
self.app = app
app.register_blueprint(self, url_prefix=url_prefix)
@staticmethod
@RoutedMixin.route('/register')
def register_account():
"""View for user registration
If the ``REGISTRATION_FAILED`` configuration value is set to ``True`` it displays the
registration disabled template. Otherwise, it performs user registration.
"""
if not current_app.config['REGISTRATION_ENABLED']:
return render_template('registration-disabled.html')
from .forms import RegistrationForm
from .models import db, User
form = RegistrationForm()
if form.validate_on_submit():
# TODO: This might become False later, if we want registrations to be confirmed via
# e-mail
user = User(active=True)
form.populate_obj(user)
db.session.add(user)
db.session.commit()
return redirect(url_for('hello'))
return render_template('account/registration.html', form=form)
@staticmethod
@RoutedMixin.route('/settings', methods=['GET', 'POST'])
@login_required
def settings():
"""View for user settings
"""
from .forms import SettingsForm
from .models import db
form = SettingsForm(current_user)
if form.validate_on_submit():
form.populate_obj(current_user)
db.session.commit()
return redirect(url_for('hello'))
return render_template('account/user-settings.html', form=form)
@staticmethod
@RoutedMixin.route('/notifications')
def notifications():
"""View to list the notifications for the current user
"""
from .models import Notification
if current_user.is_authenticated:
notifs = Notification.query.filter(Notification.profile == current_user.profile)
else:
notifs = []
return render_template('account/notifications.html', notifs=notifs)
@staticmethod
@RoutedMixin.route('/first-steps', methods=['GET', 'POST'])
@login_required
def first_steps():
"""View to set up a new registrants profile
"""
from .forms import FirstStepsForm
from .models import db, Profile
if current_user.profile:
return redirect(url_for('hello'))
form = FirstStepsForm()
if form.validate_on_submit():
profile = Profile(user=current_user, display_name=form.display_name.data)
db.session.add(profile)
current_user.settings['timezone'] = str(form.time_zone.data)
db.session.commit()
return redirect(url_for('hello'))
return render_template('account/first-steps.html', form=form)
@staticmethod
@RoutedMixin.route('/edit-profile', methods=['GET', 'POST'])
@login_required
def edit_profile():
"""View for editing ones profile
"""
from .forms import ProfileForm
from .models import db
form = ProfileForm(current_user.profile)
if form.validate_on_submit():
form.populate_obj(current_user.profile)
db.session.add(current_user.profile)
db.session.commit()
return redirect(url_for('account.edit_profile'))
return render_template('account/profile-edit.html', form=form)
@staticmethod
@RoutedMixin.route('/follow-requests')
@login_required
def follow_requests():
"""View for listing follow requests
"""
from .models import UserFollow
requests = UserFollow.query \
.filter(UserFollow.followed == current_user.profile) \
.filter(UserFollow.accepted_at.is_(None))
return render_template('account/follow-requests.html', requests=requests)
@staticmethod
@RoutedMixin.route('/follow-request/<int:follower_id>/accept')
@login_required
def accept_follow(follower_id):
"""View for accepting a follow request
"""
from .models import db, UserFollow
try:
req = UserFollow.query \
.filter(UserFollow.followed == current_user.profile) \
.filter(UserFollow.follower_id == follower_id) \
.one()
except NoResultFound:
abort(404)
if req.accepted_at is None:
req.accept()
db.session.add(req)
db.session.commit()
return redirect(url_for('account.follow_requests'))
@staticmethod
@RoutedMixin.route('/sessions')
@login_required
def active_sessions():
"""View the list of active sessions
"""
sessions = []
for sid in current_user.active_sessions:
session = current_app.session_interface.load_session(sid)
sessions.append(session)
return render_template('account/active-sessions.html', sessions=sessions)
@staticmethod
@RoutedMixin.route('/sessions/invalidate/<string:sid>')
@login_required
def invalidate_session(sid):
"""View to invalidate a session
"""
sess = current_app.session_interface.load_session(sid)
if not sess or sess.user != current_user:
abort(404)
if sess.sid == session.sid:
flash(_('Cant invalidate your current session'))
else:
current_app.session_interface.delete_session(sid)
current_user.active_sessions = [sess_id for sess_id in current_user.active_sessions if sess_id != sid]
return redirect(url_for('account.active_sessions'))