# -*- coding: utf-8 -*- """ :author: Grey Li (李辉) :url: http://greyli.com :copyright: © 2018 Grey Li :license: MIT, see LICENSE for more details. """ from flask import render_template, flash, redirect, url_for, current_app, request, Blueprint from flask_login import login_required, current_user, fresh_login_required, logout_user from albumy.decorators import confirm_required, permission_required from albumy.emails import send_change_email_email from albumy.extensions import db, avatars from albumy.forms.user import EditProfileForm, UploadAvatarForm, CropAvatarForm, ChangeEmailForm, \ ChangePasswordForm, NotificationSettingForm, PrivacySettingForm, DeleteAccountForm from albumy.models import User, Photo, Collect from albumy.notifications import push_follow_notification from albumy.settings import Operations from albumy.utils import generate_token, validate_token, redirect_back, flash_errors user_bp = Blueprint('user', __name__) @user_bp.route('/') def index(username): user = User.query.filter_by(username=username).first_or_404() if user == current_user and user.locked: flash('Your account is locked.', 'danger') if user == current_user and not user.active: logout_user() page = request.args.get('page', 1, type=int) per_page = current_app.config['ALBUMY_PHOTO_PER_PAGE'] pagination = Photo.query.with_parent(user).order_by(Photo.timestamp.desc()).paginate(page, per_page) photos = pagination.items return render_template('user/index.html', user=user, pagination=pagination, photos=photos) @user_bp.route('//collections') def show_collections(username): user = User.query.filter_by(username=username).first_or_404() page = request.args.get('page', 1, type=int) per_page = current_app.config['ALBUMY_PHOTO_PER_PAGE'] pagination = Collect.query.with_parent(user).order_by(Collect.timestamp.desc()).paginate(page, per_page) collects = pagination.items return render_template('user/collections.html', user=user, pagination=pagination, collects=collects) @user_bp.route('/follow/', methods=['POST']) @login_required @confirm_required @permission_required('FOLLOW') def follow(username): user = User.query.filter_by(username=username).first_or_404() if current_user.is_following(user): flash('Already followed.', 'info') return redirect(url_for('.index', username=username)) current_user.follow(user) flash('User followed.', 'success') if user.receive_follow_notification: push_follow_notification(follower=current_user, receiver=user) return redirect_back() @user_bp.route('/unfollow/', methods=['POST']) @login_required def unfollow(username): user = User.query.filter_by(username=username).first_or_404() if not current_user.is_following(user): flash('Not follow yet.', 'info') return redirect(url_for('.index', username=username)) current_user.unfollow(user) flash('User unfollowed.', 'info') return redirect_back() @user_bp.route('//followers') def show_followers(username): user = User.query.filter_by(username=username).first_or_404() page = request.args.get('page', 1, type=int) per_page = current_app.config['ALBUMY_USER_PER_PAGE'] pagination = user.followers.paginate(page, per_page) follows = pagination.items return render_template('user/followers.html', user=user, pagination=pagination, follows=follows) @user_bp.route('//following') def show_following(username): user = User.query.filter_by(username=username).first_or_404() page = request.args.get('page', 1, type=int) per_page = current_app.config['ALBUMY_USER_PER_PAGE'] pagination = user.following.paginate(page, per_page) follows = pagination.items return render_template('user/following.html', user=user, pagination=pagination, follows=follows) @user_bp.route('/settings/profile', methods=['GET', 'POST']) @login_required def edit_profile(): form = EditProfileForm() if form.validate_on_submit(): current_user.name = form.name.data current_user.username = form.username.data current_user.bio = form.bio.data current_user.website = form.website.data current_user.location = form.location.data db.session.commit() flash('Profile updated.', 'success') return redirect(url_for('.index', username=current_user.username)) form.name.data = current_user.name form.username.data = current_user.username form.bio.data = current_user.bio form.website.data = current_user.website form.location.data = current_user.location return render_template('user/settings/edit_profile.html', form=form) @user_bp.route('/settings/avatar') @login_required @confirm_required def change_avatar(): upload_form = UploadAvatarForm() crop_form = CropAvatarForm() return render_template('user/settings/change_avatar.html', upload_form=upload_form, crop_form=crop_form) @user_bp.route('/settings/avatar/upload', methods=['POST']) @login_required @confirm_required def upload_avatar(): form = UploadAvatarForm() if form.validate_on_submit(): image = form.image.data filename = avatars.save_avatar(image) current_user.avatar_raw = filename db.session.commit() flash('Image uploaded, please crop.', 'success') flash_errors(form) return redirect(url_for('.change_avatar')) @user_bp.route('/settings/avatar/crop', methods=['POST']) @login_required @confirm_required def crop_avatar(): form = CropAvatarForm() if form.validate_on_submit(): x = form.x.data y = form.y.data w = form.w.data h = form.h.data filenames = avatars.crop_avatar(current_user.avatar_raw, x, y, w, h) current_user.avatar_s = filenames[0] current_user.avatar_m = filenames[1] current_user.avatar_l = filenames[2] db.session.commit() flash('Avatar updated.', 'success') flash_errors(form) return redirect(url_for('.change_avatar')) @user_bp.route('/settings/change-password', methods=['GET', 'POST']) @fresh_login_required def change_password(): form = ChangePasswordForm() if form.validate_on_submit(): if current_user.validate_password(form.old_password.data): current_user.set_password(form.password.data) db.session.commit() flash('Password updated.', 'success') return redirect(url_for('.index', username=current_user.username)) else: flash('Old password is incorrect.', 'warning') return render_template('user/settings/change_password.html', form=form) @user_bp.route('/settings/change-email', methods=['GET', 'POST']) @fresh_login_required def change_email_request(): form = ChangeEmailForm() if form.validate_on_submit(): token = generate_token(user=current_user, operation=Operations.CHANGE_EMAIL, new_email=form.email.data.lower()) send_change_email_email(to=form.email.data, user=current_user, token=token) flash('Confirm email sent, check your inbox.', 'info') return redirect(url_for('.index', username=current_user.username)) return render_template('user/settings/change_email.html', form=form) @user_bp.route('/change-email/') @login_required def change_email(token): if validate_token(user=current_user, token=token, operation=Operations.CHANGE_EMAIL): flash('Email updated.', 'success') return redirect(url_for('.index', username=current_user.username)) else: flash('Invalid or expired token.', 'warning') return redirect(url_for('.change_email_request')) @user_bp.route('/settings/notification', methods=['GET', 'POST']) @login_required def notification_setting(): form = NotificationSettingForm() if form.validate_on_submit(): current_user.receive_collect_notification = form.receive_collect_notification.data current_user.receive_comment_notification = form.receive_comment_notification.data current_user.receive_follow_notification = form.receive_follow_notification.data db.session.commit() flash('Notification settings updated.', 'success') return redirect(url_for('.index', username=current_user.username)) form.receive_collect_notification.data = current_user.receive_collect_notification form.receive_comment_notification.data = current_user.receive_comment_notification form.receive_follow_notification.data = current_user.receive_follow_notification return render_template('user/settings/edit_notification.html', form=form) @user_bp.route('/settings/privacy', methods=['GET', 'POST']) @login_required def privacy_setting(): form = PrivacySettingForm() if form.validate_on_submit(): current_user.public_collections = form.public_collections.data db.session.commit() flash('Privacy settings updated.', 'success') return redirect(url_for('.index', username=current_user.username)) form.public_collections.data = current_user.public_collections return render_template('user/settings/edit_privacy.html', form=form) @user_bp.route('/settings/account/delete', methods=['GET', 'POST']) @fresh_login_required def delete_account(): form = DeleteAccountForm() if form.validate_on_submit(): db.session.delete(current_user._get_current_object()) db.session.commit() flash('Your are free, goodbye!', 'success') return redirect(url_for('main.index')) return render_template('user/settings/delete_account.html', form=form)