# 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 . """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 registrant’s 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 one’s 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//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/') @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(_('Can’t 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'))