Skip to content

Commit 3f36e61

Browse files
committed
Initial commit
0 parents  commit 3f36e61

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+1014
-0
lines changed

Diff for: flaskblog/__init__.py

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
from flask import Flask
2+
from flask_sqlalchemy import SQLAlchemy
3+
from flask_bcrypt import Bcrypt
4+
from flask_login import LoginManager
5+
from flask_mail import Mail
6+
from flaskblog.config import Config
7+
8+
9+
db = SQLAlchemy()
10+
bcrypt = Bcrypt()
11+
login_manager = LoginManager()
12+
login_manager.login_view = 'users.login'
13+
login_manager.login_message_category = 'info'
14+
15+
mail = Mail()
16+
17+
def create_app(config_class=Config):
18+
app = Flask(__name__)
19+
app.config.from_object(Config)
20+
21+
db.init_app(app)
22+
bcrypt.init_app(app)
23+
login_manager.init_app(app)
24+
mail.init_app(app)
25+
26+
from flaskblog.users.routes import users
27+
from flaskblog.posts.routes import posts
28+
from flaskblog.main.routes import main
29+
from flaskblog.errors.handlers import errors
30+
31+
app.register_blueprint(users)
32+
app.register_blueprint(posts)
33+
app.register_blueprint(main)
34+
app.register_blueprint(errors)
35+
36+
return app

Diff for: flaskblog/__pycache__/__init__.cpython-312.pyc

1.59 KB
Binary file not shown.

Diff for: flaskblog/__pycache__/config.cpython-312.pyc

729 Bytes
Binary file not shown.

Diff for: flaskblog/__pycache__/forms.cpython-312.pyc

5.32 KB
Binary file not shown.

Diff for: flaskblog/__pycache__/models.cpython-312.pyc

3.78 KB
Binary file not shown.

Diff for: flaskblog/__pycache__/routes.cpython-312.pyc

13.5 KB
Binary file not shown.

Diff for: flaskblog/config.py

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import os
2+
3+
4+
class Config:
5+
SECRET_KEY = os.environ.get('SECRET_KEY')
6+
SQLALCHEMY_DATABASE_URI = 'sqlite:///site.db'
7+
MAIL_SERVER = 'smtp.googlemail.com'
8+
MAIL_PORT = 587
9+
MAIL_USE_TLS = True
10+
MAIL_USERNAME = os.environ.get('EMAIL_USER')
11+
MAIL_PASSWORD = 'nmon ndgb alia uacu'

Diff for: flaskblog/errors/__init__.py

Whitespace-only changes.
155 Bytes
Binary file not shown.
972 Bytes
Binary file not shown.

Diff for: flaskblog/errors/handlers.py

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
from flask import Blueprint, render_template
2+
3+
4+
errors = Blueprint('errors', __name__)
5+
6+
7+
@errors.app_errorhandler(404)
8+
def error_404(error):
9+
return render_template('errors/404.html'), 404
10+
11+
12+
@errors.app_errorhandler(403)
13+
def error_403(error):
14+
return render_template('errors/403.html'), 403
15+
16+
17+
@errors.app_errorhandler(500)
18+
def error_500(error):
19+
return render_template('errors/500.html'), 500

Diff for: flaskblog/main/__init__.py

Whitespace-only changes.

Diff for: flaskblog/main/__pycache__/__init__.cpython-312.pyc

153 Bytes
Binary file not shown.

Diff for: flaskblog/main/__pycache__/routes.cpython-312.pyc

1.27 KB
Binary file not shown.

Diff for: flaskblog/main/routes.py

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
from flask import render_template, request, Blueprint
2+
from flaskblog.models import Post
3+
4+
5+
main = Blueprint('main', __name__)
6+
7+
@main.route("/")
8+
@main.route("/home")
9+
def home():
10+
page = request.args.get('page', 1, type=int)
11+
posts = Post.query.order_by(Post.date_posted.desc()).paginate(page=page, per_page=5)
12+
return render_template('home.html', posts = posts)
13+
14+
@main.route("/about")
15+
def about():
16+
return render_template('about.html', title = 'About')

