Flask Blog
Corey M Schafer
[Link]
bcrypt==3.1.4
blinker==1.4
certifi==2016.2.28
cffi==1.11.5
click==6.7
Flask==1.0
Flask-Bcrypt==0.7.1
Flask-Login==0.4.1
Flask-Mail==0.9.1
Flask-SQLAlchemy==2.3.2
Flask-WTF==0.14.2
itsdangerous==0.24
Jinja2==2.10
MarkupSafe==1.0
Pillow==5.3.0
pycparser==2.18
six==1.11.0
SQLAlchemy==1.2.7
Werkzeug==0.14.1
WTForms==2.1
[Link]
from flaskblog import create_app
app = create_app()
if __name__ == '__main__':
[Link](debug=True)
folder: flaskblog
__init__.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_bcrypt import Bcrypt
from flask_login import LoginManager
from flask_mail import Mail
from [Link] import Config
db = SQLAlchemy()
bcrypt = Bcrypt()
login_manager = LoginManager()
login_manager.login_view = '[Link]'
login_manager.login_message_category = 'info'
mail = Mail()
def create_app(config_class=Config):
app = Flask(__name__)
[Link].from_object(Config)
db.init_app(app)
bcrypt.init_app(app)
login_manager.init_app(app)
mail.init_app(app)
from [Link] import users
from [Link] import posts
from [Link] import main
from [Link] import errors
app.register_blueprint(users)
app.register_blueprint(posts)
app.register_blueprint(main)
app.register_blueprint(errors)
return app
[Link]
import os
class Config:
SECRET_KEY = [Link]('SECRET_KEY')
SQLALCHEMY_DATABASE_URI = [Link]('SQLALCHEMY_DATABASE_URI')
MAIL_SERVER = '[Link]'
MAIL_PORT = 587
MAIL_USE_TLS = True
MAIL_USERNAME = [Link]('EMAIL_USER')
MAIL_PASSWORD = [Link]('EMAIL_PASS')
[Link]
from datetime import datetime
from itsdangerous import TimedJSONWebSignatureSerializer as Serializer
from flask import current_app
from flaskblog import db, login_manager
from flask_login import UserMixin
@login_manager.user_loader
def load_user(user_id):
return [Link](int(user_id))
class User([Link], UserMixin):
id = [Link]([Link], primary_key=True)
username = [Link]([Link](20), unique=True, nullable=False)
email = [Link]([Link](120), unique=True, nullable=False)
image_file = [Link]([Link](20), nullable=False, default='[Link]')
password = [Link]([Link](60), nullable=False)
posts = [Link]('Post', backref='author', lazy=True)
def get_reset_token(self, expires_sec=1800):
s = Serializer(current_app.config['SECRET_KEY'], expires_sec)
return [Link]({'user_id': [Link]}).decode('utf-8')
@staticmethod
def verify_reset_token(token):
s = Serializer(current_app.config['SECRET_KEY'])
try:
user_id = [Link](token)['user_id']
except:
return None
return [Link](user_id)
def __repr__(self):
return f"User('{[Link]}', '{[Link]}', '{self.image_file}')"
class Post([Link]):
id = [Link]([Link], primary_key=True)
title = [Link]([Link](100), nullable=False)
date_posted = [Link]([Link], nullable=False, default=[Link])
content = [Link]([Link], nullable=False)
user_id = [Link]([Link], [Link]('[Link]'), nullable=False)
def __repr__(self):
return f"Post('{[Link]}', '{self.date_posted}')"
folder: flaskblog/errors
__init__.py
[Link]
from flask import Blueprint, render_template
errors = Blueprint('errors', __name__)
@errors.app_errorhandler(404)
def error_404(error):
return render_template('errors/[Link]'), 404
@errors.app_errorhandler(403)
def error_403(error):
return render_template('errors/[Link]'), 403
@errors.app_errorhandler(500)
def error_500(error):
return render_template('errors/[Link]'), 500
folder: flaskblog/main
__init__.py
[Link]
from flask import render_template, request, Blueprint
from [Link] import Post
main = Blueprint('main', __name__)
@[Link]("/")
@[Link]("/home")
def home():
page = [Link]('page', 1, type=int)
posts = [Link].order_by(Post.date_posted.desc()).paginate(page=page, per_page=5)
return render_template('[Link]', posts=posts)
@[Link]("/about")
def about():
return render_template('[Link]', title='About')
folder: flaskblog/posts
__init__.py
[Link]
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField, TextAreaField
from [Link] import DataRequired
class PostForm(FlaskForm):
title = StringField('Title', validators=[DataRequired()])
content = TextAreaField('Content', validators=[DataRequired()])
submit = SubmitField('Post')
[Link]
from flask import (render_template, url_for, flash,
redirect, request, abort, Blueprint)
from flask_login import current_user, login_required
from flaskblog import db
from [Link] import Post
from [Link] import PostForm
posts = Blueprint('posts', __name__)
@[Link]("/post/new", methods=['GET', 'POST'])
@login_required
def new_post():
form = PostForm()
if form.validate_on_submit():
post = Post(title=[Link], content=[Link], author=current_user)
[Link](post)
[Link]()
flash('Your post has been created!', 'success')
return redirect(url_for('[Link]'))
return render_template('create_post.html', title='New Post',
form=form, legend='New Post')
@[Link]("/post/<int:post_id>")
def post(post_id):
post = [Link].get_or_404(post_id)
return render_template('[Link]', title=[Link], post=post)
@[Link]("/post/<int:post_id>/update", methods=['GET', 'POST'])
@login_required
def update_post(post_id):
post = [Link].get_or_404(post_id)
if [Link] != current_user:
abort(403)
form = PostForm()
if form.validate_on_submit():
[Link] = [Link]
[Link] = [Link]
[Link]()
flash('Your post has been updated!', 'success')
return redirect(url_for('[Link]', post_id=[Link]))
elif [Link] == 'GET':
[Link] = [Link]
[Link] = [Link]
return render_template('create_post.html', title='Update Post',
form=form, legend='Update Post')
@[Link]("/post/<int:post_id>/delete", methods=['POST'])
@login_required
def delete_post(post_id):
post = [Link].get_or_404(post_id)
if [Link] != current_user:
abort(403)
[Link](post)
[Link]()
flash('Your post has been deleted!', 'success')
return redirect(url_for('[Link]'))
folder: flaskblog/static
[Link]
body {
background: #fafafa;
color: #333333;
margin-top: 5rem;
}
h1, h2, h3, h4, h5, h6 {
color: #444444;
}
.bg-steel {
background-color: #5f788a;
}
.site-header .navbar-nav .nav-link {
color: #cbd5db;
}
.site-header .navbar-nav .nav-link:hover {
color: #ffffff;
}
.site-header .navbar-nav .[Link] {
font-weight: 500;
}
.content-section {
background: #ffffff;
padding: 10px 20px;
border: 1px solid #dddddd;
border-radius: 3px;
margin-bottom: 20px;
}
.article-title {
color: #444444;
}
[Link]-title:hover {
color: #428bca;
text-decoration: none;
}
.article-content {
white-space: pre-line;
}
.article-img {
height: 65px;
width: 65px;
margin-right: 16px;
}
.article-metadata {
padding-bottom: 1px;
margin-bottom: 4px;
border-bottom: 1px solid #e3e3e3
}
.article-metadata a:hover {
color: #333;
text-decoration: none;
}
.article-svg {
width: 25px;
height: 25px;
vertical-align: middle;
}
.account-img {
height: 125px;
width: 125px;
margin-right: 20px;
margin-bottom: 16px;
}
.account-heading {
font-size: 2.5rem;
}
folder: flaskblog/users
__init__.py
[Link]
from flask_wtf import FlaskForm
from flask_wtf.file import FileField, FileAllowed
from wtforms import StringField, PasswordField, SubmitField, BooleanField
from [Link] import DataRequired, Length, Email, EqualTo, ValidationError
from flask_login import current_user
from [Link] import User
class RegistrationForm(FlaskForm):
username = StringField('Username',
validators=[DataRequired(), Length(min=2, max=20)])
email = StringField('Email',
validators=[DataRequired(), Email()])
password = PasswordField('Password', validators=[DataRequired()])
confirm_password = PasswordField('Confirm Password',
validators=[DataRequired(), EqualTo('password')])
submit = SubmitField('Sign Up')
def validate_username(self, username):
user = [Link].filter_by(username=[Link]).first()
if user:
raise ValidationError('That username is taken. Please choose a different one.')
def validate_email(self, email):
user = [Link].filter_by(email=[Link]).first()
if user:
raise ValidationError('That email is taken. Please choose a different one.')
class LoginForm(FlaskForm):
email = StringField('Email',
validators=[DataRequired(), Email()])
password = PasswordField('Password', validators=[DataRequired()])
remember = BooleanField('Remember Me')
submit = SubmitField('Login')
class UpdateAccountForm(FlaskForm):
username = StringField('Username',
validators=[DataRequired(), Length(min=2, max=20)])
email = StringField('Email',
validators=[DataRequired(), Email()])
picture = FileField('Update Profile Picture', validators=[FileAllowed(['jpg', 'png'])])
submit = SubmitField('Update')
def validate_username(self, username):
if [Link] != current_user.username:
user = [Link].filter_by(username=[Link]).first()
if user:
raise ValidationError('That username is taken. Please choose a different one.')
def validate_email(self, email):
if [Link] != current_user.email:
user = [Link].filter_by(email=[Link]).first()
if user:
raise ValidationError('That email is taken. Please choose a different one.')
class RequestResetForm(FlaskForm):
email = StringField('Email',
validators=[DataRequired(), Email()])
submit = SubmitField('Request Password Reset')
def validate_email(self, email):
user = [Link].filter_by(email=[Link]).first()
if user is None:
raise ValidationError('There is no account with that email. You must register first.')
class ResetPasswordForm(FlaskForm):
password = PasswordField('Password', validators=[DataRequired()])
confirm_password = PasswordField('Confirm Password',
validators=[DataRequired(), EqualTo('password')])
submit = SubmitField('Reset Password')
[Link]
from flask import render_template, url_for, flash, redirect, request, Blueprint
from flask_login import login_user, current_user, logout_user, login_required
from flaskblog import db, bcrypt
from [Link] import User, Post
from [Link] import (RegistrationForm, LoginForm, UpdateAccountForm,
RequestResetForm, ResetPasswordForm)
from [Link] import save_picture, send_reset_email
users = Blueprint('users', __name__)
@[Link]("/register", methods=['GET', 'POST'])
def register():
if current_user.is_authenticated:
return redirect(url_for('[Link]'))
form = RegistrationForm()
if form.validate_on_submit():
hashed_password = bcrypt.generate_password_hash([Link]).decode('utf-8')
user = User(username=[Link], email=[Link],
password=hashed_password)
[Link](user)
[Link]()
flash('Your account has been created! You are now able to log in', 'success')
return redirect(url_for('[Link]'))
return render_template('[Link]', title='Register', form=form)
@[Link]("/login", methods=['GET', 'POST'])
def login():
if current_user.is_authenticated:
return redirect(url_for('[Link]'))
form = LoginForm()
if form.validate_on_submit():
user = [Link].filter_by(email=[Link]).first()
if user and bcrypt.check_password_hash([Link], [Link]):
login_user(user, remember=[Link])
next_page = [Link]('next')
return redirect(next_page) if next_page else redirect(url_for('[Link]'))
else:
flash('Login Unsuccessful. Please check email and password', 'danger')
return render_template('[Link]', title='Login', form=form)
@[Link]("/logout")
def logout():
logout_user()
return redirect(url_for('[Link]'))
@[Link]("/account", methods=['GET', 'POST'])
@login_required
def account():
form = UpdateAccountForm()
if form.validate_on_submit():
if [Link]:
picture_file = save_picture([Link])
current_user.image_file = picture_file
current_user.username = [Link]
current_user.email = [Link]
[Link]()
flash('Your account has been updated!', 'success')
return redirect(url_for('[Link]'))
elif [Link] == 'GET':
[Link] = current_user.username
[Link] = current_user.email
image_file = url_for('static', filename='profile_pics/' + current_user.image_file)
return render_template('[Link]', title='Account',
image_file=image_file, form=form)
@[Link]("/user/<string:username>")
def user_posts(username):
page = [Link]('page', 1, type=int)
user = [Link].filter_by(username=username).first_or_404()
posts = [Link].filter_by(author=user)\
.order_by(Post.date_posted.desc())\
.paginate(page=page, per_page=5)
return render_template('user_posts.html', posts=posts, user=user)
@[Link]("/reset_password", methods=['GET', 'POST'])
def reset_request():
if current_user.is_authenticated:
return redirect(url_for('[Link]'))
form = RequestResetForm()
if form.validate_on_submit():
user = [Link].filter_by(email=[Link]).first()
send_reset_email(user)
flash('An email has been sent with instructions to reset your password.', 'info')
return redirect(url_for('[Link]'))
return render_template('reset_request.html', title='Reset Password', form=form)
@[Link]("/reset_password/<token>", methods=['GET', 'POST'])
def reset_token(token):
if current_user.is_authenticated:
return redirect(url_for('[Link]'))
user = User.verify_reset_token(token)
if user is None:
flash('That is an invalid or expired token', 'warning')
return redirect(url_for('users.reset_request'))
form = ResetPasswordForm()
if form.validate_on_submit():
hashed_password = bcrypt.generate_password_hash([Link]).decode('utf-8')
[Link] = hashed_password
[Link]()
flash('Your password has been updated! You are now able to log in', 'success')
return redirect(url_for('[Link]'))
return render_template('reset_token.html', title='Reset Password', form=form)
[Link]
import os
import secrets
from PIL import Image
from flask import url_for, current_app
from flask_mail import Message
from flaskblog import mail
def save_picture(form_picture):
random_hex = secrets.token_hex(8)
_, f_ext = [Link](form_picture.filename)
picture_fn = random_hex + f_ext
picture_path = [Link](current_app.root_path, 'static/profile_pics', picture_fn)
output_size = (125, 125)
i = [Link](form_picture)
[Link](output_size)
[Link](picture_path)
return picture_fn
def send_reset_email(user):
token = user.get_reset_token()
msg = Message('Password Reset Request',
sender='noreply@[Link]',
recipients=[[Link]])
[Link] = f'''To reset your password, visit the following link:
{url_for('users.reset_token', token=token, _external=True)}
If you did not make this request then simply ignore this email and no changes will be made.
'''
[Link](msg)
folder: flaskblog/templates
[Link]
{% extends "[Link]" %}
{% block content %}
<h1>About Page</h1>
{% endblock content %}
[Link]
{% extends "[Link]" %}
{% block content %}
<div class="content-section">
<div class="media">
<img class="rounded-circle account-img" src="{{ image_file }}">
<div class="media-body">
<h2 class="account-heading">{{ current_user.username }}</h2>
<p class="text-secondary">{{ current_user.email }}</p>
</div>
</div>
<form method="POST" action="" enctype="multipart/form-data">
{{ form.hidden_tag() }}
<fieldset class="form-group">
<legend class="border-bottom mb-4">Account Info</legend>
<div class="form-group">
{{ [Link](class="form-control-label") }}
{% if [Link] %}
{{ [Link](class="form-control form-control-lg is-invalid") }}
<div class="invalid-feedback">
{% for error in [Link] %}
<span>{{ error }}</span>
{% endfor %}
</div>
{% else %}
{{ [Link](class="form-control form-control-lg") }}
{% endif %}
</div>
<div class="form-group">
{{ [Link](class="form-control-label") }}
{% if [Link] %}
{{ [Link](class="form-control form-control-lg is-invalid") }}
<div class="invalid-feedback">
{% for error in [Link] %}
<span>{{ error }}</span>
{% endfor %}
</div>
{% else %}
{{ [Link](class="form-control form-control-lg") }}
{% endif %}
</div>
<div class="form-group">
{{ [Link]() }}
{{ [Link](class="form-control-file") }}
{% if [Link] %}
{% for error in [Link] %}
<span class="text-danger">{{ error }}</span></br>
{% endfor %}
{% endif %}
</div>
</fieldset>
<div class="form-group">
{{ [Link](class="btn btn-outline-info") }}
</div>
</form>
</div>
{% endblock content %}
create_post.html
{% extends "[Link]" %}
{% block content %}
<div class="content-section">
<form method="POST" action="">
{{ form.hidden_tag() }}
<fieldset class="form-group">
<legend class="border-bottom mb-4">{{ legend }}</legend>
<div class="form-group">
{{ [Link](class="form-control-label") }}
{% if [Link] %}
{{ [Link](class="form-control form-control-lg is-invalid") }}
<div class="invalid-feedback">
{% for error in [Link] %}
<span>{{ error }}</span>
{% endfor %}
</div>
{% else %}
{{ [Link](class="form-control form-control-lg") }}
{% endif %}
</div>
<div class="form-group">
{{ [Link](class="form-control-label") }}
{% if [Link] %}
{{ [Link](class="form-control form-control-lg is-invalid") }}
<div class="invalid-feedback">
{% for error in [Link] %}
<span>{{ error }}</span>
{% endfor %}
</div>
{% else %}
{{ [Link](class="form-control form-control-lg") }}
{% endif %}
</div>
</fieldset>
<div class="form-group">
{{ [Link](class="btn btn-outline-info") }}
</div>
</form>
</div>
{% endblock content %}
[Link]
{% extends "[Link]" %}
{% block content %}
{% for post in [Link] %}
<article class="media content-section">
<img class="rounded-circle article-img" src="{{ url_for('static', filename='profile_pics/' +
[Link].image_file) }}">
<div class="media-body">
<div class="article-metadata">
<a class="mr-2" href="{{ url_for('users.user_posts',
username=[Link]) }}">{{ [Link] }}</a>
<small class="text-muted">{{ post.date_posted.strftime('%Y-%m-%d') }}</small>
</div>
<h2><a class="article-title" href="{{ url_for('[Link]', post_id=[Link]) }}">{{ [Link]
}}</a></h2>
<p class="article-content">{{ [Link] }}</p>
</div>
</article>
{% endfor %}
{% for page_num in posts.iter_pages(left_edge=1, right_edge=1, left_current=1,
right_current=2) %}
{% if page_num %}
{% if [Link] == page_num %}
<a class="btn btn-info mb-4" href="{{ url_for('[Link]',
page=page_num) }}">{{ page_num }}</a>
{% else %}
<a class="btn btn-outline-info mb-4" href="{{ url_for('[Link]',
page=page_num) }}">{{ page_num }}</a>
{% endif %}
{% else %}
...
{% endif %}
{% endfor %}
{% endblock content %}
[Link]
<!DOCTYPE html>
<html>
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!-- Bootstrap CSS -->
<link rel="stylesheet"
href="[Link] integrity="sha384-
Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm"
crossorigin="anonymous">
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='[Link]') }}">
{% if title %}
<title>Flask Blog - {{ title }}</title>
{% else %}
<title>Flask Blog</title>
{% endif %}
</head>
<body>
<header class="site-header">
<nav class="navbar navbar-expand-md navbar-dark bg-steel fixed-top">
<div class="container">
<a class="navbar-brand mr-4" href="/">Flask Blog</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-
target="#navbarToggle" aria-controls="navbarToggle" aria-expanded="false" aria-label="Toggle
navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarToggle">
<div class="navbar-nav mr-auto">
<a class="nav-item nav-link" href="{{ url_for('[Link]') }}">Home</a>
<a class="nav-item nav-link" href="{{ url_for('[Link]') }}">About</a>
</div>
<!-- Navbar Right Side -->
<div class="navbar-nav">
{% if current_user.is_authenticated %}
<a class="nav-item nav-link" href="{{ url_for('posts.new_post') }}">New Post</a>
<a class="nav-item nav-link" href="{{ url_for('[Link]') }}">Account</a>
<a class="nav-item nav-link" href="{{ url_for('[Link]') }}">Logout</a>
{% else %}
<a class="nav-item nav-link" href="{{ url_for('[Link]') }}">Login</a>
<a class="nav-item nav-link" href="{{ url_for('[Link]') }}">Register</a>
{% endif %}
</div>
</div>
</div>
</nav>
</header>
<main role="main" class="container">
<div class="row">
<div class="col-md-8">
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
{% for category, message in messages %}
<div class="alert alert-{{ category }}">
{{ message }}
</div>
{% endfor %}
{% endif %}
{% endwith %}
{% block content %}{% endblock %}
</div>
<div class="col-md-4">
<div class="content-section">
<h3>Our Sidebar</h3>
<p class='text-muted'>You can put any information here you'd like.
<ul class="list-group">
<li class="list-group-item list-group-item-light">Latest Posts</li>
<li class="list-group-item list-group-item-light">Announcements</li>
<li class="list-group-item list-group-item-light">Calendars</li>
<li class="list-group-item list-group-item-light">etc</li>
</ul>
</p>
</div>
</div>
</div>
</main>
<!-- Optional JavaScript -->
<!-- jQuery first, then [Link], then Bootstrap JS -->
<script src="[Link] integrity="sha384-
KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN"
crossorigin="anonymous"></script>
<script src="[Link]
integrity="sha384-
ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q"
crossorigin="anonymous"></script>
<script src="[Link]
integrity="sha384-
JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl"
crossorigin="anonymous"></script>
</body>
</html>
[Link]
{% extends "[Link]" %}
{% block content %}
<div class="content-section">
<form method="POST" action="">
{{ form.hidden_tag() }}
<fieldset class="form-group">
<legend class="border-bottom mb-4">Log In</legend>
<div class="form-group">
{{ [Link](class="form-control-label") }}
{% if [Link] %}
{{ [Link](class="form-control form-control-lg is-invalid") }}
<div class="invalid-feedback">
{% for error in [Link] %}
<span>{{ error }}</span>
{% endfor %}
</div>
{% else %}
{{ [Link](class="form-control form-control-lg") }}
{% endif %}
</div>
<div class="form-group">
{{ [Link](class="form-control-label") }}
{% if [Link] %}
{{ [Link](class="form-control form-control-lg is-invalid") }}
<div class="invalid-feedback">
{% for error in [Link] %}
<span>{{ error }}</span>
{% endfor %}
</div>
{% else %}
{{ [Link](class="form-control form-control-lg") }}
{% endif %}
</div>
<div class="form-check">
{{ [Link](class="form-check-input") }}
{{ [Link](class="form-check-label") }}
</div>
</fieldset>
<div class="form-group">
{{ [Link](class="btn btn-outline-info") }}
<small class="text-muted ml-2">
<a href="{{ url_for('users.reset_request') }}">Forgot Password?</a>
</small>
</div>
</form>
</div>
<div class="border-top pt-3">
<small class="text-muted">
Need An Account? <a class="ml-2" href="{{ url_for('[Link]') }}">Sign Up
Now</a>
</small>
</div>
{% endblock content %}
[Link]
{% extends "[Link]" %}
{% block content %}
<article class="media content-section">
<img class="rounded-circle article-img" src="{{ url_for('static', filename='profile_pics/' +
[Link].image_file) }}">
<div class="media-body">
<div class="article-metadata">
<a class="mr-2" href="{{ url_for('users.user_posts',
username=[Link]) }}">{{ [Link] }}</a>
<small class="text-muted">{{ post.date_posted.strftime('%Y-%m-%d') }}</small>
{% if [Link] == current_user %}
<div>
<a class="btn btn-secondary btn-sm mt-1 mb-1" href="{{ url_for('posts.update_post',
post_id=[Link]) }}">Update</a>
<button type="button" class="btn btn-danger btn-sm m-1" data-toggle="modal" data-
target="#deleteModal">Delete</button>
</div>
{% endif %}
</div>
<h2 class="article-title">{{ [Link] }}</h2>
<p class="article-content">{{ [Link] }}</p>
</div>
</article>
<!-- Modal -->
<div class="modal fade" id="deleteModal" tabindex="-1" role="dialog" aria-
labelledby="deleteModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="deleteModalLabel">Delete Post?</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<form action="{{ url_for('posts.delete_post', post_id=[Link]) }}" method="POST">
<input class="btn btn-danger" type="submit" value="Delete">
</form>
</div>
</div>
</div>
</div>
{% endblock content %}
[Link]
{% extends "[Link]" %}
{% block content %}
<div class="content-section">
<form method="POST" action="">
{{ form.hidden_tag() }}
<fieldset class="form-group">
<legend class="border-bottom mb-4">Join Today</legend>
<div class="form-group">
{{ [Link](class="form-control-label") }}
{% if [Link] %}
{{ [Link](class="form-control form-control-lg is-invalid") }}
<div class="invalid-feedback">
{% for error in [Link] %}
<span>{{ error }}</span>
{% endfor %}
</div>
{% else %}
{{ [Link](class="form-control form-control-lg") }}
{% endif %}
</div>
<div class="form-group">
{{ [Link](class="form-control-label") }}
{% if [Link] %}
{{ [Link](class="form-control form-control-lg is-invalid") }}
<div class="invalid-feedback">
{% for error in [Link] %}
<span>{{ error }}</span>
{% endfor %}
</div>
{% else %}
{{ [Link](class="form-control form-control-lg") }}
{% endif %}
</div>
<div class="form-group">
{{ [Link](class="form-control-label") }}
{% if [Link] %}
{{ [Link](class="form-control form-control-lg is-invalid") }}
<div class="invalid-feedback">
{% for error in [Link] %}
<span>{{ error }}</span>
{% endfor %}
</div>
{% else %}
{{ [Link](class="form-control form-control-lg") }}
{% endif %}
</div>
<div class="form-group">
{{ form.confirm_password.label(class="form-control-label") }}
{% if form.confirm_password.errors %}
{{ form.confirm_password(class="form-control form-control-lg is-invalid") }}
<div class="invalid-feedback">
{% for error in form.confirm_password.errors %}
<span>{{ error }}</span>
{% endfor %}
</div>
{% else %}
{{ form.confirm_password(class="form-control form-control-lg") }}
{% endif %}
</div>
</fieldset>
<div class="form-group">
{{ [Link](class="btn btn-outline-info") }}
</div>
</form>
</div>
<div class="border-top pt-3">
<small class="text-muted">
Already Have An Account? <a class="ml-2" href="{{ url_for('[Link]') }}">Sign
In</a>
</small>
</div>
{% endblock content %}
reset_request.html
{% extends "[Link]" %}
{% block content %}
<div class="content-section">
<form method="POST" action="">
{{ form.hidden_tag() }}
<fieldset class="form-group">
<legend class="border-bottom mb-4">Reset Password</legend>
<div class="form-group">
{{ [Link](class="form-control-label") }}
{% if [Link] %}
{{ [Link](class="form-control form-control-lg is-invalid") }}
<div class="invalid-feedback">
{% for error in [Link] %}
<span>{{ error }}</span>
{% endfor %}
</div>
{% else %}
{{ [Link](class="form-control form-control-lg") }}
{% endif %}
</div>
</fieldset>
<div class="form-group">
{{ [Link](class="btn btn-outline-info") }}
</div>
</form>
</div>
{% endblock content %}
reset_token.html
{% extends "[Link]" %}
{% block content %}
<div class="content-section">
<form method="POST" action="">
{{ form.hidden_tag() }}
<fieldset class="form-group">
<legend class="border-bottom mb-4">Reset Password</legend>
<div class="form-group">
{{ [Link](class="form-control-label") }}
{% if [Link] %}
{{ [Link](class="form-control form-control-lg is-invalid") }}
<div class="invalid-feedback">
{% for error in [Link] %}
<span>{{ error }}</span>
{% endfor %}
</div>
{% else %}
{{ [Link](class="form-control form-control-lg") }}
{% endif %}
</div>
<div class="form-group">
{{ form.confirm_password.label(class="form-control-label") }}
{% if form.confirm_password.errors %}
{{ form.confirm_password(class="form-control form-control-lg is-invalid") }}
<div class="invalid-feedback">
{% for error in form.confirm_password.errors %}
<span>{{ error }}</span>
{% endfor %}
</div>
{% else %}
{{ form.confirm_password(class="form-control form-control-lg") }}
{% endif %}
</div>
</fieldset>
<div class="form-group">
{{ [Link](class="btn btn-outline-info") }}
</div>
</form>
</div>
{% endblock content %}
user_posts.html
{% extends "[Link]" %}
{% block content %}
<h1 class="mb-3">Posts by {{ [Link] }} ({{ [Link] }})</h1>
{% for post in [Link] %}
<article class="media content-section">
<img class="rounded-circle article-img" src="{{ url_for('static', filename='profile_pics/' +
[Link].image_file) }}">
<div class="media-body">
<div class="article-metadata">
<a class="mr-2" href="{{ url_for('users.user_posts',
username=[Link]) }}">{{ [Link] }}</a>
<small class="text-muted">{{ post.date_posted.strftime('%Y-%m-%d') }}</small>
</div>
<h2><a class="article-title" href="{{ url_for('[Link]', post_id=[Link]) }}">{{ [Link]
}}</a></h2>
<p class="article-content">{{ [Link] }}</p>
</div>
</article>
{% endfor %}
{% for page_num in posts.iter_pages(left_edge=1, right_edge=1, left_current=1,
right_current=2) %}
{% if page_num %}
{% if [Link] == page_num %}
<a class="btn btn-info mb-4" href="{{ url_for('users.user_posts', username=[Link],
page=page_num) }}">{{ page_num }}</a>
{% else %}
<a class="btn btn-outline-info mb-4" href="{{ url_for('users.user_posts',
username=[Link], page=page_num) }}">{{ page_num }}</a>
{% endif %}
{% else %}
...
{% endif %}
{% endfor %}
{% endblock content %}
folder: flaskblog/templates/errors
[Link]
{% extends "[Link]" %}
{% block content %}
<div class="content-section">
<h1>You don't have permission to do that (403)</h1>
<p>Please check your account and try again</p>
</div>
{% endblock content %}
[Link]
{% extends "[Link]" %}
{% block content %}
<div class="content-section">
<h1>Oops. Page Not Found (404)</h1>
<p>That page does not exist. Please try a different location</p>
</div>
{% endblock content %}
[Link]
{% extends "[Link]" %}
{% block content %}
<div class="content-section">
<h1>Something went wrong (500)</h1>
<p>We're experiencing some trouble on our end. Please try again in the near future</p>
</div>
{% endblock content %}
other
[Link]
server {
listen 80;
server_name YOUR_IP_OR_DOMAIN;
location /static {
alias /home/YOUR_USER/YOUR_PROJECT/flaskblog/static;
}
location / {
proxy_pass [Link]
include /etc/nginx/proxy_params;
proxy_redirect off;
}
}
[Link]
sudo apt-get update
sudo apt-get install software-properties-common
sudo add-apt-repository universe
sudo add-apt-repository ppa:certbot/certbot
sudo apt-get update
sudo apt-get install python-certbot-nginx
sudo certbot --nginx
sudo certbot renew --dry-run