diff --git a/app/__init__.py b/app/__init__.py index 3e27823..a94f301 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -1,7 +1,7 @@ import logging from logging.handlers import SMTPHandler, RotatingFileHandler import os -from flask import Flask, request +from flask import Flask, request, current_app from flask_sqlalchemy import SQLAlchemy from flask_migrate import Migrate from flask_login import LoginManager @@ -12,47 +12,73 @@ from config import Config def get_locale(): - return request.accept_languages.best_match(app.config['LANGUAGES']) + return request.accept_languages.best_match(current_app.config['LANGUAGES']) -app = Flask(__name__) -app.config.from_object(Config) -db = SQLAlchemy(app) -migrate = Migrate(app, db) -login = LoginManager(app) -login.login_view = 'login' +db = SQLAlchemy() +migrate = Migrate() +login = LoginManager() +login.login_view = 'auth.login' login.login_message = _l('Please log in to access this page.') -mail = Mail(app) -moment = Moment(app) -babel = Babel(app, locale_selector=get_locale) - -if not app.debug: - if app.config['MAIL_SERVER']: - auth = None - if app.config['MAIL_USERNAME'] or app.config['MAIL_PASSWORD']: - auth = (app.config['MAIL_USERNAME'], app.config['MAIL_PASSWORD']) - secure = None - if app.config['MAIL_USE_TLS']: - secure = () - mail_handler = SMTPHandler( - mailhost=(app.config['MAIL_SERVER'], app.config['MAIL_PORT']), - fromaddr='no-reply@' + app.config['MAIL_SERVER'], - toaddrs=app.config['ADMINS'], subject='Microblog Failure', - credentials=auth, secure=secure) - mail_handler.setLevel(logging.ERROR) - app.logger.addHandler(mail_handler) - - if not os.path.exists('logs'): - os.mkdir('logs') - file_handler = RotatingFileHandler('logs/microblog.log', maxBytes=10240, - backupCount=10) - file_handler.setFormatter(logging.Formatter( - '%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d]')) - file_handler.setLevel(logging.INFO) - app.logger.addHandler(file_handler) - - app.logger.setLevel(logging.INFO) - app.logger.info('Microblog startup') +mail = Mail() +moment = Moment() +babel = Babel() -from app import routes, models, errors +def create_app(config_class=Config): + app = Flask(__name__) + app.config.from_object(config_class) + + db.init_app(app) + migrate.init_app(app, db) + login.init_app(app) + mail.init_app(app) + moment.init_app(app) + babel.init_app(app, locale_selector=get_locale) + + from app.errors import bp as errors_bp + app.register_blueprint(errors_bp) + + from app.auth import bp as auth_bp + app.register_blueprint(auth_bp, url_prefix='/auth') + + from app.main import bp as main_bp + app.register_blueprint(main_bp) + + from app.cli import bp as cli_bp + app.register_blueprint(cli_bp) + + if not app.debug and not app.testing: + if app.config['MAIL_SERVER']: + auth = None + if app.config['MAIL_USERNAME'] or app.config['MAIL_PASSWORD']: + auth = (app.config['MAIL_USERNAME'], + app.config['MAIL_PASSWORD']) + secure = None + if app.config['MAIL_USE_TLS']: + secure = () + mail_handler = SMTPHandler( + mailhost=(app.config['MAIL_SERVER'], app.config['MAIL_PORT']), + fromaddr='no-reply@' + app.config['MAIL_SERVER'], + toaddrs=app.config['ADMINS'], subject='Microblog Failure', + credentials=auth, secure=secure) + mail_handler.setLevel(logging.ERROR) + app.logger.addHandler(mail_handler) + + if not os.path.exists('logs'): + os.mkdir('logs') + file_handler = RotatingFileHandler('logs/microblog.log', + maxBytes=10240, backupCount=10) + file_handler.setFormatter(logging.Formatter( + '%(asctime)s %(levelname)s: %(message)s ' + '[in %(pathname)s:%(lineno)d]')) + file_handler.setLevel(logging.INFO) + app.logger.addHandler(file_handler) + + app.logger.setLevel(logging.INFO) + app.logger.info('Microblog startup') + + return app + + +from app import models diff --git a/app/auth/__init__.py b/app/auth/__init__.py new file mode 100644 index 0000000..088b033 --- /dev/null +++ b/app/auth/__init__.py @@ -0,0 +1,5 @@ +from flask import Blueprint + +bp = Blueprint('auth', __name__) + +from app.auth import routes diff --git a/app/auth/email.py b/app/auth/email.py new file mode 100644 index 0000000..98755ac --- /dev/null +++ b/app/auth/email.py @@ -0,0 +1,14 @@ +from flask import render_template, current_app +from flask_babel import _ +from app.email import send_email + + +def send_password_reset_email(user): + token = user.get_reset_password_token() + send_email(_('[Microblog] Reset Your Password'), + sender=current_app.config['ADMINS'][0], + recipients=[user.email], + text_body=render_template('email/reset_password.txt', + user=user, token=token), + html_body=render_template('email/reset_password.html', + user=user, token=token)) diff --git a/app/forms.py b/app/auth/forms.py similarity index 66% rename from app/forms.py rename to app/auth/forms.py index 6cdd775..c1dd3eb 100644 --- a/app/forms.py +++ b/app/auth/forms.py @@ -1,8 +1,6 @@ from flask_wtf import FlaskForm -from wtforms import StringField, PasswordField, BooleanField, SubmitField, \ - TextAreaField -from wtforms.validators import ValidationError, DataRequired, Email, EqualTo, \ - Length +from wtforms import StringField, PasswordField, BooleanField, SubmitField +from wtforms.validators import ValidationError, DataRequired, Email, EqualTo from flask_babel import _, lazy_gettext as _l from app.models import User @@ -45,29 +43,3 @@ class ResetPasswordForm(FlaskForm): _l('Repeat Password'), validators=[DataRequired(), EqualTo('password')]) submit = SubmitField(_l('Request Password Reset')) - - -class EditProfileForm(FlaskForm): - username = StringField(_l('Username'), validators=[DataRequired()]) - about_me = TextAreaField(_l('About me'), - validators=[Length(min=0, max=140)]) - submit = SubmitField(_l('Submit')) - - def __init__(self, original_username, *args, **kwargs): - super().__init__(*args, **kwargs) - self.original_username = original_username - - def validate_username(self, username): - if username.data != self.original_username: - user = User.query.filter_by(username=self.username.data).first() - if user is not None: - raise ValidationError(_('Please use a different username.')) - - -class EmptyForm(FlaskForm): - submit = SubmitField('Submit') - - -class PostForm(FlaskForm): - post = TextAreaField(_l('Say something'), validators=[DataRequired()]) - submit = SubmitField(_l('Submit')) diff --git a/app/auth/routes.py b/app/auth/routes.py new file mode 100644 index 0000000..b3f1d72 --- /dev/null +++ b/app/auth/routes.py @@ -0,0 +1,82 @@ +from flask import render_template, redirect, url_for, flash, request +from werkzeug.urls import url_parse +from flask_login import login_user, logout_user, current_user +from flask_babel import _ +from app import db +from app.auth import bp +from app.auth.forms import LoginForm, RegistrationForm, \ + ResetPasswordRequestForm, ResetPasswordForm +from app.models import User +from app.auth.email import send_password_reset_email + + +@bp.route('/login', methods=['GET', 'POST']) +def login(): + if current_user.is_authenticated: + return redirect(url_for('main.index')) + form = LoginForm() + if form.validate_on_submit(): + user = User.query.filter_by(username=form.username.data).first() + if user is None or not user.check_password(form.password.data): + flash(_('Invalid username or password')) + return redirect(url_for('auth.login')) + login_user(user, remember=form.remember_me.data) + next_page = request.args.get('next') + if not next_page or url_parse(next_page).netloc != '': + next_page = url_for('main.index') + return redirect(next_page) + return render_template('auth/login.html', title=_('Sign In'), form=form) + + +@bp.route('/logout') +def logout(): + logout_user() + return redirect(url_for('main.index')) + + +@bp.route('/register', methods=['GET', 'POST']) +def register(): + if current_user.is_authenticated: + return redirect(url_for('main.index')) + form = RegistrationForm() + if form.validate_on_submit(): + user = User(username=form.username.data, email=form.email.data) + user.set_password(form.password.data) + db.session.add(user) + db.session.commit() + flash(_('Congratulations, you are now a registered user!')) + return redirect(url_for('auth.login')) + return render_template('auth/register.html', title=_('Register'), + form=form) + + +@bp.route('/reset_password_request', methods=['GET', 'POST']) +def reset_password_request(): + if current_user.is_authenticated: + return redirect(url_for('main.index')) + form = ResetPasswordRequestForm() + if form.validate_on_submit(): + user = User.query.filter_by(email=form.email.data).first() + if user: + send_password_reset_email(user) + flash( + _('Check your email for the instructions to reset your password')) + return redirect(url_for('auth.login')) + return render_template('auth/reset_password_request.html', + title=_('Reset Password'), form=form) + + +@bp.route('/reset_password/', methods=['GET', 'POST']) +def reset_password(token): + if current_user.is_authenticated: + return redirect(url_for('main.index')) + user = User.verify_reset_password_token(token) + if not user: + return redirect(url_for('main.index')) + form = ResetPasswordForm() + if form.validate_on_submit(): + user.set_password(form.password.data) + db.session.commit() + flash(_('Your password has been reset.')) + return redirect(url_for('auth.login')) + return render_template('auth/reset_password.html', form=form) diff --git a/app/cli.py b/app/cli.py index 8c6697a..5414c5c 100644 --- a/app/cli.py +++ b/app/cli.py @@ -1,14 +1,15 @@ import os +from flask import Blueprint import click -from app import app + +bp = Blueprint('cli', __name__, cli_group=None) -@app.cli.group() +@bp.cli.group() def translate(): """Translation and localization commands.""" pass - @translate.command() @click.argument('lang') def init(lang): @@ -20,7 +21,6 @@ def init(lang): raise RuntimeError('init command failed') os.remove('messages.pot') - @translate.command() def update(): """Update all languages.""" @@ -30,7 +30,6 @@ def update(): raise RuntimeError('update command failed') os.remove('messages.pot') - @translate.command() def compile(): """Compile all languages.""" diff --git a/app/email.py b/app/email.py index 1f4d46f..ee23da8 100644 --- a/app/email.py +++ b/app/email.py @@ -1,8 +1,7 @@ from threading import Thread -from flask import render_template +from flask import current_app from flask_mail import Message -from flask_babel import _ -from app import app, mail +from app import mail def send_async_email(app, msg): @@ -14,15 +13,5 @@ def send_email(subject, sender, recipients, text_body, html_body): msg = Message(subject, sender=sender, recipients=recipients) msg.body = text_body msg.html = html_body - Thread(target=send_async_email, args=(app, msg)).start() - - -def send_password_reset_email(user): - token = user.get_reset_password_token() - send_email(_('[Microblog] Reset Your Password'), - sender=app.config['ADMINS'][0], - recipients=[user.email], - text_body=render_template('email/reset_password.txt', - user=user, token=token), - html_body=render_template('email/reset_password.html', - user=user, token=token)) + Thread(target=send_async_email, + args=(current_app._get_current_object(), msg)).start() diff --git a/app/errors.py b/app/errors.py deleted file mode 100644 index ed214c4..0000000 --- a/app/errors.py +++ /dev/null @@ -1,13 +0,0 @@ -from flask import render_template -from app import app, db - - -@app.errorhandler(404) -def not_found_error(error): - return render_template('404.html'), 404 - - -@app.errorhandler(500) -def internal_error(error): - db.session.rollback() - return render_template('500.html'), 500 diff --git a/app/errors/__init__.py b/app/errors/__init__.py new file mode 100644 index 0000000..5701c1d --- /dev/null +++ b/app/errors/__init__.py @@ -0,0 +1,5 @@ +from flask import Blueprint + +bp = Blueprint('errors', __name__) + +from app.errors import handlers diff --git a/app/errors/handlers.py b/app/errors/handlers.py new file mode 100644 index 0000000..4a40ad9 --- /dev/null +++ b/app/errors/handlers.py @@ -0,0 +1,14 @@ +from flask import render_template +from app import db +from app.errors import bp + + +@bp.app_errorhandler(404) +def not_found_error(error): + return render_template('errors/404.html'), 404 + + +@bp.app_errorhandler(500) +def internal_error(error): + db.session.rollback() + return render_template('errors/500.html'), 500 diff --git a/app/main/__init__.py b/app/main/__init__.py new file mode 100644 index 0000000..3b580b0 --- /dev/null +++ b/app/main/__init__.py @@ -0,0 +1,5 @@ +from flask import Blueprint + +bp = Blueprint('main', __name__) + +from app.main import routes diff --git a/app/main/forms.py b/app/main/forms.py new file mode 100644 index 0000000..8dec298 --- /dev/null +++ b/app/main/forms.py @@ -0,0 +1,33 @@ +from flask import request +from flask_wtf import FlaskForm +from wtforms import StringField, SubmitField, TextAreaField +from wtforms.validators import ValidationError, DataRequired, Length +from flask_babel import _, lazy_gettext as _l +from app.models import User + + +class EditProfileForm(FlaskForm): + username = StringField(_l('Username'), validators=[DataRequired()]) + about_me = TextAreaField(_l('About me'), + validators=[Length(min=0, max=140)]) + submit = SubmitField(_l('Submit')) + + def __init__(self, original_username, *args, **kwargs): + super().__init__(*args, **kwargs) + self.original_username = original_username + + def validate_username(self, username): + if username.data != self.original_username: + user = User.query.filter_by(username=self.username.data).first() + if user is not None: + raise ValidationError(_('Please use a different username.')) + + +class EmptyForm(FlaskForm): + submit = SubmitField('Submit') + + +class PostForm(FlaskForm): + post = TextAreaField(_l('Say something'), validators=[DataRequired()]) + submit = SubmitField(_l('Submit')) + diff --git a/app/main/routes.py b/app/main/routes.py new file mode 100644 index 0000000..bccd549 --- /dev/null +++ b/app/main/routes.py @@ -0,0 +1,147 @@ +from datetime import datetime +from flask import render_template, flash, redirect, url_for, request, g, \ + jsonify, current_app +from flask_login import current_user, login_required +from flask_babel import _, get_locale +from langdetect import detect, LangDetectException +from app import db +from app.main.forms import EditProfileForm, EmptyForm, PostForm +from app.models import User, Post +from app.translate import translate +from app.main import bp + + +@bp.before_app_request +def before_request(): + if current_user.is_authenticated: + current_user.last_seen = datetime.utcnow() + db.session.commit() + g.locale = str(get_locale()) + + +@bp.route('/', methods=['GET', 'POST']) +@bp.route('/index', methods=['GET', 'POST']) +@login_required +def index(): + form = PostForm() + if form.validate_on_submit(): + try: + language = detect(form.post.data) + except LangDetectException: + language = '' + post = Post(body=form.post.data, author=current_user, + language=language) + db.session.add(post) + db.session.commit() + flash(_('Your post is now live!')) + return redirect(url_for('main.index')) + page = request.args.get('page', 1, type=int) + posts = current_user.following_posts().paginate( + page=page, per_page=current_app.config['POSTS_PER_PAGE'], + error_out=False) + next_url = url_for('main.index', page=posts.next_num) \ + if posts.has_next else None + prev_url = url_for('main.index', page=posts.prev_num) \ + if posts.has_prev else None + return render_template('index.html', title=_('Home'), form=form, + posts=posts.items, next_url=next_url, + prev_url=prev_url) + + +@bp.route('/explore') +@login_required +def explore(): + page = request.args.get('page', 1, type=int) + posts = Post.query.order_by(Post.timestamp.desc()).paginate( + page=page, per_page=current_app.config['POSTS_PER_PAGE'], + error_out=False) + next_url = url_for('main.explore', page=posts.next_num) \ + if posts.has_next else None + prev_url = url_for('main.explore', page=posts.prev_num) \ + if posts.has_prev else None + return render_template('index.html', title=_('Explore'), + posts=posts.items, next_url=next_url, + prev_url=prev_url) + + +@bp.route('/user/') +@login_required +def user(username): + user = User.query.filter_by(username=username).first_or_404() + page = request.args.get('page', 1, type=int) + posts = user.posts.order_by(Post.timestamp.desc()).paginate( + page=page, per_page=current_app.config['POSTS_PER_PAGE'], + error_out=False) + next_url = url_for('main.user', username=user.username, + page=posts.next_num) if posts.has_next else None + prev_url = url_for('main.user', username=user.username, + page=posts.prev_num) if posts.has_prev else None + form = EmptyForm() + return render_template('user.html', user=user, posts=posts.items, + next_url=next_url, prev_url=prev_url, form=form) + + +@bp.route('/edit_profile', methods=['GET', 'POST']) +@login_required +def edit_profile(): + form = EditProfileForm(current_user.username) + if form.validate_on_submit(): + current_user.username = form.username.data + current_user.about_me = form.about_me.data + db.session.commit() + flash(_('Your changes have been saved.')) + return redirect(url_for('main.edit_profile')) + elif request.method == 'GET': + form.username.data = current_user.username + form.about_me.data = current_user.about_me + return render_template('edit_profile.html', title=_('Edit Profile'), + form=form) + + +@bp.route('/follow/', methods=['POST']) +@login_required +def follow(username): + form = EmptyForm() + if form.validate_on_submit(): + user = User.query.filter_by(username=username).first() + if user is None: + flash(_('User %(username)s not found.', username=username)) + return redirect(url_for('main.index')) + if user == current_user: + flash(_('You cannot follow yourself!')) + return redirect(url_for('main.user', username=username)) + current_user.follow(user) + db.session.commit() + flash(_('You are following %(username)s!', username=username)) + return redirect(url_for('main.user', username=username)) + else: + return redirect(url_for('main.index')) + + +@bp.route('/unfollow/', methods=['POST']) +@login_required +def unfollow(username): + form = EmptyForm() + if form.validate_on_submit(): + user = User.query.filter_by(username=username).first() + if user is None: + flash(_('User %(username)s not found.', username=username)) + return redirect(url_for('main.index')) + if user == current_user: + flash(_('You cannot unfollow yourself!')) + return redirect(url_for('main.user', username=username)) + current_user.unfollow(user) + db.session.commit() + flash(_('You are not following %(username)s.', username=username)) + return redirect(url_for('main.user', username=username)) + else: + return redirect(url_for('main.index')) + + +@bp.route('/translate', methods=['POST']) +@login_required +def translate_text(): + data = request.get_json() + return {'text': translate(data['text'], + data['source_language'], + data['dest_language'])} diff --git a/app/models.py b/app/models.py index 4d61a74..a8a9edc 100644 --- a/app/models.py +++ b/app/models.py @@ -1,10 +1,11 @@ from datetime import datetime from hashlib import md5 from time import time +from flask import current_app from flask_login import UserMixin from werkzeug.security import generate_password_hash, check_password_hash import jwt -from app import app, db, login +from app import db, login followers = db.Table( @@ -69,12 +70,12 @@ class User(UserMixin, db.Model): def get_reset_password_token(self, expires_in=600): return jwt.encode( {'reset_password': self.id, 'exp': time() + expires_in}, - app.config['SECRET_KEY'], algorithm='HS256') + current_app.config['SECRET_KEY'], algorithm='HS256') @staticmethod def verify_reset_password_token(token): try: - id = jwt.decode(token, app.config['SECRET_KEY'], + id = jwt.decode(token, current_app.config['SECRET_KEY'], algorithms=['HS256'])['reset_password'] except: return diff --git a/app/routes.py b/app/routes.py deleted file mode 100644 index b08ce04..0000000 --- a/app/routes.py +++ /dev/null @@ -1,216 +0,0 @@ -from datetime import datetime -from flask import render_template, flash, redirect, url_for, request, g -from flask_login import login_user, logout_user, current_user, login_required -from werkzeug.urls import url_parse -from flask_babel import _, get_locale -from langdetect import detect, LangDetectException -from app import app, db -from app.forms import LoginForm, RegistrationForm, EditProfileForm, \ - EmptyForm, PostForm, ResetPasswordRequestForm, ResetPasswordForm -from app.models import User, Post -from app.email import send_password_reset_email -from app.translate import translate - - -@app.before_request -def before_request(): - if current_user.is_authenticated: - current_user.last_seen = datetime.utcnow() - db.session.commit() - g.locale = str(get_locale()) - - -@app.route('/', methods=['GET', 'POST']) -@app.route('/index', methods=['GET', 'POST']) -@login_required -def index(): - form = PostForm() - if form.validate_on_submit(): - try: - language = detect(form.post.data) - except LangDetectException: - language = '' - post = Post(body=form.post.data, author=current_user, - language=language) - db.session.add(post) - db.session.commit() - flash(_('Your post is now live!')) - return redirect(url_for('index')) - page = request.args.get('page', 1, type=int) - posts = current_user.following_posts().paginate( - page=page, per_page=app.config['POSTS_PER_PAGE'], error_out=False) - next_url = url_for('index', page=posts.next_num) \ - if posts.has_next else None - prev_url = url_for('index', page=posts.prev_num) \ - if posts.has_prev else None - return render_template('index.html', title=_('Home'), form=form, - posts=posts.items, next_url=next_url, - prev_url=prev_url) - - -@app.route('/explore') -@login_required -def explore(): - page = request.args.get('page', 1, type=int) - posts = Post.query.order_by(Post.timestamp.desc()).paginate( - page=page, per_page=app.config['POSTS_PER_PAGE'], error_out=False) - next_url = url_for('explore', page=posts.next_num) \ - if posts.has_next else None - prev_url = url_for('explore', page=posts.prev_num) \ - if posts.has_prev else None - return render_template('index.html', title=_('Explore'), - posts=posts.items, next_url=next_url, - prev_url=prev_url) - - -@app.route('/login', methods=['GET', 'POST']) -def login(): - if current_user.is_authenticated: - return redirect(url_for('index')) - form = LoginForm() - if form.validate_on_submit(): - user = User.query.filter_by(username=form.username.data).first() - if user is None or not user.check_password(form.password.data): - flash(_('Invalid username or password')) - return redirect(url_for('login')) - login_user(user, remember=form.remember_me.data) - next_page = request.args.get('next') - if not next_page or url_parse(next_page).netloc != '': - next_page = url_for('index') - return redirect(next_page) - return render_template('login.html', title=_('Sign In'), form=form) - - -@app.route('/logout') -def logout(): - logout_user() - return redirect(url_for('index')) - - -@app.route('/register', methods=['GET', 'POST']) -def register(): - if current_user.is_authenticated: - return redirect(url_for('index')) - form = RegistrationForm() - if form.validate_on_submit(): - user = User(username=form.username.data, email=form.email.data) - user.set_password(form.password.data) - db.session.add(user) - db.session.commit() - flash(_('Congratulations, you are now a registered user!')) - return redirect(url_for('login')) - return render_template('register.html', title=_('Register'), form=form) - - -@app.route('/reset_password_request', methods=['GET', 'POST']) -def reset_password_request(): - if current_user.is_authenticated: - return redirect(url_for('index')) - form = ResetPasswordRequestForm() - if form.validate_on_submit(): - user = User.query.filter_by(email=form.email.data).first() - if user: - send_password_reset_email(user) - flash( - _('Check your email for the instructions to reset your password')) - return redirect(url_for('login')) - return render_template('reset_password_request.html', - title=_('Reset Password'), form=form) - - -@app.route('/reset_password/', methods=['GET', 'POST']) -def reset_password(token): - if current_user.is_authenticated: - return redirect(url_for('index')) - user = User.verify_reset_password_token(token) - if not user: - return redirect(url_for('index')) - form = ResetPasswordForm() - if form.validate_on_submit(): - user.set_password(form.password.data) - db.session.commit() - flash(_('Your password has been reset.')) - return redirect(url_for('login')) - return render_template('reset_password.html', form=form) - - -@app.route('/user/') -@login_required -def user(username): - user = User.query.filter_by(username=username).first_or_404() - page = request.args.get('page', 1, type=int) - posts = user.posts.order_by(Post.timestamp.desc()).paginate( - page=page, per_page=app.config['POSTS_PER_PAGE'], error_out=False) - next_url = url_for('user', username=user.username, page=posts.next_num) \ - if posts.has_next else None - prev_url = url_for('user', username=user.username, page=posts.prev_num) \ - if posts.has_prev else None - form = EmptyForm() - return render_template('user.html', user=user, posts=posts.items, - next_url=next_url, prev_url=prev_url, form=form) - - -@app.route('/edit_profile', methods=['GET', 'POST']) -@login_required -def edit_profile(): - form = EditProfileForm(current_user.username) - if form.validate_on_submit(): - current_user.username = form.username.data - current_user.about_me = form.about_me.data - db.session.commit() - flash(_('Your changes have been saved.')) - return redirect(url_for('edit_profile')) - elif request.method == 'GET': - form.username.data = current_user.username - form.about_me.data = current_user.about_me - return render_template('edit_profile.html', title=_('Edit Profile'), - form=form) - - -@app.route('/follow/', methods=['POST']) -@login_required -def follow(username): - form = EmptyForm() - if form.validate_on_submit(): - user = User.query.filter_by(username=username).first() - if user is None: - flash(_('User %(username)s not found.', username=username)) - return redirect(url_for('index')) - if user == current_user: - flash(_('You cannot follow yourself!')) - return redirect(url_for('user', username=username)) - current_user.follow(user) - db.session.commit() - flash(_('You are following %(username)s!', username=username)) - return redirect(url_for('user', username=username)) - else: - return redirect(url_for('index')) - - -@app.route('/unfollow/', methods=['POST']) -@login_required -def unfollow(username): - form = EmptyForm() - if form.validate_on_submit(): - user = User.query.filter_by(username=username).first() - if user is None: - flash(_('User %(username)s not found.', username=username)) - return redirect(url_for('index')) - if user == current_user: - flash(_('You cannot unfollow yourself!')) - return redirect(url_for('user', username=username)) - current_user.unfollow(user) - db.session.commit() - flash(_('You are not following %(username)s.', username=username)) - return redirect(url_for('user', username=username)) - else: - return redirect(url_for('index')) - - -@app.route('/translate', methods=['POST']) -@login_required -def translate_text(): - data = request.get_json() - return {'text': translate(data['text'], - data['source_language'], - data['dest_language'])} diff --git a/app/templates/_post.html b/app/templates/_post.html index c1e33c0..8c0a9cd 100644 --- a/app/templates/_post.html +++ b/app/templates/_post.html @@ -1,13 +1,13 @@
- + {% set user_link %} - + {{ post.author.username }} {% endset %} diff --git a/app/templates/login.html b/app/templates/auth/login.html similarity index 52% rename from app/templates/login.html rename to app/templates/auth/login.html index bd16ffd..4fe5669 100644 --- a/app/templates/login.html +++ b/app/templates/auth/login.html @@ -4,9 +4,9 @@ {% block content %}

{{ _('Sign In') }}

{{ wtf.quick_form(form) }} -

{{ _('New User?') }} {{ _('Click to Register!') }}

+

{{ _('New User?') }} {{ _('Click to Register!') }}

{{ _('Forgot Your Password?') }} - {{ _('Click to Reset It') }} + {{ _('Click to Reset It') }}

{% endblock %} diff --git a/app/templates/register.html b/app/templates/auth/register.html similarity index 100% rename from app/templates/register.html rename to app/templates/auth/register.html diff --git a/app/templates/reset_password.html b/app/templates/auth/reset_password.html similarity index 100% rename from app/templates/reset_password.html rename to app/templates/auth/reset_password.html diff --git a/app/templates/reset_password_request.html b/app/templates/auth/reset_password_request.html similarity index 100% rename from app/templates/reset_password_request.html rename to app/templates/auth/reset_password_request.html diff --git a/app/templates/base.html b/app/templates/base.html index 79d05b3..cb6e18a 100644 --- a/app/templates/base.html +++ b/app/templates/base.html @@ -13,30 +13,30 @@