Diff for: flaskblog/models.py

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
from datetime import datetime
2+
from itsdangerous import TimedJSONWebSignatureSerializer as Serializer
3+
from flask import current_app
4+
from flaskblog import db, login_manager
5+
from flask_login import UserMixin
6+
7+
@login_manager.user_loader
8+
def load_user(user_id):
9+
return User.query.get(int(user_id))
10+
11+
12+
class User(db.Model, UserMixin):
13+
id = db.Column(db.Integer, primary_key=True)
14+
username = db.Column(db.String(20), unique=True, nullable=False)
15+
email = db.Column(db.String(120), unique=True, nullable=False)
16+
image_file = db.Column(db.String(20), nullable=False, default='default.jpg')
17+
password = db.Column(db.String(60), nullable=False)
18+
posts = db.relationship('Post', backref='author', lazy=True)
19+
20+
def get_reset_token(self, expires_sec=1800):
21+
s = Serializer(current_app.config['SECRET_KEY'], expires_sec)
22+
return s.dumps({'user_id': self.id}).decode('utf-8')
23+
24+
25+
@staticmethod
26+
def verify_reset_token(token):
27+
s = Serializer(current_app.config['SECRET_KEY'])
28+
try:
29+
user_id = s.loads(token)['user_id']
30+
except:
31+
return None
32+
return User.query.get(user_id)
33+
34+
35+
def __repr__(self):
36+
return f"User('{self.username}', '{self.email}', '{self.image_file}')"
37+
38+
39+
class Post(db.Model):
40+
id = db.Column(db.Integer, primary_key=True)
41+
title = db.Column(db.String(100), nullable=False)
42+
date_posted = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
43+
content = db.Column(db.Text, nullable=False)
44+
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
45+
46+
def __repr__(self):
47+
return f"Post('{self.title}', '{self.date_posted}')"

Diff for: flaskblog/posts/__init__.py

Whitespace-only changes.

Diff for: flaskblog/posts/__pycache__/__init__.cpython-312.pyc

154 Bytes
Binary file not shown.

Diff for: flaskblog/posts/__pycache__/forms.cpython-312.pyc

724 Bytes
Binary file not shown.

Diff for: flaskblog/posts/__pycache__/routes.cpython-312.pyc

3.86 KB
Binary file not shown.

Diff for: flaskblog/posts/forms.py

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
from flask_wtf import FlaskForm
2+
from wtforms import StringField, SubmitField, TextAreaField
3+
from wtforms.validators import DataRequired
4+
5+
class PostForm(FlaskForm):
6+
title = StringField('Title', validators=[DataRequired()])
7+
content = TextAreaField('Content', validators=[DataRequired()])
8+
submit = SubmitField('Post')

Diff for: flaskblog/posts/routes.py

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
from flask import (render_template, url_for, flash,
2+
redirect, request, abort, Blueprint)
3+
from flask_login import current_user, login_required
4+
from flaskblog import db
5+
from flaskblog.models import Post
6+
from flaskblog.posts.forms import PostForm
7+
8+
posts = Blueprint('posts', __name__)
9+
10+
11+
@posts.route("/post/new", methods = ['GET', 'POST'])
12+
@login_required
13+
def new_post():
14+
form = PostForm()
15+
if form.validate_on_submit():
16+
post = Post(title=form.title.data, content=form.content.data, author=current_user)
17+
db.session.add(post)
18+
db.session.commit()
19+
flash('Your post has been created!', 'success')
20+
return redirect(url_for('main.home'))
21+
return render_template('create_post.html', title='New Post', form=form, legend='New Post')
22+
23+
24+
@posts.route("/post/<int:post_id>")
25+
def post(post_id):
26+
post = Post.query.get_or_404(post_id)
27+
return render_template('post.html', title=post.title, post=post)
28+
29+
30+
@posts.route("/post/<int:post_id>/update", methods = ['GET', 'POST'])
31+
@login_required
32+
def update_post(post_id):
33+
post = Post.query.get_or_404(post_id)
34+
if post.author != current_user:
35+
abort(403)
36+
form = PostForm()
37+
if form.validate_on_submit():
38+
post.title = form.title.data
39+
post.content = form.content.data
40+
db.session.commit()
41+
flash('Your post has been updated!', 'success')
42+
return redirect(url_for('posts.post', post_id=post.id))
43+
elif request.method == 'GET':
44+
form.title.data = post.title
45+
form.content.data = post.content
46+
return render_template('create_post.html', title='Update Post', form=form, legend='Update Post')
47+
48+
49+
@posts.route("/post/<int:post_id>/delete", methods = ['POST'])
50+
@login_required
51+
def delete_post(post_id):
52+
post = Post.query.get_or_404(post_id)
53+
if post.author != current_user:
54+
abort(403)
55+
db.session.delete(post)
56+
db.session.commit()
57+
flash('Your post has been deleted!', 'success')
58+
return redirect(url_for('main.home'))

