提交 50c068cc 编写于 作者: checklate's avatar checklate

根据参考书籍提交(移除书中email及头像)

上级 c63ce1dd
...@@ -6,8 +6,22 @@ ...@@ -6,8 +6,22 @@
# Author: GaoNingNing # Author: GaoNingNing
# Date: 2022/11/27 # Date: 2022/11/27
# ------------------------------------------------------------------------------- # -------------------------------------------------------------------------------
from flask import Flask from flask import Flask, render_template
from flask_bootstrap import Bootstrap
from flask_mail import Mail
from flask_moment import Moment
from flask_sqlalchemy import SQLAlchemy
from config import config from config import config
from flask_login import LoginManager
from flask_pagedown import PageDown
bootstrap = Bootstrap()
pagedown = PageDown()
mail = Mail()
moment = Moment()
db = SQLAlchemy()
login_manager = LoginManager()
login_manager.login_view = 'auth.login'
def create_app(config_name): def create_app(config_name):
...@@ -15,7 +29,18 @@ def create_app(config_name): ...@@ -15,7 +29,18 @@ def create_app(config_name):
app.config.from_object(config[config_name]) app.config.from_object(config[config_name])
config[config_name].init_app(app) config[config_name].init_app(app)
# 添加主路由 bootstrap.init_app(app)
mail.init_app(app)
moment.init_app(app)
db.init_app(app)
login_manager.init_app(app)
pagedown.init_app(app)
# 添加路由和自定义的错误页面
from .main import main as main_blueprint from .main import main as main_blueprint
app.register_blueprint(main_blueprint) app.register_blueprint(main_blueprint)
from .auth import auth as auth_blueprint
app.register_blueprint(auth_blueprint, url_prefix='/auth')
return app return app
# -*- coding: utf-8 -*-#
# -------------------------------------------------------------------------------
# Name: __init__.py
# Description: 身份验证模块
# Author: GaoNingNing
# Date: 2022/11/27
# -------------------------------------------------------------------------------
from flask import Blueprint
auth = Blueprint('auth', __name__)
from . import views
\ No newline at end of file
# -*- coding: utf-8 -*-#
# -------------------------------------------------------------------------------
# Name: form
# Description:
# Author: GaoNingNing
# Date: 2022/11/27
# -------------------------------------------------------------------------------
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, BooleanField, SubmitField
from wtforms.validators import DataRequired, Length, Email, Regexp, EqualTo
from wtforms import ValidationError
from ..models import User
class RegistrationForm(FlaskForm):
username = StringField('Username', validators=[
DataRequired(), Length(1, 64),
Regexp('^[A-Za-z][A-Za-z0-9_.]*$', 0,
'Usernames must have only letters, numbers, dots or '
'underscores')])
password = PasswordField('Password', validators=[
DataRequired(), EqualTo('password2', message='Passwords must match.')])
password2 = PasswordField('Confirm password', validators=[DataRequired()])
submit = SubmitField('Register')
def validate_username(self, field):
if User.query.filter_by(username=field.data).first():
raise ValidationError('Username already in use.')
class LoginForm(FlaskForm):
username = StringField('Username', validators=[DataRequired()])
password = PasswordField('Password', validators=[DataRequired()])
remember_me = BooleanField('Keep me logged in')
submit = SubmitField('Log In')
# -*- coding: utf-8 -*-#
# -------------------------------------------------------------------------------
# Name: views
# Description:
# Author: GaoNingNing
# Date: 2022/11/27
# -------------------------------------------------------------------------------
from flask import render_template, redirect, request, url_for, flash
from flask_login import login_user, current_user
from . import auth
from ..models import User
from .forms import LoginForm, RegistrationForm
from flask_login import logout_user, login_required
from app import db
@auth.route('/login', methods=['GET', 'POST'])
def login():
form = LoginForm()
if form.validate_on_submit():
user = User.query.filter_by(username=form.username.data).first()
if user is not None and user.verify_password(form.password.data):
login_user(user, form.remember_me.data)
next = request.args.get('next')
if next is None or not next.startswith('/'):
next = url_for('main.index')
return redirect(next)
flash('Invalid username or password.')
return render_template('auth/login.html', form=form)
@auth.route('/logout')
@login_required
def logout():
logout_user()
flash('You have been logged out.')
return redirect(url_for('main.index'))
@auth.route('/register', methods=['GET', 'POST'])
def register():
form = RegistrationForm()
if form.validate_on_submit():
user = User(username=form.username.data,
password=form.password.data)
db.session.add(user)
db.session.commit()
flash('You can now login.')
return redirect(url_for('auth.login'))
return render_template('auth/register.html', form=form)
@auth.before_app_request
def before_request():
if current_user.is_authenticated:
current_user.ping()
# if not current_user.confirmed \
# and request.endpoint \
# and request.blueprint != 'auth' \
# and request.endpoint != 'static':
# return redirect(url_for('auth.unconfirmed'))
# -*- coding: utf-8 -*-#
# -------------------------------------------------------------------------------
# Name: decorators
# Description:
# Author: GaoNingNing
# Date: 2022/11/27
# -------------------------------------------------------------------------------
from functools import wraps
from flask import abort
from flask_login import current_user
from .models import Permission
def permission_required(permission):
def decorator(f):
@wraps(f)
def decorated_function(*args, **kwargs):
if not current_user.can(permission):
abort(403)
return f(*args, **kwargs)
return decorated_function
return decorator
def admin_required(f):
return permission_required(Permission.ADMIN)(f)
...@@ -9,6 +9,17 @@ ...@@ -9,6 +9,17 @@
from flask import Blueprint from flask import Blueprint
# 主蓝本 # 主蓝本
from ..models import Permission
main = Blueprint('main', __name__) main = Blueprint('main', __name__)
from . import views from . import views
@main.app_context_processor
def inject_permissions():
"""
使用上下文处理器。在渲染时,上下文处理器能让变量在所有模板中可访问, 为了避免每次调用render_template()时都多添加一个模板参数
:return:
"""
return dict(Permission=Permission)
# -*- coding: utf-8 -*-#
# -------------------------------------------------------------------------------
# Name: errors
# Description:
# Author: GaoNingNing
# Date: 2022/11/27
# -------------------------------------------------------------------------------
from flask import render_template
from . import main
@main.app_errorhandler(404)
def page_not_found(e):
return render_template('404.html'), 404
@main.app_errorhandler(500)
def internal_server_error(e):
return render_template('500.html'), 500
# -*- coding: utf-8 -*-#
# -------------------------------------------------------------------------------
# Name: form
# Description:
# Author: GaoNingNing
# Date: 2022/11/27
# -------------------------------------------------------------------------------
from flask_pagedown.fields import PageDownField
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField, TextAreaField, BooleanField, SelectField
from wtforms.validators import DataRequired, Length, Email, Regexp, ValidationError, input_required
from app.models import Role, User
class NameForm(FlaskForm):
name = StringField('What is your name?', validators=[DataRequired()])
submit = SubmitField('Submit')
class EditProfileForm(FlaskForm):
name = StringField('Real name', validators=[Length(0, 64)])
location = StringField('Location', validators=[Length(0, 64)])
about_me = TextAreaField('About me')
submit = SubmitField('Submit')
class EditProfileAdminForm(FlaskForm):
username = StringField('Username', validators=[
DataRequired(), Length(1, 64),
Regexp('^[A-Za-z][A-Za-z0-9_.]*$', 0,
'Usernames must have only letters, numbers, dots or '
'underscores')])
confirmed = BooleanField('Confirmed')
role = SelectField('Role', coerce=int)
name = StringField('Real name', validators=[Length(0, 64)])
location = StringField('Location', validators=[Length(0, 64)])
about_me = TextAreaField('About me')
submit = SubmitField('Submit')
def __init__(self, user, *args, **kwargs):
super(EditProfileAdminForm, self).__init__(*args, **kwargs)
self.role.choices = [(role.id, role.name)
for role in Role.query.order_by(Role.name).all()]
self.user = user
def validate_email(self, field):
if field.data != self.user.email and \
User.query.filter_by(email=field.data).first():
raise ValidationError('Email already registered.')
def validate_username(self, field):
if field.data != self.user.username and \
User.query.filter_by(username=field.data).first():
raise ValidationError('Username already in use.')
class PostForm(FlaskForm):
body = PageDownField("What's on your mind?", validators=[input_required()])
submit = SubmitField('Submit')
class CommentForm(FlaskForm):
body = StringField('', validators=[DataRequired()])
submit = SubmitField('Submit')
...@@ -6,9 +6,250 @@ ...@@ -6,9 +6,250 @@
# Author: GaoNingNing # Author: GaoNingNing
# Date: 2022/11/27 # Date: 2022/11/27
# ------------------------------------------------------------------------------- # -------------------------------------------------------------------------------
from app.main import main from flask import render_template, redirect, url_for, flash, abort, request, current_app, make_response
from flask_login import login_required, current_user
from . import main
from .forms import EditProfileForm, EditProfileAdminForm, PostForm, CommentForm
from .. import db
from ..decorators import admin_required, permission_required
from ..models import User, Role, Permission, Post, Comment
@main.route('/')
@main.route('/', methods=['GET', 'POST'])
def index(): def index():
return '<h1>Hello World!</h1>' form = PostForm()
print(Permission.WRITE)
if current_user.can(Permission.WRITE) and form.validate_on_submit():
post = Post(body=form.body.data,
author=current_user._get_current_object())
db.session.add(post)
db.session.commit()
return redirect(url_for('.index'))
show_followed = False
if current_user.is_authenticated:
show_followed = bool(request.cookies.get('show_followed', ''))
if show_followed:
query = current_user.followed_posts
else:
query = Post.query
pagination = query.order_by(Post.timestamp.desc()).paginate(
per_page=current_app.config['FLASKY_POSTS_PER_PAGE'],
error_out=False)
posts = pagination.items
return render_template('index.html', form=form, posts=posts,
show_followed=show_followed, pagination=pagination)
@main.route('/all')
@login_required
def show_all():
resp = make_response(redirect(url_for('.index')))
resp.set_cookie('show_followed', '', max_age=30 * 24 * 60 * 60) # 30天
return resp
@main.route('/followed')
@login_required
def show_followed():
resp = make_response(redirect(url_for('.index')))
resp.set_cookie('show_followed', '1', max_age=30 * 24 * 60 * 60) # 30天
return resp
@main.route('/user/<username>')
def user(username):
user = User.query.filter_by(username=username).first()
if user is None:
abort(404)
posts = user.posts.order_by(Post.timestamp.desc()).all()
return render_template('user.html', user=user, posts=posts)
@main.route('/edit-profile', methods=['GET', 'POST'])
@login_required
def edit_profile():
form = EditProfileForm()
if form.validate_on_submit():
current_user.name = form.name.data
current_user.location = form.location.data
current_user.about_me = form.about_me.data
db.session.add(current_user._get_current_object())
db.session.commit()
flash('Your profile has been updated.')
return redirect(url_for('.user', username=current_user.username))
form.name.data = current_user.name
form.location.data = current_user.location
form.about_me.data = current_user.about_me
return render_template('edit_profile.html', form=form)
@main.route('/edit-profile/<int:id>', methods=['GET', 'POST'])
@login_required
@admin_required
def edit_profile_admin(id):
user = User.query.get_or_404(id)
form = EditProfileAdminForm(user=user)
if form.validate_on_submit():
user.username = form.username.data
user.confirmed = form.confirmed.data
user.role = Role.query.get(form.role.data)
user.name = form.name.data
user.location = form.location.data
user.about_me = form.about_me.data
db.session.add(user)
db.session.commit()
flash('The profile has been updated.')
return redirect(url_for('.user', username=user.username))
form.username.data = user.username
form.confirmed.data = user.confirmed
form.role.data = user.role_id
form.name.data = user.name
form.location.data = user.location
form.about_me.data = user.about_me
return render_template('edit_profile.html', form=form, user=user)
@main.route('/post/<int:id>', methods=['GET', 'POST'])
def post(id):
post = Post.query.get_or_404(id)
form = CommentForm()
if form.validate_on_submit():
comment = Comment(body=form.body.data,
post=post,
author=current_user._get_current_object())
db.session.add(comment)
db.session.commit()
flash('Your comment has been published.')
return redirect(url_for('.post', id=post.id, page=-1))
page = request.args.get('page', 1, type=int)
if page == -1:
page = (post.comments.count() - 1) // \
current_app.config['FLASKY_COMMENTS_PER_PAGE'] + 1
pagination = post.comments.order_by(Comment.timestamp.asc()).paginate(
page, per_page=current_app.config['FLASKY_COMMENTS_PER_PAGE'],
error_out=False)
comments = pagination.items
return render_template('post.html', posts=[post], form=form,
comments=comments, pagination=pagination)
@main.route('/edit/<int:id>', methods=['GET', 'POST'])
@login_required
def edit(id):
post = Post.query.get_or_404(id)
if current_user != post.author and \
not current_user.can(Permission.ADMIN):
abort(403)
form = PostForm()
if form.validate_on_submit():
post.body = form.body.data
db.session.add(post)
db.session.commit()
flash('The post has been updated.')
return redirect(url_for('.post', id=post.id))
form.body.data = post.body
return render_template('edit_post.html', form=form)
@main.route('/follow/<username>')
@login_required
@permission_required(Permission.FOLLOW)
def follow(username):
user = User.query.filter_by(username=username).first()
if user is None:
flash('Invalid user.')
return redirect(url_for('.index'))
if current_user.is_following(user):
flash('You are already following this user.')
return redirect(url_for('.user', username=username))
current_user.follow(user)
db.session.commit()
flash('You are now following %s.' % username)
return redirect(url_for('.user', username=username))
@main.route('/unfollow/<username>')
@login_required
@permission_required(Permission.FOLLOW)
def unfollow(username):
user = User.query.filter_by(username=username).first()
if user is None:
flash('Invalid user.')
return redirect(url_for('.index'))
if not current_user.is_following(user):
flash('You are not following this user.')
return redirect(url_for('.user', username=username))
current_user.unfollow(user)
db.session.commit()
flash('You are now unfollow %s.' % username)
return redirect(url_for('.user', username=username))
@main.route('/followers/<username>')
def followers(username):
user = User.query.filter_by(username=username).first()
if user is None:
flash('Invalid user.')
return redirect(url_for('.index'))
page = request.args.get('page', 1, type=int)
pagination = user.followers.paginate(
per_page=current_app.config['FLASKY_FOLLOWERS_PER_PAGE'],
error_out=False)
follows = [{'user': item.follower, 'timestamp': item.timestamp}
for item in pagination.items]
return render_template('followers.html', user=user, title="Followers of",
endpoint='.followers', pagination=pagination,
follows=follows)
@main.route('/followed_by/<username>')
def followed_by(username):
user = User.query.filter_by(username=username).first()
if user is None:
flash('Invalid user.')
return redirect(url_for('.index'))
page = request.args.get('page', 1, type=int)
pagination = user.followed.paginate(
per_page=current_app.config['FLASKY_FOLLOWERS_PER_PAGE'],
error_out=False)
follows = [{'user': item.follower, 'timestamp': item.timestamp}
for item in pagination.items]
return render_template('followers.html', user=user, title="Followed By",
endpoint='.followers', pagination=pagination,
follows=follows)
@main.route('/moderate')
@login_required
@permission_required(Permission.MODERATE)
def moderate():
page = request.args.get('page', 1, type=int)
pagination = Comment.query.order_by(Comment.timestamp.desc()).paginate(
page, per_page=current_app.config['FLASKY_COMMENTS_PER_PAGE'],
error_out=False)
comments = pagination.items
return render_template('moderate.html', comments=comments,
pagination=pagination, page=page)
@main.route('/moderate/enable/<int:id>')
@login_required
@permission_required(Permission.MODERATE)
def moderate_enable(id):
comment = Comment.query.get_or_404(id)
comment.disabled = False
db.session.add(comment)
return redirect(url_for('.moderate',
page=request.args.get('page', 1, type=int)))
@main.route('/moderate/disable/<int:id>')
@login_required
@permission_required(Permission.MODERATE)
def moderate_disable(id):
comment = Comment.query.get_or_404(id)
comment.disabled = True
db.session.add(comment)
return redirect(url_for('.moderate',
page=request.args.get('page', 1, type=int)))
...@@ -6,3 +6,283 @@ ...@@ -6,3 +6,283 @@
# Author: GaoNingNing # Author: GaoNingNing
# Date: 2022/11/27 # Date: 2022/11/27
# ------------------------------------------------------------------------------- # -------------------------------------------------------------------------------
# -*- coding: utf-8 -*-#
# -------------------------------------------------------------------------------
# Name: models
# Description:
# Author: GaoNingNing
# Date: 2022/11/20
# -------------------------------------------------------------------------------
import hashlib
from datetime import datetime
import bleach
from flask import current_app, request, url_for
from itsdangerous import Serializer
from markdown import markdown
from wtforms import ValidationError
from app import db
from werkzeug.security import generate_password_hash, check_password_hash
from flask_login import UserMixin, AnonymousUserMixin
from . import login_manager
class Permission:
FOLLOW = 1
COMMENT = 2
WRITE = 4
MODERATE = 8
ADMIN = 16
class Role(db.Model):
__tablename__ = 'roles'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64), unique=True)
default = db.Column(db.Boolean, default=False, index=True)
permissions = db.Column(db.Integer)
users = db.relationship('User', backref='role', lazy='dynamic')
def __init__(self, **kwargs):
super(Role, self).__init__(**kwargs)
if self.permissions is None:
self.permissions = 0
def add_permission(self, perm):
if not self.has_permission(perm):
self.permissions += perm
def remove_permission(self, perm):
if self.has_permission(perm):
self.permissions -= perm
def reset_permissions(self):
self.permissions = 0
def has_permission(self, perm):
return self.permissions & perm == perm
@staticmethod
def insert_roles():
roles = {
'User': [Permission.FOLLOW, Permission.COMMENT, Permission.WRITE],
'Moderator': [Permission.FOLLOW, Permission.COMMENT,
Permission.WRITE, Permission.MODERATE],
'Administrator': [Permission.FOLLOW, Permission.COMMENT,
Permission.WRITE, Permission.MODERATE,
Permission.ADMIN],
}
default_role = 'User'
for r in roles:
role = Role.query.filter_by(name=r).first()
if role is None:
role = Role(name=r)
role.reset_permissions()
for perm in roles[r]:
role.add_permission(perm)
role.default = (role.name == default_role)
db.session.add(role)
db.session.commit()
class Post(db.Model):
__tablename__ = 'posts'
id = db.Column(db.Integer, primary_key=True)
body = db.Column(db.Text)
body_html = db.Column(db.Text)
timestamp = db.Column(db.DateTime, index=True, default=datetime.utcnow)
author_id = db.Column(db.Integer, db.ForeignKey('users.id'))
comments = db.relationship('Comment', backref='post', lazy='dynamic')
@staticmethod
def on_changed_body(target, value, oldvalue, initiator):
allowed_tags = ['a', 'abbr', 'acronym', 'b', 'blockquote', 'code',
'em', 'i', 'li', 'ol', 'pre', 'strong', 'ul',
'h1', 'h2', 'h3', 'p']
target.body_html = bleach.linkify(bleach.clean(
markdown(value, output_format='html'),
tags=allowed_tags, strip=True))
def to_json(self):
json_post = {
'url': url_for('api.get_post', id=self.id),
'body': self.body,
'body_html': self.body_html,
'timestamp': self.timestamp,
'author_url': url_for('api.get_user', id=self.author_id),
'comments_url': url_for('api.get_post_comments', id=self.id),
'comment_count': self.comments.count()
}
return json_post
@staticmethod
def from_json(json_post):
body = json_post.get('body')
if body is None or body == '':
raise ValidationError('post does not have a body')
return Post(body=body)
class Follow(db.Model):
__tablename__ = 'follows'
follower_id = db.Column(db.Integer, db.ForeignKey('users.id'),
primary_key=True)
followed_id = db.Column(db.Integer, db.ForeignKey('users.id'),
primary_key=True)
timestamp = db.Column(db.DateTime, default=datetime.utcnow)
class User(UserMixin, db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64), unique=True, index=True)
password_hash = db.Column(db.String(128))
role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))
name = db.Column(db.String(64))
location = db.Column(db.String(64))
about_me = db.Column(db.Text())
member_since = db.Column(db.DateTime(), default=datetime.utcnow)
last_seen = db.Column(db.DateTime(), default=datetime.utcnow)
posts = db.relationship('Post', backref='author', lazy='dynamic')
followed = db.relationship('Follow',
foreign_keys=[Follow.follower_id],
backref=db.backref('follower', lazy='joined'),
lazy='dynamic',
cascade='all, delete-orphan')
followers = db.relationship('Follow',
foreign_keys=[Follow.followed_id],
backref=db.backref('followed', lazy='joined'),
lazy='dynamic',
cascade='all, delete-orphan')
comments = db.relationship('Comment', backref='author', lazy='dynamic')
def __init__(self, **kwargs):
super(User, self).__init__(**kwargs)
if self.role is None:
if self.role is None:
self.role = Role.query.filter_by(default=True).first()
self.follow(self)
@staticmethod
def add_self_follows():
for user in User.query.all():
if not user.is_following(user):
user.follow(user)
db.session.add(user)
db.session.commit()
@property
def password(self):
raise AttributeError('password is not a readable attribute')
@password.setter
def password(self, password):
self.password_hash = generate_password_hash(password)
def verify_password(self, password):
return check_password_hash(self.password_hash, password)
def __repr__(self):
return '<User %r>' % self.username
def can(self, perm):
return self.role is not None and self.role.has_permission(perm)
def is_administrator(self):
return self.can(Permission.ADMIN)
def ping(self):
self.last_seen = datetime.utcnow()
db.session.add(self)
db.session.commit()
def follow(self, user):
if not self.is_following(user):
f = Follow(follower=self, followed=user)
db.session.add(f)
def unfollow(self, user):
f = self.followed.filter_by(followed_id=user.id).first()
if f:
db.session.delete(f)
def is_following(self, user):
if user.id is None:
return False
return self.followed.filter_by(
followed_id=user.id).first() is not None
def is_followed_by(self, user):
if user.id is None:
return False
return self.followers.filter_by(
follower_id=user.id).first() is not None
@property
def followed_posts(self):
return Post.query.join(Follow, Follow.followed_id == Post.author_id) \
.filter(Follow.follower_id == self.id)
def generate_auth_token(self, expiration):
s = Serializer(current_app.config['SECRET_KEY'])
return s.dumps({'id': self.id}).decode('utf-8')
@staticmethod
def verify_auth_token(token):
s = Serializer(current_app.config['SECRET_KEY'])
try:
data = s.loads(token)
except:
return None
return User.query.get(data['id'])
def to_json(self):
json_user = {
'url': url_for('api.get_user', id=self.id),
'username': self.username,
'member_since': self.member_since,
'last_seen': self.last_seen,
'posts_url': url_for('api.get_user_posts', id=self.id),
'followed_posts_url': url_for('api.get_user_followed_posts',
id=self.id),
'post_count': self.posts.count()
}
return json_user
class AnonymousUser(AnonymousUserMixin):
def can(self, permissions):
return False
def is_administrator(self):
return False
login_manager.anonymous_user = AnonymousUser
class Comment(db.Model):
__tablename__ = 'comments'
id = db.Column(db.Integer, primary_key=True)
body = db.Column(db.Text)
body_html = db.Column(db.Text)
timestamp = db.Column(db.DateTime, index=True, default=datetime.utcnow)
disabled = db.Column(db.Boolean)
author_id = db.Column(db.Integer, db.ForeignKey('users.id'))
post_id = db.Column(db.Integer, db.ForeignKey('posts.id'))
@staticmethod
def on_changed_body(target, value, oldvalue, initiator):
allowed_tags = ['a', 'abbr', 'acronym', 'b', 'code', 'em', 'i',
'strong']
target.body_html = bleach.linkify(bleach.clean(
markdown(value, output_format='html'),
tags=allowed_tags, strip=True))
db.event.listen(Comment.body, 'set', Comment.on_changed_body)
@login_manager.user_loader
def load_user(user_id):
return User.query.get(int(user_id))
db.event.listen(Post.body, 'set', Post.on_changed_body)
{% extends "base.html" %}
{% block title %}Flasky - Page Not Found{% endblock %}
{% block page_content %}
<div class="page-header">
<h1>Not Found</h1>
</div>
{% endblock %}
{% extends "base.html" %}
{% block title %}Flasky - Server Error{% endblock %}
{% block page_content %}
<div class="page-header">
<h1>Server Error</h1>
</div>
{% endblock %}
# -*- coding: utf-8 -*-#
# -------------------------------------------------------------------------------
# Name: __init__.py
# Description:
# Author: GaoNingNing
# Date: 2022/11/19
# -------------------------------------------------------------------------------
<ul class="comments">
{% for comment in comments %}
<li class="comment">
<div class="profile-thumbnail">
<a href="{{ url_for('.user', username=comment.author.username) }}">
{{ comment.author.username }}
</a>
</div>
<div class="comment-date">{{ moment(comment.timestamp).fromNow() }}</div>
<div class="comment-author">
<a href="{{ url_for('.user', username=comment.author.username) }}">
{{ comment.author.username }}
</a>
</div>
<div class="comment-body">
{% if comment.body_html %}
{{ comment.body_html | safe }}
{% else %}
{{ comment.body }}
{% endif %}
</div>
<div class="comment-footer">
<a href="{{ url_for('.comment', id=comment.id) }}">
<span class="label label-default">Permalink</span>
</a>
{% if current_user == comment.author %}
<a href="{{ url_for('.edit', id=comment.id) }}">
<span class="label label-primary">Edit</span>
</a>
{% elif current_user.is_administrator() %}
<a href="{{ url_for('.edit', id=comment.id) }}">
<span class="label label-danger">Edit [Admin]</span>
</a>
{% endif %}
</div>
</li>
{% endfor %}
</ul>
\ No newline at end of file
{% macro pagination_widget(pagination, endpoint) %}
<ul class="pagination">
<li{% if not pagination.has_prev %} class="disabled"{% endif %}>
<a href="{% if pagination.has_prev %}{{ url_for(endpoint,
page = pagination.page - 1, **kwargs) }}{% else %}#{% endif %}">
&laquo;
</a>
</li>
{% for p in pagination.iter_pages() %}
{% if p %}
{% if p == pagination.page %}
<li class="active">
<a href="{{ url_for(endpoint, page = p, **kwargs) }}">{{ p }}</a>
</li>
{% else %}
<li>
<a href="{{ url_for(endpoint, page = p, **kwargs) }}">{{ p }}</a>
</li>
{% endif %}
{% else %}
<li class="disabled"><a href="#">&hellip;</a></li>
{% endif %}
{% endfor %}
<li{% if not pagination.has_next %} class="disabled"{% endif %}>
<a href="{% if pagination.has_next %}{{ url_for(endpoint,
page = pagination.page + 1, **kwargs) }}{% else %}#{% endif %}">
&raquo;
</a>
</li>
</ul>
{% endmacro %}
{% extends "base.html" %}
{% import "_macros.html" as macros %}
{% block title %}Flasky - Comment Moderation{% endblock %}
{% block page_content %}
<div class="page-header">
<h1>Comment Moderation</h1>
</div>
{% set moderate = True %}
{% include '_comments.html' %}
{% if pagination %}
<div class="pagination">
{{ macros.pagination_widget(pagination, '.moderate') }}
</div>
{% endif %}
{% endblock %}
<ul class="posts">
{% for post in posts %}
<li class="post">
<div class="profile-thumbnail">
<a href="{{ url_for('.user', username=post.author.username) }}">
{{ post.author.username }}
</a>
</div>
<div class="post-date">{{ moment(post.timestamp).fromNow() }}</div>
<div class="post-author">
<a href="{{ url_for('.user', username=post.author.username) }}">
{{ post.author.username }}
</a>
</div>
<div class="post-body">
{% if post.body_html %}
{{ post.body_html | safe }}
{% else %}
{{ post.body }}
{% endif %}
</div>
<div class="post-footer">
<a href="{{ url_for('.post', id=post.id) }}">
<span class="label label-default">Permalink</span>
</a>
{% if current_user == post.author %}
<a href="{{ url_for('.edit', id=post.id) }}">
<span class="label label-primary">Edit</span>
</a>
{% elif current_user.is_administrator() %}
<a href="{{ url_for('.edit', id=post.id) }}">
<span class="label label-danger">Edit [Admin]</span>
</a>
{% endif %}
</div>
</li>
{% endfor %}
</ul>
\ No newline at end of file
# -*- coding: utf-8 -*-#
# -------------------------------------------------------------------------------
# Name: __init__.py
# Description:
# Author: GaoNingNing
# Date: 2022/11/21
# -------------------------------------------------------------------------------
{% extends "base.html" %}
{% import "bootstrap/wtf.html" as wtf %}
{% block title %}Flasky - Login{% endblock %}
{% block page_content %}
<div class="page-header">
<h1>Login</h1>
</div>
<div class="col-md-4">
{{ wtf.quick_form(form) }}
</div>
<p>
New user?
<a href="{{ url_for('auth.register') }}">
Click here to register
</a>
</p>
{% endblock %}
{% extends "base.html" %}
{% import "bootstrap/wtf.html" as wtf %}
{% block title %}Flasky - register{% endblock %}
{% block page_content %}
<div class="page-header">
<h1>register</h1>
</div>
<div class="col-md-4">
{{ wtf.quick_form(form) }}
</div>
{% endblock %}
{% extends "bootstrap/base.html" %}
{% block title %}Flasky{% endblock %}
{% block navbar %}
<div class="navbar navbar-inverse" role="navigation">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle"
data-toggle="collapse" data-target=".navbar-collapse">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="/">Flasky</a>
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li><a href="/">Home</a></li>
{% if current_user.is_authenticated %}
<li>
<a href="{{ url_for('main.user', username=current_user.username) }}">
Profile
</a>
</li>
{% endif %}
</ul>
<ul class="nav navbar-nav navbar-right">
{% if current_user.is_authenticated %}
<li><a href="{{ url_for('auth.logout') }}">Log Out</a></li>
{% else %}
<li><a href="{{ url_for('auth.login') }}">Log In</a></li>
{% endif %}
</ul>
</div>
</div>
</div>
{% endblock %}
{% block content %}
<div class="container">
{% for message in get_flashed_messages() %}
<div class="alert alert-warning">
<button type="button" class="close" data-dismiss="alert">&times;</button>
{{ message }}
</div>
{% endfor %}
{% block page_content %}{% endblock %}
</div>
{% endblock %}
{% block scripts %}
{{ super() }}
{{ moment.include_moment() }}
{% endblock %}
{% extends "base.html" %}
{% import "bootstrap/wtf.html" as wtf %}
{% block title %}Flasky - Edit Post{% endblock %}
{% block page_content %}
<div class="page-header">
<h1>Edit Post</h1>
</div>
<div>
{{ wtf.quick_form(form) }}
</div>
{% endblock %}
{% block scripts %}
{{ super() }}
{{ pagedown.include_pagedown() }}
{% endblock %}
{% extends "base.html" %}
{% import "bootstrap/wtf.html" as wtf %}
{% block title %}Flasky - EditProfile{% endblock %}
{% block page_content %}
<div class="page-header">
<h1>Edit Your Profile</h1>
</div>
<div class="col-md-4">
{{ wtf.quick_form(form) }}
</div>
{% endblock %}
{% extends "base.html" %}
{% import "bootstrap/wtf.html" as wtf %}
{% import "_macros.html" as macros %}
{% block title %}Flasky{% endblock %}
{% block page_content %}
<div class="page-header">
Hello,
{% if current_user.is_authenticated %}
{{ current_user.username }}
{% else %}
Stranger
{% endif %}!
{% if not known %}
<p>Pleased to meet you!</p>
{% else %}
<p>Happy to see you again!</p>
{% endif %}
</div>
<div>
{% if current_user.can(Permission.WRITE) %}
{{ wtf.quick_form(form) }}
{% endif %}
</div>
{% include '_posts.html' %}
<div class="pagination">
{{ macros.pagination_widget(pagination, '.index') }}
</div>
{% endblock %}
{% block scripts %}
{{ super() }}
{{ pagedown.include_pagedown() }}
{% endblock %}
{% extends "base.html" %}
{% block title %}Flasky - Post{% endblock %}
{% block page_content %}
{% include '_posts.html' %}
{% endblock %}
{% extends "base.html" %}
{% block title %}Flasky - {{ user.username }}{% endblock %}
{% block page_content %}
<div class="page-header">
{{ user.username }}
<div class="profile-header">
<h1>{{ user.username }}</h1>
{% if user.name or user.location %}
<p>
{% if user.name %}{{ user.name }}{% endif %}
{% if user.location %}
From <a href="http://maps.google.com/?q={{ user.location }}">
{{ user.location }}
</a>
{% endif %}
</p>
{% endif %}
{% if current_user.is_administrator() %}
<p><a href="mailto:{{ user.email }}">{{ user.email }}</a></p>
{% endif %}
{% if user.about_me %}<p>{{ user.about_me }}</p>{% endif %}
<p>
Member since {{ moment(user.member_since).format('L') }}.
Last seen {{ moment(user.last_seen).fromNow() }}.
</p>
{% if user == current_user %}
<a class="btn btn-default" href="{{ url_for('.edit_profile') }}">
Edit Profile
</a>
{% endif %}
{% if current_user.is_administrator() %}
<a class="btn btn-danger"
href="{{ url_for('.edit_profile_admin', id=user.id) }}">
Edit Profile [Admin]
</a>
{% endif %}
</div>
</div>
{% if current_user.can(Permission.FOLLOW) and user != current_user %}
{% if not current_user.is_following(user) %}
<a href="{{ url_for('.follow', username=user.username) }}"
class="btn btn-primary">Follow</a>
{% else %}
<a href="{{ url_for('.unfollow', username=user.username) }}"
class="btn btn-default">Unfollow</a>
{% endif %}
{% endif %}
<a href="{{ url_for('.followers', username=user.username) }}">
Followers: <span class="badge">{{ user.followers.count() - 1 }}</span>
</a>
<a href="{{ url_for('.followed_by', username=user.username) }}">
Following: <span class="badge">{{ user.followed.count() - 1 }}</span>
</a>
{% if current_user.is_authenticated and user != current_user and
user.is_following(current_user) %}
| <span class="label label-default">Follows you</span>
{% endif %}
<h3>Posts by {{ user.username }}</h3>
{% include '_posts.html' %}
{% endblock %}
...@@ -15,7 +15,20 @@ class Config: ...@@ -15,7 +15,20 @@ class Config:
""" """
配置基类 配置基类
""" """
pass SECRET_KEY = os.environ.get('SECRET_KEY') or 'hard to guess string'
MAIL_SERVER = os.environ.get('MAIL_SERVER', 'smtp.googlemail.com')
MAIL_PORT = int(os.environ.get('MAIL_PORT', '587'))
MAIL_USE_TLS = os.environ.get('MAIL_USE_TLS', 'true').lower() in \
['true', 'on', '1']
MAIL_USERNAME = os.environ.get('MAIL_USERNAME')
MAIL_PASSWORD = os.environ.get('MAIL_PASSWORD')
FLASKY_MAIL_SUBJECT_PREFIX = '[Flasky]'
FLASKY_MAIL_SENDER = 'Flasky Admin <flasky@example.com>'
FLASKY_ADMIN = os.environ.get('FLASKY_ADMIN')
SQLALCHEMY_TRACK_MODIFICATIONS = False
FLASKY_POSTS_PER_PAGE = 5
FLASKY_FOLLOWERS_PER_PAGE = 5
FLASKY_COMMENTS_PER_PAGE = 5
@staticmethod @staticmethod
def init_app(app): def init_app(app):
...@@ -27,6 +40,8 @@ class DevelopmentConfig(Config): ...@@ -27,6 +40,8 @@ class DevelopmentConfig(Config):
开发环境配置 开发环境配置
""" """
DEBUG = True DEBUG = True
SQLALCHEMY_DATABASE_URI = os.environ.get('DEV_DATABASE_URL') or \
'sqlite:///' + os.path.join(basedir, 'data-dev.sqlite')
class TestingConfig(Config): class TestingConfig(Config):
...@@ -34,13 +49,16 @@ class TestingConfig(Config): ...@@ -34,13 +49,16 @@ class TestingConfig(Config):
测试环境配置 测试环境配置
""" """
TESTING = True TESTING = True
SQLALCHEMY_DATABASE_URI = os.environ.get('TEST_DATABASE_URL') or \
'sqlite://'
class ProductionConfig(Config): class ProductionConfig(Config):
""" """
生产环境配置 生产环境配置
""" """
pass SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or \
'sqlite:///' + os.path.join(basedir, 'data.sqlite')
config = { config = {
......
...@@ -7,7 +7,17 @@ ...@@ -7,7 +7,17 @@
# Date: 2022/11/27 # Date: 2022/11/27
# ------------------------------------------------------------------------------- # -------------------------------------------------------------------------------
import os import os
import sys
from app import create_app import click
from app import create_app, db
from app.models import User, Role
from flask_migrate import Migrate
app = create_app(os.getenv('FLASK_CONFIG') or 'default') app = create_app(os.getenv('FLASK_CONFIG') or 'default')
migrate = Migrate(app, db)
@app.shell_context_processor
def make_shell_context():
return dict(db=db, User=User, Role=Role)
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册