選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

242 行
9.5 KiB

  1. # -*- coding: utf-8 -*-
  2. """
  3. :author: Grey Li ()
  4. :url: http://greyli.com
  5. :copyright: © 2018 Grey Li <withlihui@gmail.com>
  6. :license: MIT, see LICENSE for more details.
  7. """
  8. from flask import render_template, flash, redirect, url_for, current_app, request, Blueprint
  9. from flask_login import login_required, current_user, fresh_login_required, logout_user
  10. from albumy.decorators import confirm_required, permission_required
  11. from albumy.emails import send_change_email_email
  12. from albumy.extensions import db, avatars
  13. from albumy.forms.user import EditProfileForm, UploadAvatarForm, CropAvatarForm, ChangeEmailForm, \
  14. ChangePasswordForm, NotificationSettingForm, PrivacySettingForm, DeleteAccountForm
  15. from albumy.models import User, Photo, Collect
  16. from albumy.notifications import push_follow_notification
  17. from albumy.settings import Operations
  18. from albumy.utils import generate_token, validate_token, redirect_back, flash_errors
  19. user_bp = Blueprint('user', __name__)
  20. @user_bp.route('/<username>')
  21. def index(username):
  22. user = User.query.filter_by(username=username).first_or_404()
  23. if user == current_user and user.locked:
  24. flash('Your account is locked.', 'danger')
  25. if user == current_user and not user.active:
  26. logout_user()
  27. page = request.args.get('page', 1, type=int)
  28. per_page = current_app.config['ALBUMY_PHOTO_PER_PAGE']
  29. pagination = Photo.query.with_parent(user).order_by(Photo.timestamp.desc()).paginate(page, per_page)
  30. photos = pagination.items
  31. return render_template('user/index.html', user=user, pagination=pagination, photos=photos)
  32. @user_bp.route('/<username>/collections')
  33. def show_collections(username):
  34. user = User.query.filter_by(username=username).first_or_404()
  35. page = request.args.get('page', 1, type=int)
  36. per_page = current_app.config['ALBUMY_PHOTO_PER_PAGE']
  37. pagination = Collect.query.with_parent(user).order_by(Collect.timestamp.desc()).paginate(page, per_page)
  38. collects = pagination.items
  39. return render_template('user/collections.html', user=user, pagination=pagination, collects=collects)
  40. @user_bp.route('/follow/<username>', methods=['POST'])
  41. @login_required
  42. @confirm_required
  43. @permission_required('FOLLOW')
  44. def follow(username):
  45. user = User.query.filter_by(username=username).first_or_404()
  46. if current_user.is_following(user):
  47. flash('Already followed.', 'info')
  48. return redirect(url_for('.index', username=username))
  49. current_user.follow(user)
  50. flash('User followed.', 'success')
  51. if user.receive_follow_notification:
  52. push_follow_notification(follower=current_user, receiver=user)
  53. return redirect_back()
  54. @user_bp.route('/unfollow/<username>', methods=['POST'])
  55. @login_required
  56. def unfollow(username):
  57. user = User.query.filter_by(username=username).first_or_404()
  58. if not current_user.is_following(user):
  59. flash('Not follow yet.', 'info')
  60. return redirect(url_for('.index', username=username))
  61. current_user.unfollow(user)
  62. flash('User unfollowed.', 'info')
  63. return redirect_back()
  64. @user_bp.route('/<username>/followers')
  65. def show_followers(username):
  66. user = User.query.filter_by(username=username).first_or_404()
  67. page = request.args.get('page', 1, type=int)
  68. per_page = current_app.config['ALBUMY_USER_PER_PAGE']
  69. pagination = user.followers.paginate(page, per_page)
  70. follows = pagination.items
  71. return render_template('user/followers.html', user=user, pagination=pagination, follows=follows)
  72. @user_bp.route('/<username>/following')
  73. def show_following(username):
  74. user = User.query.filter_by(username=username).first_or_404()
  75. page = request.args.get('page', 1, type=int)
  76. per_page = current_app.config['ALBUMY_USER_PER_PAGE']
  77. pagination = user.following.paginate(page, per_page)
  78. follows = pagination.items
  79. return render_template('user/following.html', user=user, pagination=pagination, follows=follows)
  80. @user_bp.route('/settings/profile', methods=['GET', 'POST'])
  81. @login_required
  82. def edit_profile():
  83. form = EditProfileForm()
  84. if form.validate_on_submit():
  85. current_user.name = form.name.data
  86. current_user.username = form.username.data
  87. current_user.bio = form.bio.data
  88. current_user.website = form.website.data
  89. current_user.location = form.location.data
  90. db.session.commit()
  91. flash('Profile updated.', 'success')
  92. return redirect(url_for('.index', username=current_user.username))
  93. form.name.data = current_user.name
  94. form.username.data = current_user.username
  95. form.bio.data = current_user.bio
  96. form.website.data = current_user.website
  97. form.location.data = current_user.location
  98. return render_template('user/settings/edit_profile.html', form=form)
  99. @user_bp.route('/settings/avatar')
  100. @login_required
  101. @confirm_required
  102. def change_avatar():
  103. upload_form = UploadAvatarForm()
  104. crop_form = CropAvatarForm()
  105. return render_template('user/settings/change_avatar.html', upload_form=upload_form, crop_form=crop_form)
  106. @user_bp.route('/settings/avatar/upload', methods=['POST'])
  107. @login_required
  108. @confirm_required
  109. def upload_avatar():
  110. form = UploadAvatarForm()
  111. if form.validate_on_submit():
  112. image = form.image.data
  113. filename = avatars.save_avatar(image)
  114. current_user.avatar_raw = filename
  115. db.session.commit()
  116. flash('Image uploaded, please crop.', 'success')
  117. flash_errors(form)
  118. return redirect(url_for('.change_avatar'))
  119. @user_bp.route('/settings/avatar/crop', methods=['POST'])
  120. @login_required
  121. @confirm_required
  122. def crop_avatar():
  123. form = CropAvatarForm()
  124. if form.validate_on_submit():
  125. x = form.x.data
  126. y = form.y.data
  127. w = form.w.data
  128. h = form.h.data
  129. filenames = avatars.crop_avatar(current_user.avatar_raw, x, y, w, h)
  130. current_user.avatar_s = filenames[0]
  131. current_user.avatar_m = filenames[1]
  132. current_user.avatar_l = filenames[2]
  133. db.session.commit()
  134. flash('Avatar updated.', 'success')
  135. flash_errors(form)
  136. return redirect(url_for('.change_avatar'))
  137. @user_bp.route('/settings/change-password', methods=['GET', 'POST'])
  138. @fresh_login_required
  139. def change_password():
  140. form = ChangePasswordForm()
  141. if form.validate_on_submit():
  142. if current_user.validate_password(form.old_password.data):
  143. current_user.set_password(form.password.data)
  144. db.session.commit()
  145. flash('Password updated.', 'success')
  146. return redirect(url_for('.index', username=current_user.username))
  147. else:
  148. flash('Old password is incorrect.', 'warning')
  149. return render_template('user/settings/change_password.html', form=form)
  150. @user_bp.route('/settings/change-email', methods=['GET', 'POST'])
  151. @fresh_login_required
  152. def change_email_request():
  153. form = ChangeEmailForm()
  154. if form.validate_on_submit():
  155. token = generate_token(user=current_user, operation=Operations.CHANGE_EMAIL, new_email=form.email.data.lower())
  156. send_change_email_email(to=form.email.data, user=current_user, token=token)
  157. flash('Confirm email sent, check your inbox.', 'info')
  158. return redirect(url_for('.index', username=current_user.username))
  159. return render_template('user/settings/change_email.html', form=form)
  160. @user_bp.route('/change-email/<token>')
  161. @login_required
  162. def change_email(token):
  163. if validate_token(user=current_user, token=token, operation=Operations.CHANGE_EMAIL):
  164. flash('Email updated.', 'success')
  165. return redirect(url_for('.index', username=current_user.username))
  166. else:
  167. flash('Invalid or expired token.', 'warning')
  168. return redirect(url_for('.change_email_request'))
  169. @user_bp.route('/settings/notification', methods=['GET', 'POST'])
  170. @login_required
  171. def notification_setting():
  172. form = NotificationSettingForm()
  173. if form.validate_on_submit():
  174. current_user.receive_collect_notification = form.receive_collect_notification.data
  175. current_user.receive_comment_notification = form.receive_comment_notification.data
  176. current_user.receive_follow_notification = form.receive_follow_notification.data
  177. db.session.commit()
  178. flash('Notification settings updated.', 'success')
  179. return redirect(url_for('.index', username=current_user.username))
  180. form.receive_collect_notification.data = current_user.receive_collect_notification
  181. form.receive_comment_notification.data = current_user.receive_comment_notification
  182. form.receive_follow_notification.data = current_user.receive_follow_notification
  183. return render_template('user/settings/edit_notification.html', form=form)
  184. @user_bp.route('/settings/privacy', methods=['GET', 'POST'])
  185. @login_required
  186. def privacy_setting():
  187. form = PrivacySettingForm()
  188. if form.validate_on_submit():
  189. current_user.public_collections = form.public_collections.data
  190. db.session.commit()
  191. flash('Privacy settings updated.', 'success')
  192. return redirect(url_for('.index', username=current_user.username))
  193. form.public_collections.data = current_user.public_collections
  194. return render_template('user/settings/edit_privacy.html', form=form)
  195. @user_bp.route('/settings/account/delete', methods=['GET', 'POST'])
  196. @fresh_login_required
  197. def delete_account():
  198. form = DeleteAccountForm()
  199. if form.validate_on_submit():
  200. db.session.delete(current_user._get_current_object())
  201. db.session.commit()
  202. flash('Your are free, goodbye!', 'success')
  203. return redirect(url_for('main.index'))
  204. return render_template('user/settings/delete_account.html', form=form)