Diff for: flaskblog/static/main.css

+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
body {
2+
background: #fafafa;
3+
color: #333333;
4+
margin-top: 5rem;
5+
}
6+
7+
h1, h2, h3, h4, h5, h6 {
8+
color: #444444;
9+
}
10+
11+
.bg-steel {
12+
background-color: #5f788a;
13+
}
14+
15+
.site-header .navbar-nav .nav-link {
16+
color: #cbd5db;
17+
}
18+
19+
.site-header .navbar-nav .nav-link:hover {
20+
color: #ffffff;
21+
}
22+
23+
.site-header .navbar-nav .nav-link.active {
24+
font-weight: 500;
25+
}
26+
27+
.content-section {
28+
background: #ffffff;
29+
padding: 10px 20px;
30+
border: 1px solid #dddddd;
31+
border-radius: 3px;
32+
margin-bottom: 20px;
33+
}
34+
35+
.article-title {
36+
color: #444444;
37+
}
38+
39+
a.article-title:hover {
40+
color: #428bca;
41+
text-decoration: none;
42+
}
43+
44+
.article-content {
45+
white-space: pre-line;
46+
}
47+
48+
.article-img {
49+
height: 65px;
50+
width: 65px;
51+
margin-right: 16px;
52+
}
53+
54+
.article-metadata {
55+
padding-bottom: 1px;
56+
margin-bottom: 4px;
57+
border-bottom: 1px solid #e3e3e3
58+
}
59+
60+
.article-metadata a:hover {
61+
color: #333;
62+
text-decoration: none;
63+
}
64+
65+
.article-svg {
66+
width: 25px;
67+
height: 25px;
68+
vertical-align: middle;
69+
}
70+
71+
.account-img {
72+
height: 125px;
73+
width: 125px;
74+
margin-right: 20px;
75+
margin-bottom: 16px;
76+
}
77+
78+
.account-heading {
79+
font-size: 2.5rem;
80+
}

Diff for: flaskblog/static/profile_pics/23ee9bd2e29c11c6.jpg

2.42 KB
Loading

Diff for: flaskblog/static/profile_pics/35719ae9c224c10d.jpg

4.88 KB
Loading

Diff for: flaskblog/static/profile_pics/74d32cc0ad8e7448.jpg

1.95 KB
Loading

Diff for: flaskblog/static/profile_pics/c2efa1225b2f776f.jpg

254 KB
Loading

Diff for: flaskblog/static/profile_pics/default.jpg

10.7 KB
Loading

Diff for: flaskblog/static/profile_pics/f1a589c1becb42b3.jpg

4.88 KB
Loading

Diff for: flaskblog/templates/about.html

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{% extends "layout.html" %}
2+
{% block content %}
3+
<h1>About Page</h1>
4+
{% endblock content %}
5+

Diff for: flaskblog/templates/account.html

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
{% extends "layout.html" %}
2+
{% block content %}
3+
<div class="content-section">
4+
<div class="media">
5+
<img class="rounded-circle account-img" src="{{ image_file }}">
6+
<div class="media-body">
7+
<h2 class="account-heading">{{ current_user.username }}</h2>
8+
<p class="text-secondary">{{ current_user.email }}</p>
9+
</div>
10+
</div>
11+
<form method="POST" action="" enctype="multipart/form-data">
12+
{{ form.hidden_tag() }}
13+
<fieldset class="form-group">
14+
<legend class="border-bottom mb-4">Account Info</legend>
15+
<div class="form-group">
16+
{{ form.username.label(class="form-control-label") }}
17+
18+
{% if form.username.errors %}
19+
{{ form.username(class="form-control form-control-lg is-invalid") }}
20+
<div class="invalid-feedback">
21+
{% for error in form.username.errors %}
22+
<span>{{ error }}</span>
23+
{% endfor %}
24+
</div>
25+
{% else %}
26+
{{ form.username(class="form-control form-control-lg") }}
27+
{% endif %}
28+
29+
</div>
30+
31+
<div class="form-group">
32+
{{ form.email.label(class="form-control-label") }}
33+
{% if form.email.errors %}
34+
{{ form.email(class="form-control form-control-lg is-invalid") }}
35+
<div class="invalid-feedback">
36+
{% for error in form.email.errors %}
37+
<span>{{ error }}</span>
38+
{% endfor %}
39+
</div>
40+
{% else %}
41+
{{ form.email(class="form-control form-control-lg") }}
42+
{% endif %}
43+
</div>
44+
{{ form.picture.label() }}
45+
{{ form.picture(class="form-control-file") }}
46+
{% if form.picture.errors %}
47+
{% for error in form.picture.errors %}
48+
<span class="text-danger">{{ error }}</span></br>
49+
{% endfor %}
50+
{% endif %}
51+
</div>
52+
</fieldset>
53+
<div class="form-group">
54+
{{ form.submit(class="btn btn-outline-info") }}
55+
</div>
56+
</form>
57+
</div>
58+
{% endblock content %}

Diff for: flaskblog/templates/create_post.html

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
{% extends "layout.html" %}
2+
{% block content %}
3+
<div class="content-section">
4+
<form method="POST" action="">
5+
{{ form.hidden_tag() }}
6+
<fieldset class="form-group">
7+
<legend class="border-bottom mb-4">{{ legend }}</legend>
8+
<div class="form-group">
9+
{{ form.title.label(class="form-control-label") }}
10+
{% if form.title.errors %}
11+
{{ form.title(class="form-control form-control-lg is-invalid") }}
12+
<div class="invalid-feedback">
13+
{% for error in form.title.errors %}
14+
<span>{{ error }}</span>
15+
{% endfor %}
16+
</div>
17+
{% else %}
18+
{{ form.title(class="form-control form-control-lg") }}
19+
{% endif %}
20+
</div>
21+
22+
<div class="form-group">
23+
{{ form.content.label(class="form-control-label") }}
24+
{% if form.content.errors %}
25+
{{ form.content(class="form-control form-control-lg is-invalid") }}
26+
<div class="invalid-feedback">
27+
{% for error in form.content.errors %}
28+
<span>{{ error }}</span>
29+
{% endfor %}
30+
</div>
31+
{% else %}
32+
{{ form.content(class="form-control form-control-lg") }}
33+
{% endif %}
34+
</div>
35+
</fieldset>
36+
<div class="form-group">
37+
{{ form.submit(class="btn btn-outline-info") }}
38+
</div>
39+
</form>
40+
</div>
41+
{% endblock content %}

Diff for: flaskblog/templates/errors/403.html

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{% extends "layout.html" %}
2+
{% block content %}
3+
<div class="content-section">
4+
<h1>You don't have permission to do that (403)</h1>
5+
<p>Please check your account and try again.</p>
6+
</div>
7+
{% endblock content %}

0 commit comments

Comments
 (0)